Skip to content
Merged
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
172 changes: 172 additions & 0 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
- [Using MRRT with Hooks](#using-mrrt-with-hooks)
- [Using MRRT with Auth0 Class](#using-mrrt-with-auth0-class)
- [Web Platform Configuration](#web-platform-configuration)
- [Native to Web SSO (Early Access)](#native-to-web-sso-early-access)
- [Overview](#native-to-web-sso-overview)
- [Prerequisites](#native-to-web-sso-prerequisites)
- [Using Native to Web SSO with Hooks](#using-native-to-web-sso-with-hooks)
- [Using Native to Web SSO with Auth0 Class](#using-native-to-web-sso-with-auth0-class)
- [Sending the Session Transfer Token](#sending-the-session-transfer-token)
- [Bot Protection](#bot-protection)
- [Domain Switching](#domain-switching)
- [Android](#android)
Expand Down Expand Up @@ -432,6 +438,172 @@ function App() {
}
```

## Native to Web SSO (Early Access)

> ⚠️ **Early Access Feature**: Native to Web SSO is currently available in Early Access. To use this feature, you must have an Enterprise plan. For more information, see [Product Release Stages](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages).

### Native to Web SSO Overview

Native to Web SSO allows authenticated users in your native mobile application to seamlessly transition to your web application without requiring them to log in again. This is achieved by exchanging a refresh token for a Session Transfer Token, which can then be used to establish a session in the web application.

The Session Transfer Token is:

- **Short-lived**: Expires after approximately 1 minute
- **Single-use**: Can only be used once to establish a web session
- **Secure**: Can be bound to the user's device through IP address or ASN

For detailed configuration and implementation guidance, see the [Auth0 Native to Web SSO documentation](https://auth0.com/docs/authenticate/single-sign-on/native-to-web/configure-implement-native-to-web).

### Native to Web SSO Prerequisites

Before using Native to Web SSO:

1. **Enable Native to Web SSO on your Auth0 tenant** - This feature requires an Enterprise plan
2. [**Configure your native application**](https://auth0.com/docs/authenticate/single-sign-on/native-to-web/configure-implement-native-to-web#configure-native-applications)
3. **Request `offline_access` scope during login** to ensure a refresh token is issued

### Using Native to Web SSO with Hooks

```tsx
import { useAuth0 } from 'react-native-auth0';
import { Linking } from 'react-native';

function MyComponent() {
const { authorize, getSSOCredentials } = useAuth0();

const login = async () => {
// Login with offline_access to get a refresh token
await authorize({
scope: 'openid profile email offline_access',
});
};

const openWebApp = async () => {
try {
// Get session transfer credentials
const ssoCredentials = await getSSOCredentials();

console.log('Session Transfer Token:', ssoCredentials.sessionTransferToken);
console.log('Token Type:', ssoCredentials.tokenType);
console.log('Expires In:', ssoCredentials.expiresIn, 'seconds');

// Open web app with session transfer token as query parameter
const webAppUrl = `https://your-web-app.com/login?session_transfer_token=${ssoCredentials.sessionTransferToken}`;
await Linking.openURL(webAppUrl);
} catch (error) {
console.error('Failed to get SSO credentials:', error);
}
};

return (
// Your UI components
);
}
```

### Using Native to Web SSO with Auth0 Class

```js
import Auth0 from 'react-native-auth0';
import { Linking } from 'react-native';

const auth0 = new Auth0({
domain: 'YOUR_AUTH0_DOMAIN',
clientId: 'YOUR_AUTH0_CLIENT_ID',
});

// Login with offline_access scope
await auth0.webAuth.authorize({
scope: 'openid profile email offline_access',
});

// Get session transfer credentials
const ssoCredentials = await auth0.credentialsManager.getSSOCredentials();

console.log('Session Transfer Token:', ssoCredentials.sessionTransferToken);
console.log('Token Type:', ssoCredentials.tokenType);
console.log('Expires In:', ssoCredentials.expiresIn);

// Optional: ID Token and Refresh Token may be returned if RTR is enabled
if (ssoCredentials.idToken) {
console.log('ID Token:', ssoCredentials.idToken);
}
if (ssoCredentials.refreshToken) {
console.log('New Refresh Token received (RTR enabled)');
}

// Open your web application with the session transfer token
const webAppUrl = `https://your-web-app.com/login?session_transfer_token=${ssoCredentials.sessionTransferToken}`;
await Linking.openURL(webAppUrl);
```

### Sending the Session Transfer Token

There are two ways to send the Session Transfer Token to your web application:

#### Option 1: As a Query Parameter

Pass the token as a URL parameter when opening your web application:

```js
const ssoCredentials = await auth0.credentialsManager.getSSOCredentials();

// Your web app should extract the token and pass it to Auth0's /authorize endpoint
const webAppUrl = `https://your-web-app.com/login?session_transfer_token=${ssoCredentials.sessionTransferToken}`;
await Linking.openURL(webAppUrl);
```

Your web application should then include the `session_transfer_token` in the `/authorize` request:

```js
// In your web application
const urlParams = new URLSearchParams(window.location.search);
const sessionTransferToken = urlParams.get('session_transfer_token');

if (sessionTransferToken) {
// Include in your authorization request
const authorizeUrl =
`https://YOUR_AUTH0_DOMAIN/authorize?` +
`client_id=YOUR_WEB_CLIENT_ID&` +
`redirect_uri=${encodeURIComponent('https://your-web-app.com/callback')}&` +
`response_type=code&` +
`scope=openid profile email&` +
`session_transfer_token=${sessionTransferToken}`;

window.location.href = authorizeUrl;
}
```

#### Option 2: As a Cookie (WebView only)

If your application uses a WebView that supports cookie injection:

```js
import { WebView } from 'react-native-webview';

function WebAppView() {
const [cookies, setCookies] = useState('');

const prepareWebSession = async () => {
const ssoCredentials = await auth0.credentialsManager.getSSOCredentials();

// Set cookie that will be sent to Auth0
const cookie = `auth0_session_transfer_token=${ssoCredentials.sessionTransferToken}; path=/; domain=.your-auth0-domain.auth0.com; secure`;
setCookies(cookie);
};

return (
<WebView
source={{ uri: 'https://your-web-app.com' }}
sharedCookiesEnabled={true}
// Additional WebView configuration for cookie injection
/>
);
}
```

> **Note**: Cookie injection is platform-specific and may require additional configuration. The query parameter method is generally more straightforward and recommended for most use cases.

## Bot Protection

If you are using the [Bot Protection](https://auth0.com/docs/anomaly-detection/bot-protection) feature and performing database login/signup via the Authentication API, you need to handle the `requires_verification` error. It indicates that the request was flagged as suspicious and an additional verification step is necessary to log the user in. That verification step is web-based, so you need to use Universal Login to complete it.
Expand Down
30 changes: 30 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,36 @@ 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() }
}
// header is only for keeping the platform agnostic, only used in iOS

secureCredentialsManager.getSsoCredentials(
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. **iOS only** - this parameter is ignored on Android.
* @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}&...`;
* window.location.href = authorizeUrl;
* ```
*
* @see https://auth0.com/docs/authenticate/single-sign-on/native-to-web/configure-implement-native-to-web
*/
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
Loading