Skip to content

Commit f9523bb

Browse files
docs: Add FAQ section for web opaque token issue (#1366)
1 parent e169247 commit f9523bb

File tree

1 file changed

+105
-8
lines changed

1 file changed

+105
-8
lines changed

FAQ.md

Lines changed: 105 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
9. [Why doesn't `await authorize()` work on the web? How do I handle login?](#9-why-doesnt-await-authorize-work-on-the-web-how-do-i-handle-login)
1212
10. [Why do my users get logged out frequently? How do I keep them logged in?](#10-why-do-my-users-get-logged-out-frequently-how-do-i-keep-them-logged-in)
1313
11. [How can I prompt users to the login page versus signup page?](#11-how-can-i-prompt-users-to-the-login-page-versus-signup-page)
14-
12. [What is DPoP and should I enable it?](#12-what-is-dpop-and-should-i-enable-it)
15-
13. [How do I migrate existing users to DPoP?](#13-how-do-i-migrate-existing-users-to-dpop)
16-
14. [How do I know if my tokens are using DPoP?](#14-how-do-i-know-if-my-tokens-are-using-dpop)
17-
15. [What happens if I disable DPoP after enabling it?](#15-what-happens-if-i-disable-dpop-after-enabling-it)
14+
12. [Why does `getCredentials()` return an opaque access token on web instead of a JWT?](#12-why-does-getcredentials-return-an-opaque-access-token-on-web-instead-of-a-jwt)
15+
13. [What is DPoP and should I enable it?](#13-what-is-dpop-and-should-i-enable-it)
16+
14. [How do I migrate existing users to DPoP?](#14-how-do-i-migrate-existing-users-to-dpop)
17+
15. [How do I know if my tokens are using DPoP?](#15-how-do-i-know-if-my-tokens-are-using-dpop)
18+
16. [What happens if I disable DPoP after enabling it?](#16-what-happens-if-i-disable-dpop-after-enabling-it)
1819

1920
## 1. How can I have separate Auth0 domains for each environment on Android?
2021

@@ -366,7 +367,103 @@ const signup = async () => {
366367
}
367368
```
368369

369-
## 12. What is DPoP and should I enable it?
370+
## 12. Why does `getCredentials()` return an opaque access token on web instead of a JWT?
371+
372+
When calling `getCredentials()` on the **web platform**, you may receive an opaque access token (a token with ".." in the middle that doesn't parse as a JWT) instead of a JWT, even though you specified an `audience` during the initial `authorize()` call.
373+
374+
**Root Cause:**
375+
376+
On web, credentials are stored in browser storage (sessionStorage/localStorage). When you call `getCredentials()` without specifying the `audience` parameter, the SDK returns the default opaque token instead of the API-specific JWT access token.
377+
378+
**Solution:**
379+
380+
You must pass the `audience` parameter to **both** `authorize()` and `getCredentials()`:
381+
382+
```javascript
383+
import { useAuth0 } from 'react-native-auth0';
384+
385+
const AUDIENCE = 'https://your-api.example.com';
386+
387+
function App() {
388+
const { authorize, getCredentials } = useAuth0();
389+
390+
// ✅ CORRECT: Specify audience during login
391+
const onLogin = async () => {
392+
await authorize({
393+
audience: AUDIENCE,
394+
scope: 'openid profile email offline_access'
395+
});
396+
};
397+
398+
// ✅ CORRECT: Specify audience when retrieving credentials
399+
const onGetCredentials = async () => {
400+
const credentials = await getCredentials(
401+
'openid profile email offline_access',
402+
0,
403+
{ audience: AUDIENCE } // ← Must include audience here!
404+
);
405+
console.log('JWT Access Token:', credentials.accessToken);
406+
};
407+
408+
return (
409+
<View>
410+
<Button onPress={onLogin} title="Log In" />
411+
<Button onPress={onGetCredentials} title="Get Credentials" />
412+
</View>
413+
);
414+
}
415+
```
416+
417+
**Why this happens:**
418+
419+
- **During `authorize()`**: The `audience` tells Auth0 to issue a JWT for your API
420+
- **During `getCredentials()`**: On web, the audience must be re-specified to retrieve the correct token type
421+
- **Platform difference**: Native platforms (iOS/Android) store credentials with all parameters and retrieve as-is, but web may need to refresh tokens
422+
423+
**Best Practice:**
424+
425+
Define your auth configuration once and reuse it:
426+
427+
```javascript
428+
const AUTH_CONFIG = {
429+
audience: 'https://your-api.example.com',
430+
scope: 'openid profile email offline_access'
431+
};
432+
433+
// Login
434+
await authorize(AUTH_CONFIG);
435+
436+
// Get credentials later (include audience in parameters)
437+
await getCredentials(AUTH_CONFIG.scope, 0, {
438+
audience: AUTH_CONFIG.audience
439+
});
440+
```
441+
442+
**Using the class-based API:**
443+
444+
```javascript
445+
const auth0 = new Auth0({
446+
domain: 'YOUR_DOMAIN',
447+
clientId: 'YOUR_CLIENT_ID'
448+
});
449+
450+
// Login
451+
await auth0.webAuth.authorize({
452+
audience: 'https://your-api.example.com',
453+
scope: 'openid profile email offline_access'
454+
});
455+
456+
// Get credentials (must include audience)
457+
const credentials = await auth0.credentialsManager.getCredentials(
458+
'openid profile email offline_access',
459+
0,
460+
{ audience: 'https://your-api.example.com' }
461+
);
462+
```
463+
464+
> **Note**: This behavior is specific to the web platform. On iOS and Android, the `audience` parameter is automatically preserved from the initial `authorize()` call.
465+
466+
## 13. What is DPoP and should I enable it?
370467

371468
**DPoP** (Demonstrating Proof-of-Possession) is an OAuth 2.0 security extension ([RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) that cryptographically binds access and refresh tokens to a specific device using public/private key pairs. This means that even if an access token is stolen (e.g., through XSS or network interception), it cannot be used from a different device because the attacker won't have the private key needed to generate valid DPoP proofs.
372469

@@ -397,7 +494,7 @@ const auth0 = new Auth0({
397494
});
398495
```
399496

400-
## 13. How do I migrate existing users to DPoP?
497+
## 14. How do I migrate existing users to DPoP?
401498

402499
When you enable DPoP in your app, existing users will still have Bearer tokens from their previous sessions. DPoP only applies to **new sessions** created after it's enabled. Here's how to handle the migration:
403500

@@ -589,7 +686,7 @@ if (credentials.tokenType !== 'DPoP') {
589686
3. **Communicate with users**: Explain why re-authentication is necessary
590687
4. **Handle errors gracefully**: Network issues or user cancellation should be handled appropriately
591688

592-
## 14. How do I know if my tokens are using DPoP?
689+
## 15. How do I know if my tokens are using DPoP?
593690

594691
You can check the `tokenType` property of the credentials returned by `getCredentials()`:
595692

@@ -670,7 +767,7 @@ if (headers.DPoP) {
670767
}
671768
```
672769

673-
## 15. What happens if I disable DPoP after enabling it?
770+
## 16. What happens if I disable DPoP after enabling it?
674771

675772
If you disable DPoP after enabling it (by setting `useDPoP: false`), here's what happens:
676773

0 commit comments

Comments
 (0)