84 lines
2.1 KiB
TypeScript
84 lines
2.1 KiB
TypeScript
import { Request, Response, NextFunction } from 'express';
|
|
import jwt from 'jsonwebtoken';
|
|
import jwksClient from 'jwks-rsa';
|
|
|
|
const OIDC_ISSUER = process.env.OIDC_ISSUER || 'http://localhost:9000/application/o/kaboot/';
|
|
const OIDC_JWKS_URI = process.env.OIDC_JWKS_URI || 'http://localhost:9000/application/o/kaboot/jwks/';
|
|
const OIDC_INTERNAL_JWKS_URI = process.env.OIDC_INTERNAL_JWKS_URI || OIDC_JWKS_URI;
|
|
|
|
const client = jwksClient({
|
|
jwksUri: OIDC_INTERNAL_JWKS_URI,
|
|
cache: true,
|
|
cacheMaxAge: 600000,
|
|
rateLimit: true,
|
|
jwksRequestsPerMinute: 10,
|
|
});
|
|
|
|
function getSigningKey(header: jwt.JwtHeader, callback: jwt.SigningKeyCallback): void {
|
|
if (!header.kid) {
|
|
callback(new Error('No kid in token header'));
|
|
return;
|
|
}
|
|
client.getSigningKey(header.kid, (err, key) => {
|
|
if (err) {
|
|
callback(err);
|
|
return;
|
|
}
|
|
const signingKey = key?.getPublicKey();
|
|
callback(null, signingKey);
|
|
});
|
|
}
|
|
|
|
export interface AuthenticatedUser {
|
|
sub: string;
|
|
preferred_username: string;
|
|
email?: string;
|
|
name?: string;
|
|
groups?: string[];
|
|
}
|
|
|
|
export interface AuthenticatedRequest extends Request {
|
|
user?: AuthenticatedUser;
|
|
}
|
|
|
|
export function requireAuth(
|
|
req: AuthenticatedRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): void {
|
|
const authHeader = req.headers.authorization;
|
|
|
|
if (!authHeader?.startsWith('Bearer ')) {
|
|
res.status(401).json({ error: 'Missing or invalid authorization header' });
|
|
return;
|
|
}
|
|
|
|
const token = authHeader.slice(7);
|
|
|
|
jwt.verify(
|
|
token,
|
|
getSigningKey,
|
|
{
|
|
issuer: OIDC_ISSUER,
|
|
algorithms: ['RS256'],
|
|
},
|
|
(err, decoded) => {
|
|
if (err) {
|
|
console.error('Token verification failed:', err.message);
|
|
res.status(401).json({ error: 'Invalid token', details: err.message });
|
|
return;
|
|
}
|
|
|
|
const payload = decoded as jwt.JwtPayload;
|
|
req.user = {
|
|
sub: payload.sub!,
|
|
preferred_username: payload.preferred_username || payload.sub!,
|
|
email: payload.email,
|
|
name: payload.name,
|
|
groups: payload.groups || [],
|
|
};
|
|
|
|
next();
|
|
}
|
|
);
|
|
}
|