fix(auth): callback iss must equal metadata issuer (honor metadataIssuer override) (#361)
#359 made the metadata `issuer` honor the metadataIssuer override so the 2025-03-26 backcompat scenario is RFC 8414 §3.3-compliant. The authorize callback's `iss` parameter was still derived as `getAuthBaseUrl()+routePrefix`, so when a scenario sets metadataIssuer (bare origin) but keeps a routePrefix, the callback `iss` mismatches the metadata `issuer` and an RFC 9207-conforming client correctly rejects it. Extract `resolveIssuer()` and use it for both the metadata `issuer` and the `'correct'`/`'normalized'` callback `iss` so they always match.
This commit is contained in:
parent
1ff5d88370
commit
5d1b3cea3b
1 changed files with 15 additions and 7 deletions
|
|
@ -127,6 +127,14 @@ export function createAuthServer(
|
||||||
registration_endpoint: `${routePrefix}/register`
|
registration_endpoint: `${routePrefix}/register`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The issuer identifier this AS publishes — used for both the metadata
|
||||||
|
// `issuer` field and the RFC 9207 callback `iss` parameter, which per
|
||||||
|
// RFC 9207 §2.4 must be identical under simple string comparison.
|
||||||
|
const resolveIssuer = () =>
|
||||||
|
typeof metadataIssuer === 'function'
|
||||||
|
? metadataIssuer()
|
||||||
|
: (metadataIssuer ?? `${getAuthBaseUrl()}${routePrefix}`);
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(express.urlencoded({ extended: true }));
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
@ -158,10 +166,7 @@ export function createAuthServer(
|
||||||
});
|
});
|
||||||
|
|
||||||
const metadata: any = {
|
const metadata: any = {
|
||||||
issuer:
|
issuer: resolveIssuer(),
|
||||||
typeof metadataIssuer === 'function'
|
|
||||||
? metadataIssuer()
|
|
||||||
: (metadataIssuer ?? `${getAuthBaseUrl()}${routePrefix}`),
|
|
||||||
authorization_endpoint: `${getAuthBaseUrl()}${authRoutes.authorization_endpoint}`,
|
authorization_endpoint: `${getAuthBaseUrl()}${authRoutes.authorization_endpoint}`,
|
||||||
token_endpoint: `${getAuthBaseUrl()}${authRoutes.token_endpoint}`,
|
token_endpoint: `${getAuthBaseUrl()}${authRoutes.token_endpoint}`,
|
||||||
...(!disableDynamicRegistration && {
|
...(!disableDynamicRegistration && {
|
||||||
|
|
@ -274,16 +279,19 @@ export function createAuthServer(
|
||||||
redirectUrl.searchParams.set('state', state);
|
redirectUrl.searchParams.set('state', state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ISS: Include iss parameter in redirect if configured
|
// ISS: Include iss parameter in redirect if configured. The 'correct'
|
||||||
|
// value must equal the metadata `issuer` exactly (RFC 9207 §2.4 simple
|
||||||
|
// string comparison), so honor the same metadataIssuer override the
|
||||||
|
// metadata document does.
|
||||||
if (issInRedirect === 'correct') {
|
if (issInRedirect === 'correct') {
|
||||||
redirectUrl.searchParams.set('iss', `${getAuthBaseUrl()}${routePrefix}`);
|
redirectUrl.searchParams.set('iss', resolveIssuer());
|
||||||
} else if (issInRedirect === 'wrong') {
|
} else if (issInRedirect === 'wrong') {
|
||||||
redirectUrl.searchParams.set('iss', 'https://evil.example.com');
|
redirectUrl.searchParams.set('iss', 'https://evil.example.com');
|
||||||
} else if (issInRedirect === 'normalized') {
|
} else if (issInRedirect === 'normalized') {
|
||||||
// Normalization-equivalent variant of the correct issuer: identical
|
// Normalization-equivalent variant of the correct issuer: identical
|
||||||
// after RFC 3986 scheme-based normalization (trailing slash on an
|
// after RFC 3986 scheme-based normalization (trailing slash on an
|
||||||
// empty path) but different under simple string comparison.
|
// empty path) but different under simple string comparison.
|
||||||
redirectUrl.searchParams.set('iss', `${getAuthBaseUrl()}${routePrefix}/`);
|
redirectUrl.searchParams.set('iss', `${resolveIssuer()}/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.redirect(redirectUrl.toString());
|
res.redirect(redirectUrl.toString());
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue