Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/client/src/client/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1420,7 +1420,11 @@ export async function startAuthorization(
// if the request includes the OIDC-only "offline_access" scope,
// we need to set the prompt to "consent" to ensure the user is prompted to grant offline access
// https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
authorizationUrl.searchParams.append('prompt', 'consent');
// Use .set() not .append(): RFC 6749 §3.1 forbids duplicate params, and the
// authorization_endpoint may already include a prompt value. Azure AD also rejects the
// OIDC space-delimited list form (AADSTS90023), so a single value is the only portable
// option; consent is required for offline_access per OIDC §11.
authorizationUrl.searchParams.set('prompt', 'consent');
}

if (resource) {
Expand Down
17 changes: 17 additions & 0 deletions packages/client/test/client/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,23 @@ describe('OAuth Authorization', () => {
expect(authorizationUrl.searchParams.get('prompt')).toBe('consent');
});

it('overwrites existing prompt parameter from authorization_endpoint instead of duplicating', async () => {
const { authorizationUrl } = await startAuthorization('https://auth.example.com', {
metadata: {
issuer: 'https://auth.example.com',
authorization_endpoint: 'https://auth.example.com/authorize?prompt=select_account',
token_endpoint: 'https://auth.example.com/token',
response_types_supported: ['code'],
code_challenge_methods_supported: ['S256']
},
clientInformation: validClientInfo,
redirectUrl: 'http://localhost:3000/callback',
scope: 'read offline_access'
});

expect(authorizationUrl.searchParams.getAll('prompt')).toEqual(['consent']);
});

it.each([validMetadata, validOpenIdMetadata])('uses metadata authorization_endpoint when provided', async baseMetadata => {
const { authorizationUrl } = await startAuthorization('https://auth.example.com', {
metadata: baseMetadata,
Expand Down
Loading