Spaces:
Sleeping
Sleeping
| import { verify } from 'jsonwebtoken'; | |
| const createTemplateLogger = (runId?: string) => ({ failed: async (s: string, e: string, err: any) => console.error(s, e, err), log: async (o: any) => console.log(o), succeeded: async (s: string, e: string, o: any) => console.log(s, e, o) }); | |
| import type { IncomingMessage } from 'node:http'; | |
| // We fetch the public key (or use a shared secret if configured) from SIN-Authenticator's OIDC jwks endpoint or env | |
| const OIDC_PUBLIC_KEY = process.env.SIN_OIDC_PUBLIC_KEY || process.env.SIN_SHARED_SECRET || ''; | |
| export type AuthContext = { | |
| subject: string; | |
| roles: string[]; | |
| claims: Record<string, unknown>; | |
| isValid: boolean; | |
| error?: string; | |
| }; | |
| export async function verifyZeroTrustToken(request: IncomingMessage, runId?: string): Promise<AuthContext> { | |
| const logger = createTemplateLogger(runId); | |
| const authHeader = request.headers.authorization; | |
| if (!authHeader || !authHeader.startsWith('Bearer ')) { | |
| await logger.failed('auth.zero_trust', 'auth.token.missing', new Error('Missing or malformed Authorization header')); | |
| return { subject: 'anonymous', roles: [], claims: {}, isValid: false, error: 'missing_token' }; | |
| } | |
| const token = authHeader.split(' ')[1]; | |
| if (!OIDC_PUBLIC_KEY) { | |
| // If the fleet runs in a mode without strict OIDC enforcement configured yet, | |
| // we log a warning but might fail closed depending on fleet policy. | |
| // For Epic #195, we want to enforce zero-trust strictly when keys are present. | |
| await logger.log({ | |
| surface: 'auth.zero_trust', | |
| event: 'auth.keys.missing', | |
| level: 'warn', | |
| status: 'failed', | |
| details: { note: 'No SIN_OIDC_PUBLIC_KEY or SIN_SHARED_SECRET configured for verification.' } | |
| }); | |
| return { subject: 'anonymous', roles: [], claims: {}, isValid: false, error: 'missing_keys' }; | |
| } | |
| try { | |
| const decoded = verify(token, OIDC_PUBLIC_KEY, { algorithms: ['RS256', 'HS256'] }) as Record<string, unknown>; | |
| await logger.succeeded('auth.zero_trust', 'auth.token.verified', { subject: decoded.sub }); | |
| return { | |
| subject: String(decoded.sub || 'unknown'), | |
| roles: Array.isArray(decoded.roles) ? decoded.roles.map(String) : [], | |
| claims: decoded, | |
| isValid: true | |
| }; | |
| } catch (error) { | |
| await logger.failed('auth.zero_trust', 'auth.token.rejected', error); | |
| return { | |
| subject: 'anonymous', | |
| roles: [], | |
| claims: {}, | |
| isValid: false, | |
| error: error instanceof Error ? error.message : String(error) | |
| }; | |
| } | |
| } | |