Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions android/src/main/java/com/auth0/react/A0Auth0Module.kt
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,35 @@ class A0Auth0Module(private val reactContext: ReactApplicationContext) : A0Auth0
}
}

@ReactMethod
override fun getSSOCredentials(parameters: ReadableMap?, headers: ReadableMap?, promise: Promise) {
val params = mutableMapOf<String, String>()
parameters?.toHashMap()?.forEach { (key, value) ->
value?.let { params[key] = it.toString() }
}

secureCredentialsManager.getSsoCredentials(
Comment on lines +438 to +444
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

The headers parameter is accepted but not used in the Android implementation. It's converted from ReadableMap but never passed to secureCredentialsManager.getSsoCredentials(). If headers are not supported by the Android SDK, this should be documented, or if they should be supported, they need to be passed through to the native SDK call.

Copilot uses AI. Check for mistakes.
params,
object : com.auth0.android.callback.Callback<com.auth0.android.result.SSOCredentials, CredentialsManagerException> {
override fun onSuccess(result: com.auth0.android.result.SSOCredentials) {
val map = WritableNativeMap().apply {
putString("sessionTransferToken", result.sessionTransferToken)
putString("tokenType", result.tokenType)
putInt("expiresIn", result.expiresIn)
result.idToken?.let { putString("idToken", it) }
result.refreshToken?.let { putString("refreshToken", it) }
}
promise.resolve(map)
}

override fun onFailure(error: CredentialsManagerException) {
val errorCode = deduceErrorCode(error)
promise.reject(errorCode, error.message, error)
}
}
)
}

override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
// No-op
}
Expand Down
4 changes: 4 additions & 0 deletions android/src/main/oldarch/com/auth0/react/A0Auth0Spec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,8 @@ abstract class A0Auth0Spec(context: ReactApplicationContext) : ReactContextBaseJ
@ReactMethod
@DoNotStrip
abstract fun clearDPoPKey(promise: Promise)

@ReactMethod
@DoNotStrip
abstract fun getSSOCredentials(parameters: ReadableMap?, headers: ReadableMap?, promise: Promise)
}
7 changes: 7 additions & 0 deletions ios/A0Auth0.mm
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@ - (dispatch_queue_t)methodQueue
[self.nativeBridge getDPoPHeadersWithUrl:url method:method accessToken:accessToken tokenType:tokenType nonce:nonce resolve:resolve reject:reject];
}

RCT_EXPORT_METHOD(getSSOCredentials:(NSDictionary *)parameters
headers:(NSDictionary *)headers
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject) {
[self.nativeBridge getSSOCredentialsWithParameters:parameters headers:headers resolve:resolve reject:reject];
}




Expand Down
29 changes: 29 additions & 0 deletions ios/NativeBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,35 @@ public class NativeBridge: NSObject {
resolve(removed)
}

@objc public func getSSOCredentials(parameters: [String: Any], headers: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
credentialsManager.ssoCredentials(parameters: parameters, headers: headers) { result in
switch result {
case .success(let ssoCredentials):
var response: [String: Any] = [
"sessionTransferToken": ssoCredentials.sessionTransferToken,
"tokenType": ssoCredentials.tokenType,
"expiresIn": ssoCredentials.expiresIn
]

// Add optional fields if present
if let idToken = ssoCredentials.idToken {
response["idToken"] = idToken
}
if let refreshToken = ssoCredentials.refreshToken {
response["refreshToken"] = refreshToken
}

resolve(response)
case .failure(let error):
reject(
NativeBridge.credentialsManagerErrorCode,
error.localizedDescription,
error
)
}
}
}

@objc public func getDPoPHeaders(url: String, method: String, accessToken: String, tokenType: String, nonce: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
// Validate parameters
guard !url.isEmpty else {
Expand Down
43 changes: 42 additions & 1 deletion src/core/interfaces/ICredentialsManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Credentials } from '../../types';
import type { Credentials, SessionTransferCredentials } from '../../types';
import { ApiCredentials } from '../models';

/**
Expand Down Expand Up @@ -50,6 +50,47 @@ export interface ICredentialsManager {
*/
clearCredentials(): Promise<void>;

/**
* Obtains session transfer credentials for performing Native to Web SSO.
*
* @remarks
* This method exchanges the stored refresh token for a session transfer token
* that can be used to authenticate in web contexts without requiring the user
* to log in again. The session transfer token can be passed as a cookie or
* query parameter to the `/authorize` endpoint to establish a web session.
*
* Session transfer tokens are short-lived and expire after a few minutes.
* Once expired, they can no longer be used for web SSO.
*
* If Refresh Token Rotation is enabled, this method will also update the stored
* credentials with new tokens (ID token and refresh token) returned from the
* token exchange.
*
* @param parameters Optional additional parameters to pass to the token exchange.
* @param headers Optional additional headers to include in the token exchange request.
* @returns A promise that resolves with the session transfer credentials.
*
* @example
* ```typescript
* // Get session transfer credentials
* const ssoCredentials = await auth0.credentialsManager.getSSOCredentials();
*
* // Option 1: Use as a cookie
* const cookie = `auth0_session_transfer_token=${ssoCredentials.sessionTransferToken}; path=/; domain=.yourdomain.com; secure; httponly`;
* document.cookie = cookie;
*
* // Option 2: Use as a query parameter
* const authorizeUrl = `https://${domain}/authorize?session_transfer_token=${ssoCredentials.sessionTransferToken}&...`;
Comment on lines +75 to +83
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

The example code demonstrates using document.cookie and window.location.href, which are web APIs, but this is a native-only method. This is confusing because the example shows web usage. Consider updating the example to show how to pass these credentials from a native app to a WebView or web context, or clarify that the example is for the web context that receives the token (not where getSSOCredentials is called).

Suggested change
* // Get session transfer credentials
* const ssoCredentials = await auth0.credentialsManager.getSSOCredentials();
*
* // Option 1: Use as a cookie
* const cookie = `auth0_session_transfer_token=${ssoCredentials.sessionTransferToken}; path=/; domain=.yourdomain.com; secure; httponly`;
* document.cookie = cookie;
*
* // Option 2: Use as a query parameter
* const authorizeUrl = `https://${domain}/authorize?session_transfer_token=${ssoCredentials.sessionTransferToken}&...`;
* // Native context: Obtain the session transfer token
* const ssoCredentials = await auth0.credentialsManager.getSSOCredentials();
*
* // Pass the sessionTransferToken to your WebView or browser context.
* // For example, inject it as a query parameter or via postMessage:
* const authorizeUrl = `https://${domain}/authorize?session_transfer_token=${ssoCredentials.sessionTransferToken}&...`;
* // Open the URL in a WebView or browser, or inject the token as needed.
*
* // --- In the web context (e.g., inside the WebView) ---
* // Option 1: Set as a cookie (injected JS in WebView)
* document.cookie = `auth0_session_transfer_token=${sessionTransferToken}; path=/; domain=.yourdomain.com; secure; httponly`;
*
* // Option 2: Use as a query parameter (already included in authorizeUrl)

Copilot uses AI. Check for mistakes.
* window.location.href = authorizeUrl;
* ```
*
* @see https://auth0.com/docs/authenticate/login/configure-silent-authentication
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

The documentation URL https://auth0.com/docs/authenticate/login/configure-silent-authentication references silent authentication, which is a different feature from Native to Web SSO using session transfer tokens. Consider linking to more specific documentation about session transfer tokens or Native to Web SSO if available, as silent authentication typically refers to refreshing tokens without user interaction in the same context, not transferring sessions between native and web contexts.

Suggested change
* @see https://auth0.com/docs/authenticate/login/configure-silent-authentication
* @see https://auth0.com/docs/authenticate/login/session-transfer-tokens

Copilot uses AI. Check for mistakes.
*/
getSSOCredentials(
parameters?: Record<string, any>,
headers?: Record<string, string>
): Promise<SessionTransferCredentials>;

/**
* Retrieves API-specific credentials for a given audience using the Multi-Resource Refresh Token (MRRT).
*
Expand Down
47 changes: 47 additions & 0 deletions src/hooks/Auth0Context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type {
ResetPasswordParameters,
MfaChallengeResponse,
DPoPHeadersParams,
SessionTransferCredentials,
} from '../types';
import type { ApiCredentials } from '../core/models';
import type {
Expand Down Expand Up @@ -276,6 +277,51 @@ export interface Auth0ContextInterface extends AuthState {
getDPoPHeaders: (
params: DPoPHeadersParams
) => Promise<Record<string, string>>;

/**
* Obtains session transfer credentials for performing Native to Web SSO.
*
* @remarks
* This method exchanges the stored refresh token for a session transfer token
* that can be used to authenticate in web contexts without requiring the user
* to log in again. The session transfer token can be passed as a cookie or
* query parameter to the `/authorize` endpoint to establish a web session.
*
* Session transfer tokens are short-lived and expire after a few minutes.
* Once expired, they can no longer be used for web SSO.
*
* If Refresh Token Rotation is enabled, this method will also update the stored
* credentials with new tokens (ID token and refresh token) returned from the
* token exchange.
*
* **Platform specific:** This method is only available on native platforms (iOS/Android).
* On web, it will throw an error.
*
* @param parameters Optional additional parameters to pass to the token exchange.
* @param headers Optional additional headers to include in the token exchange request.
* @returns A promise that resolves with the session transfer credentials.
*
* @example
* ```typescript
* // Get session transfer credentials
* const ssoCredentials = await getSSOCredentials();
*
* // Option 1: Use as a cookie (recommended)
* const cookie = `auth0_session_transfer_token=${ssoCredentials.sessionTransferToken}; path=/; domain=.yourdomain.com; secure; httponly`;
* document.cookie = cookie;
* window.location.href = `https://yourdomain.com/authorize?client_id=${clientId}&...`;
*
* // Option 2: Use as a query parameter
* const authorizeUrl = `https://yourdomain.com/authorize?session_transfer_token=${ssoCredentials.sessionTransferToken}&client_id=${clientId}&...`;
* window.location.href = authorizeUrl;
* ```
Comment on lines +304 to +317
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

The example code demonstrates using document.cookie and window.location.href, which are web APIs, but the documentation states this method is "only available on native platforms (iOS/Android)". This is confusing because the example shows web usage. Consider updating the example to show how to pass these credentials from a native app to a WebView or web context, or clarify that the example is for the web context that receives the token (not where getSSOCredentials is called).

Copilot uses AI. Check for mistakes.
*
* @see https://auth0.com/docs/authenticate/login/configure-silent-authentication
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

The documentation URL https://auth0.com/docs/authenticate/login/configure-silent-authentication references silent authentication, which is a different feature from Native to Web SSO using session transfer tokens. Consider linking to more specific documentation about session transfer tokens or Native to Web SSO if available, as silent authentication typically refers to refreshing tokens without user interaction in the same context, not transferring sessions between native and web contexts.

Suggested change
* @see https://auth0.com/docs/authenticate/login/configure-silent-authentication
* @see https://auth0.com/docs/authenticate/login/session-transfer-tokens

Copilot uses AI. Check for mistakes.
*/
getSSOCredentials: (
parameters?: Record<string, any>,
headers?: Record<string, string>
) => Promise<SessionTransferCredentials>;
}

const stub = (): any => {
Expand Down Expand Up @@ -310,6 +356,7 @@ const initialContext: Auth0ContextInterface = {
resetPassword: stub,
revokeRefreshToken: stub,
getDPoPHeaders: stub,
getSSOCredentials: stub,
};

export const Auth0Context =
Expand Down
21 changes: 21 additions & 0 deletions src/hooks/Auth0Provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,25 @@ export const Auth0Provider = ({
}
}, [client]);

const getSSOCredentials = useCallback(
async (
parameters?: Record<string, any>,
headers?: Record<string, string>
) => {
try {
return await client.credentialsManager.getSSOCredentials(
parameters,
headers
);
} catch (e) {
const error = e as AuthError;
dispatch({ type: 'ERROR', error });
throw error;
}
},
[client]
);

const cancelWebAuth = useCallback(
() => voidFlow(client.webAuth.cancelWebAuth()),
[client, voidFlow]
Expand Down Expand Up @@ -372,6 +391,7 @@ export const Auth0Provider = ({
getCredentials,
hasValidCredentials,
clearCredentials,
getSSOCredentials,
getApiCredentials,
clearApiCredentials,
cancelWebAuth,
Expand Down Expand Up @@ -399,6 +419,7 @@ export const Auth0Provider = ({
getCredentials,
hasValidCredentials,
clearCredentials,
getSSOCredentials,
getApiCredentials,
clearApiCredentials,
cancelWebAuth,
Expand Down
Loading