105 lines
2.7 KiB
TypeScript
105 lines
2.7 KiB
TypeScript
import type { Context } from "jsr:@hono/hono@4";
|
|
|
|
import {
|
|
getClientIp,
|
|
getCookieDomain,
|
|
isIpAllowed,
|
|
isPathBypassed,
|
|
isSafeRedirectUrl,
|
|
} from "./forward_auth.ts";
|
|
|
|
import {
|
|
type AppRecord,
|
|
type AuthenticatedUser,
|
|
extractAllSessionIds,
|
|
getAppByHost,
|
|
getAuthenticatedUser,
|
|
getUserGrant,
|
|
isGlobalAdmin,
|
|
} from "./session_resolver.ts";
|
|
|
|
// Re-export for backward compatibility
|
|
export {
|
|
type AppRecord,
|
|
type AuthenticatedUser,
|
|
extractAllSessionIds,
|
|
getAppByHost,
|
|
getAuthenticatedUser,
|
|
getClientIp,
|
|
getCookieDomain,
|
|
getUserGrant,
|
|
isGlobalAdmin,
|
|
isIpAllowed,
|
|
isPathBypassed,
|
|
isSafeRedirectUrl,
|
|
};
|
|
|
|
/**
|
|
* Evaluates if the current user's capabilities satisfy the required scope.
|
|
* If !auth.isAgent, primary sessions inherit full capabilities (returns true).
|
|
* If auth.isAgent, checks if customScopes contains the requiredScope or '*'.
|
|
*/
|
|
export function hasScope(
|
|
auth: AuthenticatedUser,
|
|
requiredScope: string,
|
|
): boolean {
|
|
if (!auth.isAgent) return true;
|
|
if (!Array.isArray(auth.customScopes)) return false;
|
|
return auth.customScopes.includes("*") ||
|
|
auth.customScopes.includes(requiredScope);
|
|
}
|
|
|
|
/**
|
|
* Helper to check if the session itself is authorized as an admin.
|
|
*/
|
|
export async function isSessionAdmin(
|
|
auth: AuthenticatedUser,
|
|
): Promise<boolean> {
|
|
const globalAdmin = await isGlobalAdmin(auth.userId);
|
|
if (!globalAdmin) return false;
|
|
if (!auth.isAgent) return true;
|
|
return Array.isArray(auth.customScopes) && auth.customScopes.includes("*");
|
|
}
|
|
|
|
/**
|
|
* Hono Middleware: Blocks access if the session is a delegated agent session.
|
|
*/
|
|
export async function requirePrimarySession(
|
|
c: Context,
|
|
next: () => Promise<void>,
|
|
) {
|
|
const auth = await getAuthenticatedUser(c);
|
|
if (!auth) return c.json({ error: "Unauthorized" }, 401);
|
|
if (auth.isAgent) {
|
|
return c.json({ error: "Forbidden: Primary session required" }, 403);
|
|
}
|
|
await next();
|
|
}
|
|
|
|
/**
|
|
* Hono Middleware Factory: Requires a specific scope.
|
|
*/
|
|
export function requireScope(scope: string) {
|
|
return async (c: Context, next: () => Promise<void>) => {
|
|
const auth = await getAuthenticatedUser(c);
|
|
if (!auth) return c.json({ error: "Unauthorized" }, 401);
|
|
if (!hasScope(auth, scope)) {
|
|
return c.json({ error: "Forbidden: Insufficient scopes" }, 403);
|
|
}
|
|
await next();
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hono Middleware: Blocks access unless the session is an admin session.
|
|
*/
|
|
export async function requireAdmin(c: Context, next: () => Promise<void>) {
|
|
const auth = await getAuthenticatedUser(c);
|
|
if (!auth) return c.json({ error: "Unauthorized" }, 401);
|
|
const isAdmin = await isSessionAdmin(auth);
|
|
if (!isAdmin) {
|
|
return c.json({ error: "Forbidden: Global admin access required" }, 403);
|
|
}
|
|
await next();
|
|
}
|