diff --git a/server/auth.js b/server/auth.js index a50172b..88c50be 100644 --- a/server/auth.js +++ b/server/auth.js @@ -96,7 +96,7 @@ function ensureConfigured() { if (!issuer || !clientId || !clientSecret) throw new Error('Keycloak OIDC is not fully configured') } -async function verifyAllowedMachineToken(token, allowedClients, purpose) { +async function verifyAllowedMachineToken(token, allowedClients, purpose, { exactClient = false } = {}) { const { issuer } = config() if (!issuer) throw new Error('KEYCLOAK_ISSUER is required') if (!allowedClients.length) throw new Error(`No ${purpose} clients are allowed`) @@ -105,10 +105,12 @@ async function verifyAllowedMachineToken(token, allowedClients, purpose) { const { payload } = await jwtVerify(token, jwks, { issuer }) const client = String(payload.azp || '') - if (!client || !allowedClients.includes(client)) { - throw new Error(`Token client is not allowed for ${purpose}`) - } + const audiences = Array.isArray(payload.aud) ? payload.aud : [payload.aud].filter(Boolean) + const allowed = exactClient + ? Boolean(client && allowedClients.includes(client)) + : allowedClients.some(value => value === client || audiences.includes(value)) + if (!allowed) throw new Error(`Token client is not allowed for ${purpose}`) return payload } @@ -119,7 +121,7 @@ export async function verifyMachineToken(token) { export async function verifyMcpToken(token) { const { allowedMcpClients } = config() - return verifyAllowedMachineToken(token, allowedMcpClients, 'template authoring') + return verifyAllowedMachineToken(token, allowedMcpClients, 'template authoring', { exactClient: true }) } export function registerAuthRoutes(app) {