Skip to content

Fix Microsoft OAuth callback by passing scope in token exchange#1509

Closed
N2D4 wants to merge 1 commit into
devfrom
devin/1779922855-fix-microsoft-oauth-scope
Closed

Fix Microsoft OAuth callback by passing scope in token exchange#1509
N2D4 wants to merge 1 commit into
devfrom
devin/1779922855-fix-microsoft-oauth-scope

Conversation

@N2D4
Copy link
Copy Markdown
Contributor

@N2D4 N2D4 commented May 27, 2026

Microsoft's v2.0 token endpoint requires a scope parameter in the authorization code exchange request (AADSTS70011). The openid-client library doesn't include it automatically since it's optional per RFC 6749.

Pass scope via exchangeBody in the callback extras for all providers, and forward the provider-specific extra scope from the outer OAuth info.

Link to Devin session: https://app.devin.ai/sessions/3cdf9182be7c47289d52f66de534c384
Requested by: @N2D4


Summary by cubic

Include scope in the token exchange for OAuth callbacks to satisfy Microsoft v2.0 and prevent AADSTS70011. Applied generically across providers using openid-client’s exchangeBody extras.

  • Bug Fixes
    • Send merged scope via exchangeBody.scope to callback/oauthCallback in openid-client.
    • Forward provider-specific extraScope from the route to the base provider.

Written for commit ab1efa2. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Authentication
    • Improved OAuth scope parameter handling during provider authentication callbacks to better align with OAuth provider requirements and enhance authentication compatibility.

Review Change Stack

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 <n2d4xc@gmail.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@vercel
Copy link
Copy Markdown

vercel Bot commented May 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment May 27, 2026 11:10pm
stack-auth-internal-tool Ready Ready Preview, Comment May 27, 2026 11:10pm
stack-auth-mcp Ready Ready Preview, Comment May 27, 2026 11:10pm
stack-auth-skills Ready Ready Preview, Comment May 27, 2026 11:10pm
stack-backend Ready Ready Preview, Comment May 27, 2026 11:10pm
stack-dashboard Ready Ready Preview, Comment May 27, 2026 11:10pm
stack-demo Ready Ready Preview, Comment May 27, 2026 11:10pm
stack-docs Ready Ready Preview, Comment May 27, 2026 11:10pm
stack-preview-backend Ready Ready Preview, Comment May 27, 2026 11:10pm
stack-preview-dashboard Ready Ready Preview, Comment May 27, 2026 11:10pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b06d699f-b799-4687-84d5-204415af587e

📥 Commits

Reviewing files that changed from the base of the PR and between c0fefd3 and ab1efa2.

📒 Files selected for processing (2)
  • apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx
  • apps/backend/src/oauth/providers/base.tsx

📝 Walkthrough

Walkthrough

This PR extends OAuth callback handling to support dynamic scope passing. The route handler now forwards providerScope as extraScope to the base provider's getCallback method, which merges it with the base scope during token exchange with the OpenID client.

Changes

Dynamic scope in OAuth callback

Layer / File(s) Summary
Scope parameter and token exchange
apps/backend/src/oauth/providers/base.tsx, apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx
The getCallback method signature now accepts an optional extraScope parameter. During authorization-code exchange, the implementation constructs an exchangeBody containing the merged scope (this.scope + extraScope) and passes it to openid-client. The route handler invokes this by passing extraScope: providerScope when calling getCallback.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A scope so wide, now dynamic and bright,
Extra scopes merge in the OAuth night,
Base and beyond dance in sweet harmony,
Token exchange flows with fresh potency!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly describes the main change: fixing Microsoft OAuth callback by passing scope in token exchange, which matches the changeset's core purpose.
Description check ✅ Passed The description is comprehensive and includes context (Microsoft v2.0 requirements, RFC 6749 reference), the solution approach, affected files, and references.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch devin/1779922855-fix-microsoft-oauth-scope

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 27, 2026

Greptile Summary

This PR fixes Microsoft OAuth sign-in failures (AADSTS70011) by including the scope parameter in the authorization-code token-exchange request, which Microsoft's v2.0 endpoint requires but openid-client does not send by default. The fix routes the already-available providerScope from the outer OAuth state through getCallback into openid-client's exchangeBody, applying the change generically for all providers.

  • base.tsx: Builds a callbackExtras object with exchangeBody.scope set to the merged provider base scope plus any caller-supplied extra scope, and passes it to both callback and oauthCallback.
  • route.tsx: Threads the existing providerScope from outer OAuth state into getCallback as extraScope — a one-line addition that requires no new data.

Confidence Score: 4/5

The change is targeted and low-risk, forwarding an already-validated scope string into the token exchange with no new data sources.

Both changed files touch a narrow, well-understood path: base.tsx builds callbackExtras from data already present in the object, and route.tsx adds one field to an existing options struct. The merged scope passed in exchangeBody is computed identically to what getAuthorizationUrl sends, so authorization and token-exchange scopes remain in sync. The small ?? vs || inconsistency is the only finding.

No files require special attention; the changes are self-contained within the two modified files.

Important Files Changed

Filename Overview
apps/backend/src/oauth/providers/base.tsx Adds extraScope parameter to getCallback and constructs callbackExtras.exchangeBody.scope by merging the provider base scope with the caller-supplied extra scope, then passes it to both callback and oauthCallback — fixes Microsoft AADSTS70011.
apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx Forwards the existing providerScope from outer OAuth state to getCallback as extraScope — a one-line change that threads the already-available value down to the base provider.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant CallbackRoute as /callback/[provider_id]
    participant BaseProvider as OAuthBaseProvider
    participant OpenIDClient as openid-client
    participant MicrosoftAAD as Microsoft AAD v2.0

    Browser->>CallbackRoute: "GET /callback?code=...&state=..."
    CallbackRoute->>CallbackRoute: Read outerInfo (providerScope from DB)
    CallbackRoute->>BaseProvider: "getCallback({ extraScope: providerScope, ... })"
    BaseProvider->>BaseProvider: "Build callbackExtras { exchangeBody: { scope: merge(this.scope, extraScope) } }"
    alt "openid = true"
        BaseProvider->>OpenIDClient: oauthClient.callback(...params, callbackExtras)
    else "openid = false"
        BaseProvider->>OpenIDClient: oauthClient.oauthCallback(...params, callbackExtras)
    end
    OpenIDClient->>MicrosoftAAD: "POST /token code=...&scope=openid+profile+..."
    MicrosoftAAD-->>OpenIDClient: access_token, id_token, refresh_token
    OpenIDClient-->>BaseProvider: TokenSet
    BaseProvider-->>CallbackRoute: "{ userInfo, tokenSet }"
    CallbackRoute->>Browser: Redirect to app
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/backend/src/oauth/providers/base.tsx:414-418
Minor style inconsistency: `getAuthorizationUrl` (line 372) uses `|| ""` for the same fallback, while the new code here uses `?? ""`. Both are functionally equivalent for string scopes, but aligning them keeps the two code sites consistent.

```suggestion
    const callbackExtras = {
      exchangeBody: {
        scope: mergeScopeStrings(this.scope, options.extraScope || ""),
      },
    };
```

Reviews (1): Last reviewed commit: "Fix Microsoft OAuth callback by passing ..." | Re-trigger Greptile

Comment on lines +414 to +418
const callbackExtras = {
exchangeBody: {
scope: mergeScopeStrings(this.scope, options.extraScope ?? ""),
},
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Minor style inconsistency: getAuthorizationUrl (line 372) uses || "" for the same fallback, while the new code here uses ?? "". Both are functionally equivalent for string scopes, but aligning them keeps the two code sites consistent.

Suggested change
const callbackExtras = {
exchangeBody: {
scope: mergeScopeStrings(this.scope, options.extraScope ?? ""),
},
};
const callbackExtras = {
exchangeBody: {
scope: mergeScopeStrings(this.scope, options.extraScope || ""),
},
};
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/backend/src/oauth/providers/base.tsx
Line: 414-418

Comment:
Minor style inconsistency: `getAuthorizationUrl` (line 372) uses `|| ""` for the same fallback, while the new code here uses `?? ""`. Both are functionally equivalent for string scopes, but aligning them keeps the two code sites consistent.

```suggestion
    const callbackExtras = {
      exchangeBody: {
        scope: mergeScopeStrings(this.scope, options.extraScope || ""),
      },
    };
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Tip: cubic could auto-approve low-risk PRs like this, if it thinks it's safe to merge. Learn more

Re-trigger cubic

@N2D4 N2D4 closed this May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant