diff --git a/apps/backend/src/lib/redirect-urls.test.tsx b/apps/backend/src/lib/redirect-urls.test.tsx index 41cd3799cc..6d8ff60839 100644 --- a/apps/backend/src/lib/redirect-urls.test.tsx +++ b/apps/backend/src/lib/redirect-urls.test.tsx @@ -612,6 +612,9 @@ describe('validateRedirectUrl', () => { expect(validateRedirectUrl('stack-auth-mobile-oauth-url://success', tenancy)).toBe(false); expect(validateRedirectUrl('stack-auth-mobile-oauth-url://error', tenancy)).toBe(false); expect(validateRedirectUrl('stack-auth-mobile-oauth-url://oauth-callback', tenancy)).toBe(false); + expect(validateRedirectUrl('hexclave-mobile-oauth-url://success', tenancy)).toBe(false); + expect(validateRedirectUrl('hexclave-mobile-oauth-url://error', tenancy)).toBe(false); + expect(validateRedirectUrl('hexclave-mobile-oauth-url://oauth-callback', tenancy)).toBe(false); }); it('should not accept other custom schemes without trusted domain config', () => { @@ -631,15 +634,23 @@ describe('validateRedirectUrl', () => { }); describe('isAcceptedNativeAppUrl', () => { - it('should accept the native app OAuth URL scheme', () => { + it('should accept the legacy native app OAuth URL scheme', () => { expect(isAcceptedNativeAppUrl('stack-auth-mobile-oauth-url://success')).toBe(true); expect(isAcceptedNativeAppUrl('stack-auth-mobile-oauth-url://error')).toBe(true); }); + it('should accept the canonical Hexclave native app OAuth URL scheme', () => { + expect(isAcceptedNativeAppUrl('hexclave-mobile-oauth-url://success')).toBe(true); + expect(isAcceptedNativeAppUrl('hexclave-mobile-oauth-url://error')).toBe(true); + expect(isAcceptedNativeAppUrl('hexclave-mobile-oauth-url://oauth-callback')).toBe(true); + }); + it('should reject other custom schemes', () => { expect(isAcceptedNativeAppUrl('myapp://callback')).toBe(false); expect(isAcceptedNativeAppUrl('stackauth-myapp://callback')).toBe(false); expect(isAcceptedNativeAppUrl('stack-auth://callback')).toBe(false); + expect(isAcceptedNativeAppUrl('hexclave://callback')).toBe(false); + expect(isAcceptedNativeAppUrl('hexclave-mobile-oauth-url-extra://callback')).toBe(false); expect(isAcceptedNativeAppUrl('https://example.com/callback')).toBe(false); expect(isAcceptedNativeAppUrl('http://localhost:3000/callback')).toBe(false); }); diff --git a/packages/stack-shared/src/utils/redirect-urls.tsx b/packages/stack-shared/src/utils/redirect-urls.tsx index 5ac92217cd..29baf15572 100644 --- a/packages/stack-shared/src/utils/redirect-urls.tsx +++ b/packages/stack-shared/src/utils/redirect-urls.tsx @@ -160,7 +160,9 @@ export function isAcceptedNativeAppUrl(urlOrString: string): boolean { const url = createUrlIfValid(urlOrString); if (!url) return false; - return url.protocol === 'stack-auth-mobile-oauth-url:'; + // Legacy scheme accepted indefinitely; baked into already-shipped Swift SDK binaries. + return url.protocol === 'stack-auth-mobile-oauth-url:' + || url.protocol === 'hexclave-mobile-oauth-url:'; } export function validateRedirectUrl( diff --git a/packages/template/src/dev-tool/dev-tool-core.ts b/packages/template/src/dev-tool/dev-tool-core.ts index c267deb36b..ba135e8c8e 100644 --- a/packages/template/src/dev-tool/dev-tool-core.ts +++ b/packages/template/src/dev-tool/dev-tool-core.ts @@ -75,7 +75,10 @@ const DEFAULT_STATE: DevToolState = { panelHeight: 520, }; -const STACK_LOGO_SVG = ''; +// Hexclave mark — hexagon outline with three radial bars, monochrome via currentColor +// so it inherits the trigger logo's color. Sourced from apps/dashboard/public/hexclave-icon.svg +// (gradient + glow stripped; this is a tiny trigger glyph, not the full brand mark). +const HEXCLAVE_LOGO_SVG = ''; // --------------------------------------------------------------------------- // State management @@ -454,7 +457,7 @@ function createTrigger(onClick: () => void): { element: HTMLElement; cleanup: () title: 'Hexclave Dev Tools', }); const logoSpan = h('span', { className: 'sdt-trigger-logo' }); - setHtml(logoSpan, STACK_LOGO_SVG); + setHtml(logoSpan, HEXCLAVE_LOGO_SVG); btn.appendChild(logoSpan); let placement = loadPlacement() ?? { corner: 'bottom-right' as TriggerCorner }; diff --git a/sdks/implementations/swift/Examples/StackAuthMacOS/StackAuthMacOS/StackAuthMacOSApp.swift b/sdks/implementations/swift/Examples/StackAuthMacOS/StackAuthMacOS/StackAuthMacOSApp.swift index e7df831d16..c12c18063e 100644 --- a/sdks/implementations/swift/Examples/StackAuthMacOS/StackAuthMacOS/StackAuthMacOSApp.swift +++ b/sdks/implementations/swift/Examples/StackAuthMacOS/StackAuthMacOS/StackAuthMacOSApp.swift @@ -1238,8 +1238,8 @@ class MacOSPresentationContextProvider: NSObject, ASWebAuthenticationPresentatio struct OAuthView: View { @Bindable var viewModel: SDKTestViewModel @State private var provider = "google" - @State private var redirectUrl = "stack-auth-mobile-oauth-url://success" - @State private var errorRedirectUrl = "stack-auth-mobile-oauth-url://error" + @State private var redirectUrl = "hexclave-mobile-oauth-url://success" + @State private var errorRedirectUrl = "hexclave-mobile-oauth-url://error" @State private var isSigningIn = false private let presentationProvider = MacOSPresentationContextProvider() diff --git a/sdks/implementations/swift/Examples/StackAuthiOS/StackAuthiOS/StackAuthiOSApp.swift b/sdks/implementations/swift/Examples/StackAuthiOS/StackAuthiOS/StackAuthiOSApp.swift index 91ef545277..7d0d93ea7a 100644 --- a/sdks/implementations/swift/Examples/StackAuthiOS/StackAuthiOS/StackAuthiOSApp.swift +++ b/sdks/implementations/swift/Examples/StackAuthiOS/StackAuthiOS/StackAuthiOSApp.swift @@ -1252,8 +1252,8 @@ struct ContactChannelsView: View { struct OAuthView: View { @Bindable var viewModel: SDKTestViewModel @State private var provider = "google" - @State private var redirectUrl = "stack-auth-mobile-oauth-url://success" - @State private var errorRedirectUrl = "stack-auth-mobile-oauth-url://error" + @State private var redirectUrl = "hexclave-mobile-oauth-url://success" + @State private var errorRedirectUrl = "hexclave-mobile-oauth-url://error" @State private var isSigningIn = false private let presentationProvider = iOSPresentationContextProvider() diff --git a/sdks/implementations/swift/README.md b/sdks/implementations/swift/README.md index 591d1655f9..f46a483b11 100644 --- a/sdks/implementations/swift/README.md +++ b/sdks/implementations/swift/README.md @@ -88,20 +88,20 @@ Two approaches for OAuth authentication: ```swift // Opens auth session, handles callback automatically -// Uses fixed callback scheme: stack-auth-mobile-oauth-url:// +// Uses fixed callback scheme: hexclave-mobile-oauth-url:// try await stack.signInWithOAuth(provider: "google") ``` **2. Manual URL handling** - For custom implementations: -> **Note:** The `stack-auth-mobile-oauth-url://` scheme is automatically accepted. +> **Note:** The `hexclave-mobile-oauth-url://` scheme is automatically accepted (the legacy `stack-auth-mobile-oauth-url://` scheme also remains accepted for backwards compatibility). ```swift // Get the OAuth URL (must provide absolute URLs) let oauth = try await stack.getOAuthUrl( provider: "google", - redirectUrl: "stack-auth-mobile-oauth-url://success", - errorRedirectUrl: "stack-auth-mobile-oauth-url://error" + redirectUrl: "hexclave-mobile-oauth-url://success", + errorRedirectUrl: "hexclave-mobile-oauth-url://error" ) // Open oauth.url in your own browser/webview diff --git a/sdks/implementations/swift/Sources/StackAuth/StackClientApp.swift b/sdks/implementations/swift/Sources/StackAuth/StackClientApp.swift index a4dedfde08..c570be745e 100644 --- a/sdks/implementations/swift/Sources/StackAuth/StackClientApp.swift +++ b/sdks/implementations/swift/Sources/StackAuth/StackClientApp.swift @@ -131,10 +131,10 @@ public actor StackClientApp { ) async throws -> OAuthUrlResult { // Validate that URLs are absolute URLs (panic if not - these are programmer errors) guard redirectUrl.contains("://") else { - fatalError("redirectUrl must be an absolute URL (e.g., 'stack-auth-mobile-oauth-url://success')") + fatalError("redirectUrl must be an absolute URL (e.g., 'hexclave-mobile-oauth-url://success')") } guard errorRedirectUrl.contains("://") else { - fatalError("errorRedirectUrl must be an absolute URL (e.g., 'stack-auth-mobile-oauth-url://error')") + fatalError("errorRedirectUrl must be an absolute URL (e.g., 'hexclave-mobile-oauth-url://error')") } let actualState = state ?? generateRandomString(length: 32) @@ -186,7 +186,7 @@ public actor StackClientApp { return } - let callbackScheme = "stack-auth-mobile-oauth-url" + let callbackScheme = "hexclave-mobile-oauth-url" let oauth = try await getOAuthUrl( provider: provider, redirectUrl: callbackScheme + "://success", diff --git a/sdks/implementations/swift/Tests/StackAuthTests/OAuthTests.swift b/sdks/implementations/swift/Tests/StackAuthTests/OAuthTests.swift index 97fbeb249f..caa197f9bf 100644 --- a/sdks/implementations/swift/Tests/StackAuthTests/OAuthTests.swift +++ b/sdks/implementations/swift/Tests/StackAuthTests/OAuthTests.swift @@ -6,8 +6,8 @@ import Foundation struct OAuthTests { // Default test URLs (must be absolute URLs) - let testRedirectUrl = "stack-auth-mobile-oauth-url://success" - let testErrorRedirectUrl = "stack-auth-mobile-oauth-url://error" + let testRedirectUrl = "hexclave-mobile-oauth-url://success" + let testErrorRedirectUrl = "hexclave-mobile-oauth-url://error" // MARK: - OAuth URL Generation Tests diff --git a/sdks/spec/src/apps/client-app.spec.md b/sdks/spec/src/apps/client-app.spec.md index f2785be7da..ea76075d65 100644 --- a/sdks/spec/src/apps/client-app.spec.md +++ b/sdks/spec/src/apps/client-app.spec.md @@ -66,7 +66,7 @@ Note: Additional provider scopes are configured via oauthScopesOnSignIn construc Implementation: 1. Construct full redirect URLs using a fixed callback scheme: - - Native apps: "stack-auth-mobile-oauth-url://success" and "stack-auth-mobile-oauth-url://error" + - Native apps: "hexclave-mobile-oauth-url://success" and "hexclave-mobile-oauth-url://error" - Browser: Use the configured OAuth callback handler URL as redirect_uri and window.location to construct absolute URLs - Browser: If options.returnTo is provided, pass it as afterCallbackRedirectUrl, not as redirect_uri @@ -82,7 +82,7 @@ Implementation: 4. Open the authorization URL: - Browser: perform redirect according to redirectMethod - - iOS/macOS: ASWebAuthenticationSession with callbackURLScheme: "stack-auth-mobile-oauth-url" + - iOS/macOS: ASWebAuthenticationSession with callbackURLScheme: "hexclave-mobile-oauth-url" - Android: Custom Tabs with callback URL registered as deep link - Desktop: Open system browser with registered URL scheme for callback @@ -166,7 +166,7 @@ Returns: { url: string, state: string, codeVerifier: string, redirectUrl: string redirectUrl: The redirect URL (same as input, needed for token exchange - must match exactly) Note on URL schemes: -- The "stack-auth-mobile-oauth-url://" scheme is automatically accepted by the backend without any configuration. +- The "hexclave-mobile-oauth-url://" scheme is automatically accepted by the backend without any configuration. Implementation: 1. Generate or use provided state and codeVerifier