mirror of
https://github.com/nestriness/nestri.git
synced 2026-08-01 17:34:15 +03:00
fix: Fix the auth API middleware
This commit is contained in:
@@ -3,27 +3,26 @@ import { Context } from "./context.js";
|
||||
import { ErrorCodes, VisibleError } from "./error.js";
|
||||
|
||||
export namespace Actor {
|
||||
export interface Member {
|
||||
type: "member";
|
||||
export interface User {
|
||||
type: "user";
|
||||
properties: {
|
||||
userID: string;
|
||||
email: string;
|
||||
teamID: string;
|
||||
memberID: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Team {
|
||||
type: "team";
|
||||
export interface Steam {
|
||||
type: "steam";
|
||||
properties: {
|
||||
teamID: string;
|
||||
email: string;
|
||||
userID: string;
|
||||
steamID: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface System {
|
||||
type: "system";
|
||||
properties: {
|
||||
teamID: string;
|
||||
userID: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,13 +39,13 @@ export namespace Actor {
|
||||
properties: {};
|
||||
}
|
||||
|
||||
export type Info = Team | Member | Public | Token | System;
|
||||
export type Info = Steam | User | Public | Token | System;
|
||||
|
||||
export const ctx = Context.create<Info>();
|
||||
|
||||
export function teamID() {
|
||||
export function userID() {
|
||||
const actor = ctx.use();
|
||||
if ("teamID" in actor.properties) return actor.properties.teamID;
|
||||
if ("userID" in actor.properties) return actor.properties.userID;
|
||||
throw new VisibleError(
|
||||
"authentication",
|
||||
ErrorCodes.Authentication.UNAUTHORIZED,
|
||||
|
||||
@@ -22,7 +22,6 @@ export namespace Examples {
|
||||
id: "74839300282033", // Steam ID
|
||||
ownerID: User.id,
|
||||
name: "JD The 65th",
|
||||
username: "jdoe",
|
||||
realName: "John Doe",
|
||||
memberSince: new Date("2010-01-26T21:00:00.000Z"),
|
||||
avatarHash: "3a5e805fd4c1e04e26a97af0b9c6fab2dee91a19",
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { z } from "zod/v4";
|
||||
import { Examples } from "../examples.js";
|
||||
import { Limitations } from "./steam.sql.js";
|
||||
import { Limitations, steamTable } from "./steam.sql.js";
|
||||
import { fn } from "../utils/fn.js";
|
||||
import { Actor } from "../actor.js";
|
||||
import { Database } from "../drizzle/index.js";
|
||||
import { eq, isNull, and } from "drizzle-orm";
|
||||
|
||||
export namespace Steam {
|
||||
export const Info = z
|
||||
@@ -18,10 +22,6 @@ export namespace Steam {
|
||||
description: "The display name of the Steam account",
|
||||
example: Examples.SteamAccount.name,
|
||||
}),
|
||||
username: z.string().meta({
|
||||
description: "The username of the Steam account",
|
||||
example: Examples.SteamAccount.username,
|
||||
}),
|
||||
realName: z.string().nullable().meta({
|
||||
description: "The real name associated with the Steam account",
|
||||
example: Examples.SteamAccount.realName,
|
||||
@@ -38,7 +38,7 @@ export namespace Steam {
|
||||
description: "Account limitations and restrictions",
|
||||
example: Examples.SteamAccount.limitations,
|
||||
}),
|
||||
profileUrl: z.string().meta({
|
||||
profileUrl: z.string().nullable().meta({
|
||||
description: "The Steam community profile URL or identifier",
|
||||
example: Examples.SteamAccount.profileUrl,
|
||||
}),
|
||||
@@ -54,4 +54,38 @@ export namespace Steam {
|
||||
});
|
||||
|
||||
export type Info = z.infer<typeof Info>;
|
||||
|
||||
export const fromID = fn(Info.shape.id, async (id) => {
|
||||
const userID = Actor.userID();
|
||||
|
||||
return Database.transaction((tx) =>
|
||||
tx
|
||||
.select()
|
||||
.from(steamTable)
|
||||
.where(
|
||||
and(
|
||||
eq(steamTable.id, id),
|
||||
eq(steamTable.ownerID, userID),
|
||||
isNull(steamTable.timeDeleted),
|
||||
),
|
||||
)
|
||||
.then((rows) => rows.map(serialize).at(0)),
|
||||
);
|
||||
});
|
||||
|
||||
export function serialize(
|
||||
input: typeof steamTable.$inferSelect,
|
||||
): z.infer<typeof Info> {
|
||||
return {
|
||||
id: input.id,
|
||||
name: input.name,
|
||||
ownerID: input.ownerID,
|
||||
realName: input.realName,
|
||||
profileUrl: input.profileUrl,
|
||||
memberSince: input.memberSince,
|
||||
avatarHash: input.avatarHash,
|
||||
limitations: input.limitations,
|
||||
lastSyncedAt: input.lastSyncedAt,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ import { subjects } from "@/subjects";
|
||||
import { MiddlewareHandler } from "hono";
|
||||
import { Actor } from "@nestri/core/actor";
|
||||
import { Api } from "@nestri/core/api/index";
|
||||
import { Member } from "@nestri/core/member/index";
|
||||
import { Steam } from "@nestri/core/steam/index";
|
||||
import { createClient } from "@openauthjs/openauth/client";
|
||||
import { VisibleError, ErrorCodes } from "@nestri/core/error";
|
||||
|
||||
const client = createClient({
|
||||
clientID: "api",
|
||||
issuer: process.env.AUTH_URL,
|
||||
issuer: process.env.AUTH_URL || "http://localhost:3000",
|
||||
subjects,
|
||||
});
|
||||
|
||||
@@ -52,15 +52,14 @@ export const auth: MiddlewareHandler = async (c, next) => {
|
||||
ErrorCodes.Authentication.INVALID_TOKEN,
|
||||
"Invalid bearer token",
|
||||
);
|
||||
if (result.subject.type === "team") {
|
||||
const memberID =
|
||||
c.req.header("x-nestri-member") || c.req.query("memberID");
|
||||
if (!memberID) {
|
||||
if (result.subject.type === "user") {
|
||||
const steamID = c.req.header("x-nestri-steam") || c.req.query("steamID");
|
||||
if (!steamID) {
|
||||
return Actor.provide(
|
||||
"team",
|
||||
"user",
|
||||
{
|
||||
email: result.subject.properties.email,
|
||||
teamID: result.subject.properties.teamID,
|
||||
userID: result.subject.properties.userID,
|
||||
},
|
||||
next,
|
||||
);
|
||||
@@ -69,17 +68,20 @@ export const auth: MiddlewareHandler = async (c, next) => {
|
||||
return Actor.provide(
|
||||
"system",
|
||||
{
|
||||
teamID: result.subject.properties.teamID,
|
||||
userID: result.subject.properties.userID,
|
||||
},
|
||||
async () => {
|
||||
const member = await Member.fromID(email);
|
||||
if (!member || member.timeDeleted) {
|
||||
c.status(401);
|
||||
return c.text("Unauthorized: User not found");
|
||||
const steamAcc = await Steam.fromID(steamID);
|
||||
if (!steamAcc) {
|
||||
throw new VisibleError(
|
||||
"authentication",
|
||||
ErrorCodes.Authentication.INVALID_TOKEN,
|
||||
"Invalid bearer token",
|
||||
);
|
||||
}
|
||||
return Actor.provide(
|
||||
"member",
|
||||
{ memberID: member.id, teamID: member.workspaceID },
|
||||
"steam",
|
||||
{ steamID: steamAcc.id, userID: steamAcc.ownerID },
|
||||
next,
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user