diff --git a/src/client/auth.test.ts b/src/client/auth.test.ts index 93dd8e94..3d2f18d7 100644 --- a/src/client/auth.test.ts +++ b/src/client/auth.test.ts @@ -714,6 +714,20 @@ describe("OAuth Authorization", () => { expect(authorizationUrl.searchParams.has("state")).toBe(false); }); + // OpenID Connect requires that the user is prompted for consent if the scope includes 'offline_access' + it("includes consent prompt parameter if scope includes 'offline_access'", async () => { + const { authorizationUrl } = await startAuthorization( + "https://auth.example.com", + { + clientInformation: validClientInfo, + redirectUrl: "http://localhost:3000/callback", + scope: "read write profile offline_access", + } + ); + + expect(authorizationUrl.searchParams.get("prompt")).toBe("consent"); + }); + it("uses metadata authorization_endpoint when provided", async () => { const { authorizationUrl } = await startAuthorization( "https://auth.example.com", diff --git a/src/client/auth.ts b/src/client/auth.ts index 2b69a5d8..2bac386f 100644 --- a/src/client/auth.ts +++ b/src/client/auth.ts @@ -614,6 +614,13 @@ export async function startAuthorization( authorizationUrl.searchParams.set("scope", scope); } + if (scope?.includes("offline_access")) { + // 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"); + } + if (resource) { authorizationUrl.searchParams.set("resource", resource.href); }