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