Skip to content

feat(backend): dual-accept hexclave-mobile-oauth-url:// alongside legacy scheme#1501

Merged
BilalG1 merged 4 commits into
devfrom
hexclave-mobile-oauth-url-dual-accept
May 27, 2026
Merged

feat(backend): dual-accept hexclave-mobile-oauth-url:// alongside legacy scheme#1501
BilalG1 merged 4 commits into
devfrom
hexclave-mobile-oauth-url-dual-accept

Conversation

@BilalG1
Copy link
Copy Markdown
Collaborator

@BilalG1 BilalG1 commented May 27, 2026

What

  1. Backend dual-accept: isAcceptedNativeAppUrl() accepts both stack-auth-mobile-oauth-url:// (legacy) and hexclave-mobile-oauth-url:// (canonical).
  2. Swift SDK switches to the canonical scheme: StackAuth Swift SDK now emits and intercepts hexclave-mobile-oauth-url:// for native-app OAuth callbacks.

Before this PR, hexclave-mobile-oauth-url existed only inside RENAME-TO-HEXCLAVE.md — not in any code.

Why the Swift SDK change is safe

The Swift SDK uses ASWebAuthenticationSession(url:callbackURLScheme:completion:) (StackClientApp.swift:197-199). With this API, iOS intercepts the callback scheme ephemerally — no Info.plist registration is required. The Swift SDK source has no Info.plist, and the example apps' pbxproj registers no CFBundleURLSchemes. So:

  • New customer builds against the updated SDK → emit new scheme → backend accepts → ASWebAuthenticationSession intercepts on new scheme → works.
  • Already-shipped customer App Store binaries on older SDK versions → emit old scheme → backend still accepts → works.
  • No customer ever has to update an Info.plist.

The only real backward-compat constraint is that the backend can never drop the old scheme (already-shipped customer binaries have the constant baked into them). Hence the dual-accept.

(Note: RENAME-TO-HEXCLAVE.md line 88 incorrectly attributes the constraint to Info.plist registration. That's not how the SDK works — the scheme is baked into the SDK binary, not the customer's plist. The fix described in that doc is essentially the right shape; only the mechanism description is wrong.)

Changes

File Change
packages/stack-shared/src/utils/redirect-urls.tsx isAcceptedNativeAppUrl() accepts either protocol.
apps/backend/src/lib/redirect-urls.test.tsx Adds positive assertions for the new scheme in isAcceptedNativeAppUrl; parity negative assertions in validateRedirectUrl.
sdks/implementations/swift/Sources/StackAuth/StackClientApp.swift callbackScheme"hexclave-mobile-oauth-url"; fatalError example strings updated.
sdks/implementations/swift/Tests/StackAuthTests/OAuthTests.swift Test fixture URLs updated (no assertions depend on the scheme literal).
sdks/implementations/swift/Examples/StackAuthiOS/.../StackAuthiOSApp.swift Default values in the example UI.
sdks/implementations/swift/Examples/StackAuthMacOS/.../StackAuthMacOSApp.swift Default values in the example UI.
sdks/implementations/swift/README.md Documents the new canonical scheme; compat note for the legacy one.
sdks/spec/src/apps/client-app.spec.md New scheme is canonical; legacy is "accepted indefinitely for already-shipped customer app binaries built against older SDK versions."

Verification

  • pnpm test run apps/backend/src/lib/redirect-urls.test.tsx — 34/34 passing (was 33; one new it block plus parity assertions).
  • pnpm --filter @stackframe/stack-shared --filter @stackframe/backend run lint — clean.
  • pnpm --filter @stackframe/stack-shared --filter @stackframe/backend run typecheck — clean.
  • Swift assertions in OAuthTests.swift do not check the scheme literal — they only check oauth/authorize/<provider>, state/verifier non-emptiness, and that redirectUrl round-trips. The fixture-value change is mechanical.

Risk

Low. Backend behavior strictly widens (every URL accepted before is still accepted). Swift SDK change is internal to OAuth callback handling, requires no customer migration, and is paired with the backend dual-accept landing in the same PR.

Summary by CodeRabbit

  • New Features

    • Adopted the canonical OAuth callback scheme "hexclave-mobile-oauth-url://" for native apps while continuing to accept the legacy "stack-auth-mobile-oauth-url://".
  • Documentation

    • Updated SDK docs, examples, and spec guidance to reference the canonical callback scheme and clarify legacy acceptance.
  • Tests & Samples

    • Updated tests and example apps to use and validate the canonical scheme.
  • Style

    • Rebranded the dev-tool trigger icon to the new Hexclave monochrome logo.

Review Change Stack

…acy scheme

The mobile OAuth URL scheme is a Tier 0 wire identifier (see
RENAME-TO-HEXCLAVE.md). It is baked into customer iOS/macOS apps via
Info.plist, so the backend must accept the new canonical
'hexclave-mobile-oauth-url://' without ever dropping the legacy
'stack-auth-mobile-oauth-url://' — removing the latter would break
OAuth for every already-shipped customer app built against the frozen
StackAuth Swift SDK.

- packages/stack-shared: isAcceptedNativeAppUrl() now returns true for
  either protocol.
- apps/backend tests: assert the new scheme is accepted by
  isAcceptedNativeAppUrl and (parity with old) rejected by
  validateRedirectUrl in the no-trusted-domain case.
- sdks/spec/client-app.spec.md: document both schemes; canonical for
  new SDKs is the Hexclave one.

Out of scope: the frozen StackAuth Swift SDK keeps registering and
emitting the legacy scheme; the new Hexclave Swift package (which will
use the new scheme) is not yet created.
@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 10:06pm
stack-auth-internal-tool Ready Ready Preview, Comment May 27, 2026 10:06pm
stack-auth-mcp Ready Ready Preview, Comment May 27, 2026 10:06pm
stack-auth-skills Ready Ready Preview, Comment May 27, 2026 10:06pm
stack-backend Ready Ready Preview, Comment May 27, 2026 10:06pm
stack-dashboard Ready Ready Preview, Comment May 27, 2026 10:06pm
stack-demo Ready Ready Preview, Comment May 27, 2026 10:06pm
stack-docs Ready Ready Preview, Comment May 27, 2026 10:06pm
stack-preview-backend Ready Ready Preview, Comment May 27, 2026 10:06pm
stack-preview-dashboard Ready Ready Preview, Comment May 27, 2026 10:06pm

@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: 584277cc-ea6c-4280-8d20-f7a6b960eb19

📥 Commits

Reviewing files that changed from the base of the PR and between e95b023 and f932a6a.

📒 Files selected for processing (1)
  • packages/template/src/dev-tool/dev-tool-core.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/template/src/dev-tool/dev-tool-core.ts

📝 Walkthrough

Walkthrough

Adds canonical hexclave-mobile-oauth-url:// support alongside legacy stack-auth-mobile-oauth-url:// for native OAuth redirects; updates validator, backend tests, Swift SDK examples/tests, spec/docs, and a dev-tool logo asset.

Changes

Native app OAuth redirect URL scheme support

Layer / File(s) Summary
Core URL scheme validation implementation
packages/stack-shared/src/utils/redirect-urls.tsx
isAcceptedNativeAppUrl now accepts both stack-auth-mobile-oauth-url:// and hexclave-mobile-oauth-url://.
Backend test validation for native app URL schemes
apps/backend/src/lib/redirect-urls.test.tsx
Tests expanded to assert acceptance of both schemes for success/error/oauth-callback and added rejections for hexclave://callback and hexclave-mobile-oauth-url-extra://callback.
Swift SDK examples, usage, and tests
sdks/implementations/swift/...
Swift example apps, StackClientApp, tests, and README updated to use hexclave-mobile-oauth-url:// as the canonical callback scheme and updated the signInWithOAuth callbackScheme constant and example strings.
SDK specification and developer documentation
sdks/spec/src/apps/client-app.spec.md
Docs updated to document hexclave-mobile-oauth-url:// as the canonical callback scheme for new SDKs and note indefinite backend acceptance of the legacy scheme.

Dev tool rebrand

Layer / File(s) Summary
Dev tool trigger logo
packages/template/src/dev-tool/dev-tool-core.ts
Replaced old STACK_LOGO_SVG with HEXCLAVE_LOGO_SVG and updated rendering to use the new SVG HTML.

Sequence Diagram(s)

sequenceDiagram
  participant ClientApp
  participant ASWebAuthenticationSession
  participant Backend_validateRedirectUrl
  ClientApp->>ASWebAuthenticationSession: open OAuth (callbackScheme: hexclave-mobile-oauth-url / stack-auth-mobile-oauth-url)
  ASWebAuthenticationSession->>Backend_validateRedirectUrl: submit redirect URL for validation
  Backend_validateRedirectUrl-->>ASWebAuthenticationSession: validation result (accepted / rejected)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • N2D4
  • nams1570

Poem

🐰 I hopped through code to change a scheme,
hexclave now leads the OAuth dream.
Legacy hops stay, side by side,
Tests and docs now take the ride.
✨📱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: backend accepting both the new hexclave-mobile-oauth-url scheme and the legacy stack-auth-mobile-oauth-url scheme.
Description check ✅ Passed The description is comprehensive and well-structured, covering what changed, why the changes are safe, a detailed file-by-file breakdown, verification steps, and risk assessment.
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 hexclave-mobile-oauth-url-dual-accept

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 widens isAcceptedNativeAppUrl to accept hexclave-mobile-oauth-url:// alongside the legacy stack-auth-mobile-oauth-url://, using a dual-accept strategy so already-shipped customer iOS/macOS apps (which have the old scheme baked into Info.plist) continue to work without requiring an App Store update.

  • redirect-urls.tsx: Two-line OR-check added to isAcceptedNativeAppUrl; exact url.protocol match prevents any prefix-collision with adjacent schemes like hexclave:// or hexclave-mobile-oauth-url-extra://.
  • redirect-urls.test.tsx: Positive tests for the new scheme in isAcceptedNativeAppUrl, negative parity tests in validateRedirectUrl (confirming native-app URLs still bypass the trusted-domain list only through isAcceptedNativeAppUrl), and boundary rejection tests for similar-looking but invalid schemes.
  • client-app.spec.md: SDK spec updated to clearly distinguish canonical (new SDKs) from legacy (frozen Swift SDK) schemes, with explicit note that both are accepted indefinitely.

Confidence Score: 5/5

Safe to merge — the change adds exactly one new accepted scheme string and cannot remove or alter any previously accepted URL.

The core edit is a two-line OR-check on an exact url.protocol comparison, well-tested with both positive and negative assertions, and the existing Swift SDK is intentionally left untouched so no live traffic path changes.

No files require special attention.

Important Files Changed

Filename Overview
packages/stack-shared/src/utils/redirect-urls.tsx Core logic change: isAcceptedNativeAppUrl now OR-checks both schemes; minimal, well-commented, exact-match via url.protocol so no prefix-collision risk.
apps/backend/src/lib/redirect-urls.test.tsx Tests added for new scheme in both isAcceptedNativeAppUrl (positive) and validateRedirectUrl (negative, ensuring native-app URLs are not accepted there); coverage is thorough.
sdks/spec/src/apps/client-app.spec.md Spec updated to document both canonical and legacy schemes, with clear guidance that new SDKs should use the Hexclave scheme.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Native App sends OAuth callback URL] --> B{isAcceptedNativeAppUrl?}
    B --> C{url.protocol === 'stack-auth-mobile-oauth-url:'}
    B --> D{url.protocol === 'hexclave-mobile-oauth-url:'}
    C -- Yes --> E[Accept - Legacy SDK]
    D -- Yes --> F[Accept - Canonical Hexclave SDK]
    C -- No --> G{Either matched?}
    D -- No --> G
    G -- Neither --> H[Reject]
Loading

Reviews (1): Last reviewed commit: "feat(backend): dual-accept hexclave-mobi..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/backend/src/lib/redirect-urls.test.tsx`:
- Around line 637-640: The legacy-scheme test in redirect-urls.test.tsx is
missing an assertion for the legacy oauth-callback path; update the "should
accept the legacy native app OAuth URL scheme" test that calls
isAcceptedNativeAppUrl to include
expect(isAcceptedNativeAppUrl('stack-auth-mobile-oauth-url://oauth-callback')).toBe(true)
alongside the existing 'success' and 'error' assertions so the legacy scheme has
parity with the canonical oauth-callback coverage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9494bc71-361c-434a-bc44-8661b3a853ca

📥 Commits

Reviewing files that changed from the base of the PR and between c753ab2 and b49459a.

📒 Files selected for processing (3)
  • apps/backend/src/lib/redirect-urls.test.tsx
  • packages/stack-shared/src/utils/redirect-urls.tsx
  • sdks/spec/src/apps/client-app.spec.md

Comment thread apps/backend/src/lib/redirect-urls.test.tsx
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 3 files

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

Re-trigger cubic

…orten backend comment

Builds on the previous commit. Backend now accepts both schemes, so the
StackAuth Swift SDK can safely emit the canonical hexclave-mobile-oauth-url
scheme without breaking already-shipped customer App Store binaries
(those still emit the legacy scheme and the backend still accepts it).

ASWebAuthenticationSession's callbackURLScheme: parameter intercepts the
callback ephemerally, so no customer Info.plist change is required.

- StackClientApp.swift: callbackScheme constant + fatalError example strings.
- OAuthTests.swift: test fixture URLs (no assertions depend on the scheme).
- StackAuthiOSApp.swift / StackAuthMacOSApp.swift: default values in the example UIs.
- swift/README.md: docs point at the new scheme; compat note mentions the legacy one.
- spec/client-app.spec.md: tightened wording — old scheme is for already-shipped binaries, not 'the frozen StackAuth Swift SDK' (which now also emits the new scheme).
- redirect-urls.tsx: shortened the dual-accept comment to one line.
The spec is the source of truth for generating SDK implementations; it
should describe the canonical wire behavior only. Backend dual-accept of
the legacy 'stack-auth-mobile-oauth-url://' scheme is a backend
implementation detail (documented in the code comment at
packages/stack-shared/src/utils/redirect-urls.tsx) and git history
preserves the previous spec wording. New SDK authors reading this spec
now see one canonical scheme.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sdks/implementations/swift/Sources/StackAuth/StackClientApp.swift (1)

189-199: ⚠️ Potential issue | 🔴 Critical

Ensure example iOS/macOS apps register hexclave-mobile-oauth-url in CFBundleURLSchemes

StackClientApp.swift sets callbackScheme = "hexclave-mobile-oauth-url" and uses it for both the OAuth redirect URLs and ASWebAuthenticationSession(callbackURLScheme:). The example apps’ Info.plist files under sdks/implementations/swift/Examples/StackAuthiOS and sdks/implementations/swift/Examples/StackAuthMacOS must include hexclave-mobile-oauth-url in CFBundleURLSchemes; a targeted search of those example Info.plist files returned no matches, so OAuth callback routing is likely to fail.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/implementations/swift/Sources/StackAuth/StackClientApp.swift` around
lines 189 - 199, The app uses callbackScheme = "hexclave-mobile-oauth-url" in
StackClientApp.swift (used in getOAuthUrl redirectUrl and
ASWebAuthenticationSession(callbackURLScheme:)), but the example iOS and macOS
apps must register that scheme in their Info.plist; open the Example targets
(StackAuthiOS and StackAuthMacOS) Info.plist files and add
"hexclave-mobile-oauth-url" under CFBundleURLTypes → CFBundleURLSchemes so the
ASWebAuthenticationSession callback and OAuth redirects resolve correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@sdks/implementations/swift/Sources/StackAuth/StackClientApp.swift`:
- Around line 189-199: The app uses callbackScheme = "hexclave-mobile-oauth-url"
in StackClientApp.swift (used in getOAuthUrl redirectUrl and
ASWebAuthenticationSession(callbackURLScheme:)), but the example iOS and macOS
apps must register that scheme in their Info.plist; open the Example targets
(StackAuthiOS and StackAuthMacOS) Info.plist files and add
"hexclave-mobile-oauth-url" under CFBundleURLTypes → CFBundleURLSchemes so the
ASWebAuthenticationSession callback and OAuth redirects resolve correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1fcc1ffc-7072-4e5a-960c-4a03e39691d0

📥 Commits

Reviewing files that changed from the base of the PR and between b49459a and ccdc1a6.

📒 Files selected for processing (7)
  • packages/stack-shared/src/utils/redirect-urls.tsx
  • sdks/implementations/swift/Examples/StackAuthMacOS/StackAuthMacOS/StackAuthMacOSApp.swift
  • sdks/implementations/swift/Examples/StackAuthiOS/StackAuthiOS/StackAuthiOSApp.swift
  • sdks/implementations/swift/README.md
  • sdks/implementations/swift/Sources/StackAuth/StackClientApp.swift
  • sdks/implementations/swift/Tests/StackAuthTests/OAuthTests.swift
  • sdks/spec/src/apps/client-app.spec.md
✅ Files skipped from review due to trivial changes (3)
  • sdks/implementations/swift/README.md
  • sdks/implementations/swift/Examples/StackAuthMacOS/StackAuthMacOS/StackAuthMacOSApp.swift
  • sdks/spec/src/apps/client-app.spec.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/stack-shared/src/utils/redirect-urls.tsx

Copy link
Copy Markdown

@vercel vercel Bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

The iOS example app does not register the 'hexclave-mobile-oauth-url' URL scheme in its Info.plist build settings, preventing OAuth callbacks from being routed back to the app.

Fix on Vercel

Comment thread apps/backend/src/lib/redirect-urls.test.tsx
Comment thread sdks/spec/src/apps/client-app.spec.md
@BilalG1 BilalG1 requested a review from N2D4 May 27, 2026 20:37
@BilalG1 BilalG1 assigned N2D4 and unassigned BilalG1 May 27, 2026
…uth glyph

The dev-tool floating trigger button was still rendering the old
stacked-layers Stack Auth mark even after the rest of the dev tool
(aria-label, title, storage keys, docs URL) was rebranded to Hexclave.

Replace STACK_LOGO_SVG with HEXCLAVE_LOGO_SVG — a monochrome
currentColor rendering of the hexagon-with-three-radial-bars benzene
mark from apps/dashboard/public/hexclave-icon.svg. Gradient and glow
are stripped because the trigger glyph is 22x22 inside a colored chip
with color: white, so a flat monochrome reads better at that size.
@github-actions github-actions Bot assigned BilalG1 and unassigned N2D4 May 27, 2026
@BilalG1 BilalG1 merged commit c0fefd3 into dev May 27, 2026
35 of 37 checks passed
@BilalG1 BilalG1 deleted the hexclave-mobile-oauth-url-dual-accept branch May 27, 2026 22:44
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.

2 participants