From ab1efa2b28394c62d9f2f0f66422406ffa79be4a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 23:01:01 +0000 Subject: [PATCH] Fix Microsoft OAuth callback by passing scope in token exchange Microsoft's v2.0 token endpoint requires a 'scope' parameter in the authorization code exchange request. The openid-client library does not include it automatically since it is optional per RFC 6749. Pass scope via the exchangeBody extras parameter for all providers' callback and oauthCallback calls. Also forward the provider-specific extra scope from the outer OAuth info to the token exchange. Co-Authored-By: Konstantin Wohlwend --- .../auth/oauth/callback/[provider_id]/route.tsx | 1 + apps/backend/src/oauth/providers/base.tsx | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx b/apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx index 3e7613c3b..c0ccd9c4b 100644 --- a/apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx +++ b/apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx @@ -168,6 +168,7 @@ const handler = createSmartRouteHandler({ callbackResult = await providerObj.getCallback({ codeVerifier: innerCodeVerifier, state: innerState, + extraScope: providerScope, callbackParams: { ...query, ...body, diff --git a/apps/backend/src/oauth/providers/base.tsx b/apps/backend/src/oauth/providers/base.tsx index a75fb67fb..f27140ec3 100644 --- a/apps/backend/src/oauth/providers/base.tsx +++ b/apps/backend/src/oauth/providers/base.tsx @@ -386,6 +386,7 @@ export abstract class OAuthBaseProvider { callbackParams: CallbackParamsType, codeVerifier: string, state: string, + extraScope?: string, }): Promise<{ userInfo: OAuthUserInfo, tokenSet: TokenSet }> { let tokenSet; const callbackParams = { ...options.callbackParams }; @@ -410,11 +411,17 @@ export abstract class OAuthBaseProvider { }, ] as const; + const callbackExtras = { + exchangeBody: { + scope: mergeScopeStrings(this.scope, options.extraScope ?? ""), + }, + }; + try { if (this.openid) { - tokenSet = await this.oauthClient.callback(...params); + tokenSet = await this.oauthClient.callback(...params, callbackExtras); } else { - tokenSet = await this.oauthClient.oauthCallback(...params); + tokenSet = await this.oauthClient.oauthCallback(...params, callbackExtras); } } catch (error: any) { if (error?.error === "invalid_grant" || error?.error?.error === "invalid_grant") {