fix(center): graceful 4xx when OIDC issuer/token endpoint unreachable

Wrap discovery + token-exchange fetch in try/catch so a DNS/network
failure returns BadRequest/Unauthorized instead of a bare 500.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
h z
2026-05-18 09:47:28 +01:00
parent 2a394969d2
commit 9f7216565b

View File

@@ -62,7 +62,12 @@ export class OidcService {
return this.discoveryCache.doc; return this.discoveryCache.doc;
} }
const url = issuer.replace(/\/$/, '') + '/.well-known/openid-configuration'; const url = issuer.replace(/\/$/, '') + '/.well-known/openid-configuration';
const res = await fetch(url); let res: Response;
try {
res = await fetch(url);
} catch {
throw new BadRequestException('oidc: issuer unreachable (discovery failed)');
}
if (!res.ok) throw new BadRequestException(`oidc discovery failed: ${res.status}`); if (!res.ok) throw new BadRequestException(`oidc discovery failed: ${res.status}`);
const doc = (await res.json()) as Discovery; const doc = (await res.json()) as Discovery;
if (!doc.authorization_endpoint || !doc.token_endpoint) { if (!doc.authorization_endpoint || !doc.token_endpoint) {
@@ -120,11 +125,16 @@ export class OidcService {
client_secret: c.clientSecret, client_secret: c.clientSecret,
code_verifier: st.cv, code_verifier: st.cv,
}); });
const tokRes = await fetch(doc.token_endpoint, { let tokRes: Response;
method: 'POST', try {
headers: { 'content-type': 'application/x-www-form-urlencoded', accept: 'application/json' }, tokRes = await fetch(doc.token_endpoint, {
body: body.toString(), method: 'POST',
}); headers: { 'content-type': 'application/x-www-form-urlencoded', accept: 'application/json' },
body: body.toString(),
});
} catch {
throw new UnauthorizedException('oidc: token endpoint unreachable');
}
if (!tokRes.ok) { if (!tokRes.ok) {
throw new UnauthorizedException(`oidc: token exchange failed (${tokRes.status})`); throw new UnauthorizedException(`oidc: token exchange failed (${tokRes.status})`);
} }