46 lines
1.4 KiB
JavaScript

import { createRemoteJWKSet, jwtVerify } from 'jose'
let jwks
function authConfig() {
const issuer = process.env.KEYCLOAK_ISSUER?.replace(/\/$/, '')
const clientId = process.env.KEYCLOAK_CLIENT_ID
return { issuer, clientId }
}
export function publicAuthConfig() {
const { issuer, clientId } = authConfig()
return {
authRequired: process.env.AUTH_REQUIRED !== 'false',
issuer: issuer || '',
clientId: clientId || ''
}
}
export async function requireAuth(req, res, next) {
if (process.env.AUTH_REQUIRED === 'false') {
req.user = { sub: 'local-dev', preferred_username: 'local-dev' }
return next()
}
try {
const { issuer, clientId } = authConfig()
if (!issuer || !clientId) throw new Error('Keycloak is not configured')
const header = req.headers.authorization || ''
if (!header.startsWith('Bearer ')) return res.status(401).json({ error: 'missing_bearer_token' })
jwks ||= createRemoteJWKSet(new URL(`${issuer}/protocol/openid-connect/certs`))
const { payload } = await jwtVerify(header.slice(7), jwks, { issuer })
const aud = Array.isArray(payload.aud) ? payload.aud : [payload.aud].filter(Boolean)
if (payload.azp !== clientId && !aud.includes(clientId)) {
return res.status(403).json({ error: 'wrong_client' })
}
req.user = payload
next()
} catch (error) {
res.status(401).json({ error: 'invalid_token', detail: error.message })
}
}