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
12 changes: 1 addition & 11 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,7 @@ On React Native Web, the `authorize()` method now triggers a **full-page redirec

**✅ 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.

### Change #5: New Peer Dependency for Web Support

To support the web platform, the library now has an **optional peer dependency** on `@auth0/auth0-spa-js`.

**✅ Action Required:** If you are using `react-native-auth0` in a React Native Web project, you **must** install this package. Native-only projects can ignore this.

```bash
npm install @auth0/auth0-spa-js
```

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

Previously, all hook-related methods such as `getCredentials()`, `saveCredentials()`, etc., did not throw error directly. Instead, any issues were silently handled and surfaced via the error property in `useAuth0()`:

Expand Down
14 changes: 1 addition & 13 deletions REACT_NATIVE_WEB_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,7 @@ If you want to use React Native Web with React Native Auth0, follow these steps:
Follow the official React Native Web installation guide:
**https://necolas.github.io/react-native-web/docs/setup/**

### 2. Install Auth0 SPA JS (Required for Web)

React Native Auth0 requires `@auth0/auth0-spa-js` for web platform support:

```bash
# Using npm
npm install @auth0/auth0-spa-js

# Using yarn
yarn add @auth0/auth0-spa-js
```

### 3. Use React Native Auth0
### 2. Use React Native Auth0

Once React Native Web and Auth0 SPA JS are installed, you can use React Native Auth0 exactly as you would in a native React Native app. The library will automatically detect the web platform and use the appropriate implementation.

Expand Down
1 change: 0 additions & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"build:ios": "react-native build-ios --mode Debug"
},
"dependencies": {
"@auth0/auth0-spa-js": "^2.2.0",
"@react-navigation/bottom-tabs": "^7.4.2",
"@react-navigation/native": "^7.1.13",
"@react-navigation/stack": "^7.3.6",
Expand Down
6 changes: 1 addition & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,15 @@
"registry": "https://registry.npmjs.org/"
},
"peerDependencies": {
"@auth0/auth0-spa-js": ">=2.2.0",
"react": ">=19.0.0",
"react-native": ">=0.78.0"
},
"peerDependenciesMeta": {
"@auth0/auth0-spa-js": {
"optional": true
},
"expo": {
"optional": true
}
},
"devDependencies": {
"@auth0/auth0-spa-js": "^2.2.0",
"@commitlint/config-conventional": "^17.0.2",
"@eslint/compat": "^1.2.7",
"@eslint/eslintrc": "^3.3.0",
Expand Down Expand Up @@ -140,6 +135,7 @@
"typescript": "5.2.2"
},
"dependencies": {
"@auth0/auth0-spa-js": "2.3.0",
"base-64": "^1.0.0",
"jwt-decode": "^4.0.0",
"url": "^0.11.4"
Expand Down
6 changes: 6 additions & 0 deletions src/core/interfaces/IWebAuthProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {
Credentials,
WebAuthorizeParameters,
ClearSessionParameters,
User,
} from '../../types';

import type {
Expand Down Expand Up @@ -53,6 +54,11 @@ export interface IWebAuthProvider {
options?: NativeClearSessionOptions | WebClearSessionOptions
): Promise<void>;

/**
* Checks the user's session and updates the local state if the session is still valid.
*/
checkWebSession(): Promise<User | null>;

/**
* Cancels an ongoing web authentication transaction.
*
Expand Down
3 changes: 3 additions & 0 deletions src/hooks/Auth0Provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ export const Auth0Provider = ({
// If the redirect fails, dispatch an error.
dispatch({ type: 'ERROR', error: e as AuthError });
}
} else if (typeof window !== 'undefined') {
const user = await client.webAuth.checkWebSession();
dispatch({ type: 'INITIALIZED', user });
}
try {
const credentials = await client.credentialsManager.getCredentials();
Expand Down
15 changes: 13 additions & 2 deletions src/hooks/__tests__/Auth0Provider.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ const createMockClient = () => {
clearSession: jest.fn().mockResolvedValue(undefined),
cancelWebAuth: jest.fn().mockResolvedValue(undefined),
handleRedirectCallback: jest.fn().mockResolvedValue(undefined),
checkWebSession: jest.fn().mockResolvedValue(null),
},
credentialsManager: {
hasValidCredentials: jest.fn().mockResolvedValue(false),
Expand Down Expand Up @@ -194,11 +195,20 @@ describe('Auth0Provider', () => {
});

it('should render a loading state initially', async () => {
// Make getCredentials return a promise that we can control
// Make both checkWebSession and getCredentials return promises that we can control
let resolveCheckSession: (value: any) => void;
let resolveCredentials: (value: any) => void;

const checkSessionPromise = new Promise((resolve) => {
resolveCheckSession = resolve;
});
const credentialsPromise = new Promise((resolve) => {
resolveCredentials = resolve;
});

mockClientInstance.webAuth.checkWebSession.mockReturnValue(
checkSessionPromise
);
mockClientInstance.credentialsManager.getCredentials.mockReturnValue(
credentialsPromise
);
Expand All @@ -214,8 +224,9 @@ describe('Auth0Provider', () => {
// Should show loading state initially
expect(screen.getByTestId('loading')).toBeDefined();

// Resolve the credentials promise
// Resolve the promises
await act(async () => {
resolveCheckSession!(null);
resolveCredentials!(null);
});

Expand Down
5 changes: 5 additions & 0 deletions src/platforms/native/adapters/NativeWebAuthProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
Credentials,
WebAuthorizeParameters,
ClearSessionParameters,
User,
} from '../../../types';
import type {
NativeAuthorizeOptions,
Expand All @@ -28,6 +29,10 @@ export class NativeWebAuthProvider implements IWebAuthProvider {
throw new AuthError('NotImplemented', webAuthNotSupported);
}

async checkWebSession(): Promise<User | null> {
throw new AuthError('NotImplemented', webAuthNotSupported);
}

async authorize(
parameters: WebAuthorizeParameters = {},
options: NativeAuthorizeOptions = {}
Expand Down
2 changes: 1 addition & 1 deletion src/platforms/web/adapters/WebAuth0Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class WebAuth0Client implements IAuth0Client {
domain: options.domain,
clientId: options.clientId,
cacheLocation: options.cacheLocation ?? 'memory',
useRefreshTokens: options.useRefreshTokens ?? true,
useRefreshTokens: options.useRefreshTokens ?? false,
authorizationParams: {
redirect_uri:
typeof window !== 'undefined' ? window.location.origin : '',
Expand Down
42 changes: 41 additions & 1 deletion src/platforms/web/adapters/WebWebAuthProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,46 @@ import type {
Credentials,
WebAuthorizeParameters,
ClearSessionParameters,
User,
} from '../../../types';
import { AuthError, WebAuthError } from '../../../core/models';
import { finalizeScope } from '../../../core/utils';
import type { Auth0Client, PopupCancelledError } from '@auth0/auth0-spa-js';
import type {
Auth0Client,
PopupCancelledError,
User as SpaJSUser,
} from '@auth0/auth0-spa-js';

export class WebWebAuthProvider implements IWebAuthProvider {
constructor(private client: Auth0Client) {}

// private method to convert a SpaJSUser to a User
private convertUser(user: SpaJSUser | undefined): User | null {
if (!user || !user.sub) return null;
return {
sub: user.sub,
name: user.name,
givenName: user.given_name,
familyName: user.family_name,
middleName: user.middle_name,
nickname: user.nickname,
preferredUsername: user.preferred_username,
profile: user.profile,
picture: user.picture,
website: user.website,
email: user.email,
emailVerified: user.email_verified,
gender: user.gender,
birthdate: user.birthdate,
zoneinfo: user.zoneinfo,
locale: user.locale,
phoneNumber: user.phone_number,
phoneNumberVerified: user.phone_number_verified,
address: user.address,
updatedAt: user.updated_at,
};
}

async authorize(
parameters: WebAuthorizeParameters = {}
): Promise<Credentials> {
Expand Down Expand Up @@ -79,6 +111,14 @@ export class WebWebAuthProvider implements IWebAuthProvider {
}
}

async checkWebSession(): Promise<User | null> {
await this.client.checkSession();
const spaUser: SpaJSUser | undefined = await this.client.getUser();
// convert this to a User
const user = this.convertUser(spaUser);
return user;
}

async cancelWebAuth(): Promise<void> {
// Web-based flows cannot be programmatically cancelled. This is a no-op.
return Promise.resolve();
Expand Down
8 changes: 2 additions & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ __metadata:
languageName: node
linkType: hard

"@auth0/auth0-spa-js@npm:^2.2.0":
"@auth0/auth0-spa-js@npm:2.3.0":
version: 2.3.0
resolution: "@auth0/auth0-spa-js@npm:2.3.0"
checksum: 6f9c84cd300ba7215a7b894690418010cd743ada5970083f0753f189a39208b4262a9828f28c8f932ca33c1dfe6e587b057a6c956e40632aea9dbed5099220fd
Expand Down Expand Up @@ -5184,7 +5184,6 @@ __metadata:
version: 0.0.0-use.local
resolution: "Auth0Example@workspace:example"
dependencies:
"@auth0/auth0-spa-js": ^2.2.0
"@babel/core": ^7.25.2
"@babel/preset-env": ^7.25.3
"@babel/runtime": ^7.25.0
Expand Down Expand Up @@ -14604,7 +14603,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "react-native-auth0@workspace:."
dependencies:
"@auth0/auth0-spa-js": ^2.2.0
"@auth0/auth0-spa-js": 2.3.0
"@commitlint/config-conventional": ^17.0.2
"@eslint/compat": ^1.2.7
"@eslint/eslintrc": ^3.3.0
Expand Down Expand Up @@ -14660,12 +14659,9 @@ __metadata:
typescript: 5.2.2
url: ^0.11.4
peerDependencies:
"@auth0/auth0-spa-js": ">=2.2.0"
react: ">=19.0.0"
react-native: ">=0.78.0"
peerDependenciesMeta:
"@auth0/auth0-spa-js":
optional: true
expo:
optional: true
languageName: unknown
Expand Down