update project
This commit is contained in:
@@ -1,92 +0,0 @@
|
||||
import * as jose from 'jose';
|
||||
import { env } from '../config/env';
|
||||
|
||||
export interface AccessTokenPayload {
|
||||
sub: string;
|
||||
type: string;
|
||||
sid: string;
|
||||
iat: number;
|
||||
nbf: number;
|
||||
exp: number;
|
||||
iss?: string;
|
||||
aud?: string;
|
||||
}
|
||||
|
||||
export interface AuthContext {
|
||||
userId: string;
|
||||
sid: string;
|
||||
token: AccessTokenPayload;
|
||||
}
|
||||
|
||||
let jwks: ReturnType<typeof jose.createRemoteJWKSet> | null = null;
|
||||
let localKey: Awaited<ReturnType<typeof jose.importSPKI>> | null = null;
|
||||
|
||||
function getJWKS(): ReturnType<typeof jose.createRemoteJWKSet> {
|
||||
if (!jwks && env.jwt.jwksUrl) {
|
||||
jwks = jose.createRemoteJWKSet(new URL(env.jwt.jwksUrl));
|
||||
}
|
||||
if (!jwks) {
|
||||
throw new Error('JWT_JWKS_URL is not configured');
|
||||
}
|
||||
return jwks;
|
||||
}
|
||||
|
||||
async function getLocalKey(): Promise<Awaited<ReturnType<typeof jose.importSPKI>>> {
|
||||
if (!localKey && env.jwt.publicKey) {
|
||||
localKey = await jose.importSPKI(env.jwt.publicKey, env.jwt.algorithm);
|
||||
}
|
||||
if (!localKey) {
|
||||
throw new Error('No JWT public key available');
|
||||
}
|
||||
return localKey;
|
||||
}
|
||||
|
||||
export async function verifyAccessToken(token: string): Promise<AuthContext> {
|
||||
let payload: jose.JWTPayload;
|
||||
|
||||
try {
|
||||
const verifyOptions: jose.JWTVerifyOptions = {
|
||||
algorithms: [env.jwt.algorithm],
|
||||
clockTolerance: 10,
|
||||
};
|
||||
if (env.jwt.issuer) verifyOptions.issuer = env.jwt.issuer;
|
||||
if (env.jwt.audience) verifyOptions.audience = env.jwt.audience;
|
||||
|
||||
if (env.jwt.jwksUrl) {
|
||||
const result = await jose.jwtVerify(token, getJWKS(), verifyOptions);
|
||||
payload = result.payload;
|
||||
} else {
|
||||
const key = await getLocalKey();
|
||||
const result = await jose.jwtVerify(token, key, verifyOptions);
|
||||
payload = result.payload;
|
||||
}
|
||||
} catch (err: any) {
|
||||
if (err.code === 'ERR_JWT_EXPIRED') {
|
||||
throw Object.assign(new Error('Token expired'), { status: 401 });
|
||||
}
|
||||
throw Object.assign(new Error('Invalid token'), { status: 401 });
|
||||
}
|
||||
|
||||
if (payload.type !== 'access') {
|
||||
throw Object.assign(new Error('Invalid token type'), { status: 401 });
|
||||
}
|
||||
|
||||
if (!payload.sub || !payload.sid) {
|
||||
throw Object.assign(new Error('Missing token claims'), { status: 401 });
|
||||
}
|
||||
|
||||
return {
|
||||
userId: payload.sub,
|
||||
sid: payload.sid as string,
|
||||
token: {
|
||||
sub: payload.sub,
|
||||
type: payload.type as string,
|
||||
sid: payload.sid as string,
|
||||
iat: payload.iat!,
|
||||
nbf: payload.nbf!,
|
||||
exp: payload.exp!,
|
||||
iss: payload.iss,
|
||||
aud: typeof payload.aud === 'string' ? payload.aud : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user