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
65 changes: 38 additions & 27 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -985,11 +985,7 @@ Custom Token Exchange allows you to exchange external identity provider tokens f
```typescript
import React from 'react';
import { Button, Alert } from 'react-native';
import {
useAuth0,
AuthenticationException,
AuthenticationErrorCodes,
} from 'react-native-auth0';
import { useAuth0, AuthError } from 'react-native-auth0';

function TokenExchangeScreen() {
const { customTokenExchange, user, error } = useAuth0();
Expand All @@ -1006,25 +1002,28 @@ function TokenExchangeScreen() {

Alert.alert('Success', `Logged in as ${user?.name}`);
} catch (e) {
if (e instanceof AuthenticationException) {
switch (e.type) {
case AuthenticationErrorCodes.INVALID_SUBJECT_TOKEN:
Alert.alert('Error', 'The external token is invalid or expired');
if (e instanceof AuthError) {
// Custom Token Exchange surfaces the OAuth 2.0 error from the token
// endpoint on `code`. See the RFC 8693 error responses and your Action's
// own failure reasons.
switch (e.code) {
case 'invalid_request':
Alert.alert('Error', 'The external token or token type is invalid');
break;
case 'invalid_grant':
Alert.alert('Error', 'The external token was rejected or expired');
break;
case AuthenticationErrorCodes.UNSUPPORTED_TOKEN_TYPE:
Alert.alert('Error', 'The token type is not supported');
case 'unsupported_token_type':
Alert.alert('Error', 'The external token type is not supported');
break;
case AuthenticationErrorCodes.TOKEN_EXCHANGE_NOT_CONFIGURED:
case 'unauthorized_client':
Alert.alert(
'Error',
'Custom Token Exchange is not configured for this tenant'
'Custom Token Exchange is not enabled for this client'
);
break;
case AuthenticationErrorCodes.TOKEN_VALIDATION_FAILED:
Alert.alert('Error', 'Token validation failed in Auth0 Action');
break;
case AuthenticationErrorCodes.NETWORK_ERROR:
Alert.alert('Error', 'Network error. Please check your connection.');
case 'access_denied':
Alert.alert('Error', 'Token validation failed in the Auth0 Action');
Comment thread
NandanPrabhu marked this conversation as resolved.
break;
default:
Alert.alert('Error', e.message);
Expand All @@ -1042,10 +1041,7 @@ function TokenExchangeScreen() {
### Using Custom Token Exchange with Auth0 Class

```typescript
import Auth0, {
AuthenticationException,
AuthenticationErrorCodes,
} from 'react-native-auth0';
import Auth0, { AuthError } from 'react-native-auth0';

const auth0 = new Auth0({
domain: 'YOUR_AUTH0_DOMAIN',
Expand All @@ -1064,14 +1060,14 @@ async function exchangeExternalToken(externalToken: string) {
console.log('Exchange successful:', credentials);
return credentials;
} catch (error) {
if (error instanceof AuthenticationException) {
if (error instanceof AuthError) {
// Access the underlying error details
console.error('Error type:', error.type);
console.error('Error code:', error.code);
console.error('Error message:', error.message);
console.error('Underlying error code:', error.underlyingError.code);
console.error('HTTP status:', error.status);

// Handle specific error types
if (error.type === AuthenticationErrorCodes.INVALID_SUBJECT_TOKEN) {
// Handle specific error codes
if (error.code === 'invalid_grant') {
// Token is invalid or expired - prompt user to re-authenticate
throw new Error('Please authenticate again with the external provider');
}
Expand Down Expand Up @@ -1849,6 +1845,21 @@ try {
}
```

The My Account API reports failures as [RFC 7807](https://datatracker.ietf.org/doc/html/rfc7807)
type URIs. `MyAccountError` normalizes those to a `MyAccountErrorCodes` value on `type` so your
error handling matches every other error class in the SDK, and preserves the original URI on
`typeUri` for logging or support tickets:

```typescript
catch (e) {
if (e instanceof MyAccountError) {
console.log(e.type); // "UNAUTHORIZED" — normalized, switch on this
console.log(e.typeUri); // "https://auth0.com/api-errors/A0E-401-0001" — raw, log this
console.log(e.statusCode); // 401
}
}
```

### Platform Support

| Platform | Support | Notes |
Expand Down
26 changes: 24 additions & 2 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,29 @@ Only one of these was exported to consumers:

**✅ Action Required:** rename the import if you annotated anything with `IMfaClient` — typically a variable holding `auth0.mfa` or the `mfa` object from `useAuth0()`. This is a type-only change; runtime behaviour is identical.

The rest (`AuthenticationProvider`, `CredentialsManager`, `MyAccountClient`, `PasswordlessClient`, `WebAuthProvider`, `NativeBridge`) were never exported from the package entry point, so nothing to do there.
`AuthenticationProvider`, `CredentialsManager`, `MyAccountClient`, `PasswordlessClient`, and `WebAuthProvider` are now exported under their plain names too _(see [§11](#11-public-api-surface-freeze--my-account-error-normalization-))_; only `NativeBridge` stays internal-only.

### 11. Public API surface freeze & My Account error normalization ✅

The public surface was audited before v6 GA: previously-unreachable types were exported, dead internal types were un-exported, and `MyAccountError` was brought in line with the rest of the error taxonomy.

#### `MyAccountError.type` is now a normalized code

`MyAccountError.type` used to be the raw [RFC 7807](https://datatracker.ietf.org/doc/html/rfc7807) type URI reported by the My Account API (e.g. `https://auth0.com/api-errors/A0E-401-0001`). It is now a normalized `MyAccountErrorCodes` value, consistent with every other error class in the SDK. The original URI is preserved on a new `typeUri` property.

**⚠️ Action Required:** if you compared `MyAccountError.type` against a raw URI string, switch to comparing against `MyAccountErrorCodes` and read `typeUri` for the raw value.
Comment thread
NandanPrabhu marked this conversation as resolved.

```diff
- if (error.type === 'https://auth0.com/api-errors/A0E-401-0001') { ... }
+ if (error.type === MyAccountErrorCodes.UNAUTHORIZED) { ... }
+ console.log(error.typeUri); // "https://auth0.com/api-errors/A0E-401-0001" — raw, log this
```

#### Four internal types are no longer exported

`NativeAuth0Options`, `WebAuth0Options`, `NativeCredentialsResponse`, and `SSOCredentialsResponse` were internal adapter-construction/wire shapes that were reachable from `react-native-auth0` by accident. They are not part of the supported API and have been removed from the package's exports.

**✅ Action Required:** if you imported any of these four types directly, inline the shape you need or open an issue describing your use case — none of them were meant to be public.

### Recommended Reading

Expand Down Expand Up @@ -365,7 +387,7 @@ With the introduction of **React Native Web support**, some methods are only ava

On React Native Web, the `authorize()` method now triggers a **full-page redirect** to Auth0. As a result, the promise returned by `authorize()` will **not resolve** in the browser. Your application must be structured to handle the user state upon reloading after the redirect.

**✅ Action Required:** Review the new **[FAQ entry](#faq-authorize-web)** for guidance on how to correctly handle the post-login flow on the web. The `Auth0Provider` and `useAuth0` hook are designed to manage this flow automatically.
**✅ Action Required:** Review the new **[FAQ entry](FAQ.md#9-why-doesnt-await-authorize-work-on-the-web-how-do-i-handle-login)** for guidance on how to correctly handle the post-login flow on the web. The `Auth0Provider` and `useAuth0` hook are designed to manage this flow automatically.

### Change #5: Hook Methods Now Throw Error

Expand Down
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,65 @@ The options for configuring the display of local authentication prompt, authenti

> :warning: You need a real device to test Local Authentication for iOS. Local Authentication is not available in simulators.

### Error taxonomy

Every error the SDK throws extends `AuthError`. The six normalized subclasses below carry a
**normalized, platform-agnostic** `type` — switch on `type`, not `code`, for these and your error
handling behaves identically on iOS, Android, and web. Flows that throw a plain `AuthError`
instead — for example [Custom Token Exchange](EXAMPLES.md#custom-token-exchange-rfc-8693), which surfaces
the raw OAuth error from the token endpoint — don't get a normalized `type`; there, `code` is the
correct (and only) thing to switch on.

| Property | Use it for |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | **Control flow** for the six normalized subclasses. A normalized code, stable across platforms. Compare against the `…ErrorCodes` constants. |
| `code` | **Diagnostics** for the normalized subclasses (raw code from the underlying platform SDK or wire response, varies by platform); **control flow** for plain `AuthError` flows that have no normalized `type`. |
| `message` | Human-readable description. Not stable — do not parse it. |
| `status` | HTTP status, when the failure came from an HTTP response (`0` otherwise). |

Each of the six normalized classes ships a companion constants object and a matching TypeScript
union. Handle every value explicitly (no `default` branch) and TypeScript enforces exhaustiveness
at compile time — a `switch` missing a case fails to compile. The example below adds a `default`
fallback for brevity, so it does not get that compile-time guarantee:

| Error class | Constants | Type union | Thrown by |
| ------------------------- | ------------------------------ | ---------------------------------- | ----------------------------------------------- |
| `WebAuthError` | `WebAuthErrorCodes` | `WebAuthErrorCode` | `webAuth.authorize()`, `webAuth.clearSession()` |
| `CredentialsManagerError` | `CredentialsManagerErrorCodes` | `CredentialsManagerErrorCode` | `credentialsManager.*` |
| `MfaError` | `MfaErrorCodes` | `MfaErrorCode` | `mfa.*` |
| `PasskeyError` | `PasskeyErrorCodes` | `PasskeyErrorCode` | passkey signup/login and passkey enrollment |
| `MyAccountError` | `MyAccountErrorCodes` | `MyAccountErrorCode` | `myAccount.*` |
| `DPoPError` | `DPoPErrorCodes` | `DPoPErrorCode` | `getDPoPHeaders()` and DPoP key handling |
| `TimeoutError` | — | `type` is always `'TIMEOUT_ERROR'` | HTTP requests exceeding `timeout` |

```typescript
import { WebAuthError, WebAuthErrorCodes } from 'react-native-auth0';
import type { WebAuthErrorCode } from 'react-native-auth0';

function describe(type: WebAuthErrorCode): string {
switch (type) {
case WebAuthErrorCodes.USER_CANCELLED:
return 'Cancelled';
case WebAuthErrorCodes.NETWORK_ERROR:
return 'Offline';
default:
return 'Login failed';
}
}
```

`Auth0ErrorCode` is the union of all of the above. Prefer the specific union when handling one error
class — it keeps `switch` statements exhaustive and rejects codes that cannot occur there. Reach for
`Auth0ErrorCode` only in generic code such as logging or telemetry.

> **Stability.** These constants, their unions, and the `type` values they contain are the public
> error contract. Values will not be removed or renamed outside a major version.

`MyAccountError` is the one class with an extra property: the My Account API reports failures as
[RFC 7807](https://datatracker.ietf.org/doc/html/rfc7807) type URIs, so `type` holds the normalized
code while `typeUri` preserves the original URI (e.g. `https://auth0.com/api-errors/A0E-401-0001`) for
logging and support tickets.
Comment thread
NandanPrabhu marked this conversation as resolved.

### Credentials Manager errors

The Credentials Manager will only throw `CredentialsManagerError` exceptions. You can find more information in the details property of the exception.
Expand Down
4 changes: 2 additions & 2 deletions src/Auth0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { MfaClient } from './core/interfaces/MfaClient';
import { Auth0ClientFactory } from './factory/Auth0ClientFactory';
import type {
Auth0Options,
DPoPHeadersParams,
DPoPHeadersParameters,
CustomTokenExchangeParameters,
PasskeySignupChallengeParameters,
PasskeyLoginChallengeParameters,
Expand Down Expand Up @@ -122,7 +122,7 @@ class Auth0 {
* }
* ```
*/
getDPoPHeaders(params: DPoPHeadersParams) {
getDPoPHeaders(params: DPoPHeadersParameters) {
return this.client.getDPoPHeaders(params);
}

Expand Down
141 changes: 141 additions & 0 deletions src/__tests__/fixtures/frozenPublicApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/**
* The frozen public API surface of `src/index.ts`.
*
* This list is the stable public contract for v6. Adding an entry is a minor
* change; **removing or renaming an entry is a breaking change** and must be
* treated as such (major version, deprecation cycle, changelog entry).
*
* If a test fails against this list, do not "fix" it by regenerating the
* list. Confirm the change to the surface is intentional and versioned
* appropriately first.
*/
export const FROZEN_PUBLIC_API = [
'ApiCredentials',
'Auth0',
'Auth0Client',
'Auth0ContextInterface',
'Auth0ErrorCode',
'Auth0Options',
'Auth0Provider',
'AuthError',
'AuthState',
'AuthenticationMethod',
'AuthenticationMethodType',
'AuthenticationMethodTypes',
'AuthenticationProvider',
'AuthorizeUrlParameters',
'BiometricPolicy',
'ClearSessionParameters',
'ConfirmOTPEnrollmentParameters',
'ConfirmPushNotificationEnrollmentParameters',
'ConfirmRecoveryCodeEnrollmentParameters',
'CreateUserParameters',
'Credentials',
'CredentialsManager',
'CredentialsManagerError',
'CredentialsManagerErrorCode',
'CredentialsManagerErrorCodes',
'CustomTokenExchangeParameters',
'DPoPError',
'DPoPErrorCode',
'DPoPErrorCodes',
'DPoPHeadersParameters',
'DPoPHeadersParams',
'DeleteAuthenticationMethodByIdParameters',
'DeliveryMethod',
'EnrollEmailParameters',
'EnrollPasskeyParameters',
'EnrollPhoneParameters',
'EnrollPushNotificationParameters',
'EnrollRecoveryCodeParameters',
'EnrollTOTPParameters',
'EnrollmentChallenge',
'ExchangeNativeSocialParameters',
'ExchangeParameters',
'Factor',
'GetAuthenticationMethodByIdParameters',
'GetAuthenticationMethodsParameters',
'GetFactorsParameters',
'GetTokenByPasskeyParameters',
'LocalAuthenticationLevel',
'LocalAuthenticationOptions',
'LocalAuthenticationStrategy',
'LoginEmailParameters',
'LoginSmsParameters',
'LogoutUrlParameters',
'MfaAuthenticator',
'MfaChallengeResult',
'MfaChallengeWithAuthenticatorParameters',
'MfaClient',
'MfaEnrollEmailParameters',
'MfaEnrollOtpParameters',
'MfaEnrollParameters',
'MfaEnrollPushParameters',
'MfaEnrollSmsParameters',
'MfaEnrollVoiceParameters',
'MfaEnrollmentChallenge',
'MfaError',
'MfaErrorCode',
'MfaErrorCodes',
'MfaFactor',
'MfaFactorType',
'MfaGetAuthenticatorsParameters',
'MfaOobEnrollmentChallenge',
'MfaPushEnrollmentChallenge',
'MfaRecoveryCodeEnrollmentChallenge',
'MfaRequiredErrorPayload',
'MfaRequirements',
'MfaTotpEnrollmentChallenge',
'MfaVerifyOobParameters',
'MfaVerifyOtpParameters',
'MfaVerifyParameters',
'MfaVerifyRecoveryCodeParameters',
'MyAccountClient',
'MyAccountError',
'MyAccountErrorCode',
'MyAccountErrorCodes',
'NativeAuthorizeOptions',
'NativeClearSessionOptions',
'PasskeyAuthenticationMethod',
'PasskeyChallengeResponse',
'PasskeyEnrollmentChallengeParameters',
'PasskeyEnrollmentChallengeResponse',
'PasskeyError',
'PasskeyErrorCode',
'PasskeyErrorCodes',
'PasskeyLoginChallengeParameters',
'PasskeySignupChallengeParameters',
'PasswordRealmParameters',
'PasswordlessChallenge',
'PasswordlessChallengeEmailParameters',
'PasswordlessChallengePhoneParameters',
'PasswordlessClient',
'PasswordlessDeliveryMethod',
'PasswordlessEmailParameters',
'PasswordlessLoginOtpParameters',
'PasswordlessSmsParameters',
'PreferredAuthenticationMethods',
'RecoveryCodeEnrollmentChallenge',
'RefreshTokenParameters',
'ResetPasswordParameters',
'RevokeOptions',
'SSOExchangeParameters',
'SafariViewControllerPresentationStyle',
'SessionTransferCredentials',
'TOTPEnrollmentChallenge',
'TimeoutError',
'TokenType',
'UpdateAuthenticationMethodByIdParameters',
'User',
'UserInfoParameters',
'WebAuthError',
'WebAuthErrorCode',
'WebAuthErrorCodes',
'WebAuthProvider',
'WebAuthorizeOptions',
'WebAuthorizeParameters',
'WebClearSessionOptions',
'default',
'parseIdToken',
'useAuth0',
];
Loading
Loading