556 lines
11 KiB
Markdown
556 lines
11 KiB
Markdown
---
|
|
description: Troubleshooting guide for common Auth0 OAuth issues
|
|
---
|
|
|
|
# OAuth Troubleshooting Guide
|
|
|
|
Solutions for common Auth0 OAuth problems.
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
/oauth-troubleshoot [issue]
|
|
```
|
|
|
|
Common issues:
|
|
- `callback-mismatch` - Callback URL doesn't match
|
|
- `token-expired` - Access token expired
|
|
- `cors-error` - CORS error when calling API
|
|
- `silent-auth` - Silent authentication not working
|
|
- `scope-error` - Scope not in token
|
|
- `mfa-required` - MFA login failing
|
|
- `logout-not-clearing` - Session not cleared on logout
|
|
- `social-login-fails` - Social login buttons not working
|
|
|
|
---
|
|
|
|
## Issue: Callback URL Mismatch
|
|
|
|
**Error**: "Unable to process redirect request" or "Invalid redirect_uri"
|
|
|
|
**Cause**: Redirect URL in Auth0 doesn't match your app's callback URL
|
|
|
|
**Solution**:
|
|
|
|
1. **Check your app's callback URL**:
|
|
```typescript
|
|
// React
|
|
<Auth0Provider
|
|
authorizationParams={{
|
|
redirect_uri: window.location.origin + '/callback'
|
|
}}
|
|
>
|
|
|
|
// Next.js
|
|
AUTH0_CALLBACK_URL=http://localhost:3000/api/auth/callback
|
|
```
|
|
|
|
2. **Update Auth0 Dashboard**:
|
|
```
|
|
Auth0 Dashboard → Applications → Your App → Settings
|
|
|
|
Allowed Callback URLs:
|
|
http://localhost:3000/callback
|
|
OR
|
|
http://localhost:3000/api/auth/callback
|
|
|
|
(Must EXACTLY match your app!)
|
|
```
|
|
|
|
3. **Common mistakes**:
|
|
- Including query params: `/callback?foo=bar` (wrong!)
|
|
- Using `http://` vs `https://`
|
|
- Port number mismatch: `:3000` vs `:3001`
|
|
- Missing trailing slash inconsistency
|
|
|
|
4. **Test**:
|
|
```bash
|
|
# Verify URL matches exactly
|
|
echo "App uses: window.location.origin + '/callback'"
|
|
echo "Current: $CALLBACK_URL"
|
|
```
|
|
|
|
---
|
|
|
|
## Issue: Access Token Expired
|
|
|
|
**Error**: API returns 401 Unauthorized
|
|
|
|
**Cause**: Access token expired (default 10 minutes)
|
|
|
|
**Solution**:
|
|
|
|
1. **Use Auth0 SDK (auto-refresh)**:
|
|
```typescript
|
|
// React
|
|
const { getAccessTokenSilently } = useAuth0()
|
|
const token = await getAccessTokenSilently() // Auto-refreshes if expired
|
|
|
|
// Next.js
|
|
const session = await getSession() // SDK handles refresh
|
|
const token = session.accessToken
|
|
```
|
|
|
|
2. **Verify token expiration**:
|
|
```
|
|
Auth0 Dashboard → Applications → Settings → Token Expiration
|
|
|
|
Should be: 600 (10 minutes)
|
|
Refresh Token Expiration: 2592000 (30 days)
|
|
Refresh Token Rotation: Enabled ✅
|
|
```
|
|
|
|
3. **Manual refresh (if needed)**:
|
|
```typescript
|
|
async function getRefreshToken() {
|
|
const response = await fetch(`https://${AUTH0_DOMAIN}/oauth/token`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
client_id: CLIENT_ID,
|
|
client_secret: CLIENT_SECRET,
|
|
refresh_token: refreshToken,
|
|
grant_type: 'refresh_token'
|
|
})
|
|
})
|
|
|
|
const { access_token } = await response.json()
|
|
return access_token
|
|
}
|
|
```
|
|
|
|
4. **Test**:
|
|
```bash
|
|
# Decode token to check expiration
|
|
# Visit https://jwt.io and paste your access token
|
|
# Look at "exp" claim (Unix timestamp)
|
|
```
|
|
|
|
---
|
|
|
|
## Issue: CORS Error Calling API
|
|
|
|
**Error**: "Access to XMLHttpRequest blocked by CORS policy"
|
|
|
|
**Cause 1**: Backend CORS not configured for frontend origin
|
|
|
|
**Solution 1** (Express/Node.js):
|
|
|
|
```typescript
|
|
import cors from 'cors'
|
|
|
|
// Allow specific origins
|
|
app.use(cors({
|
|
origin: ['http://localhost:3000', 'https://myapp.com'],
|
|
credentials: true // Important for cookies
|
|
}))
|
|
|
|
// Or check origin dynamically
|
|
app.use(cors({
|
|
origin: function(origin, callback) {
|
|
if (ALLOWED_ORIGINS.includes(origin)) {
|
|
callback(null, true)
|
|
} else {
|
|
callback(new Error('Not allowed by CORS'))
|
|
}
|
|
}
|
|
}))
|
|
```
|
|
|
|
**Cause 2**: Auth0 domain not in allowed origins
|
|
|
|
**Solution 2** (Auth0 Dashboard):
|
|
|
|
```
|
|
Auth0 Dashboard → Applications → Settings
|
|
|
|
Allowed Web Origins:
|
|
http://localhost:3000 (for development)
|
|
https://myapp.com (for production)
|
|
|
|
(Allows Auth0 calls from these origins)
|
|
```
|
|
|
|
**Cause 3**: Missing credentials in request
|
|
|
|
**Solution 3**:
|
|
|
|
```typescript
|
|
// Make sure credentials are included
|
|
fetch('/api/items', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
credentials: 'include' // Important for cookies
|
|
})
|
|
```
|
|
|
|
**Test**:
|
|
```bash
|
|
# Test CORS
|
|
curl -H "Origin: http://localhost:3000" \
|
|
-H "Access-Control-Request-Method: GET" \
|
|
http://localhost:3001/api/items
|
|
|
|
# Should include Access-Control-Allow-Origin header
|
|
```
|
|
|
|
---
|
|
|
|
## Issue: Silent Authentication Fails
|
|
|
|
**Error**: Silent auth not working, user must login again
|
|
|
|
**Cause 1**: Cache location wrong
|
|
|
|
**Solution 1**:
|
|
|
|
```typescript
|
|
// WRONG ❌
|
|
new Auth0Provider({
|
|
cacheLocation: 'localStorage' // Can't do silent auth in localStorage
|
|
})
|
|
|
|
// RIGHT ✅
|
|
new Auth0Provider({
|
|
cacheLocation: 'memory' // For SPAs
|
|
})
|
|
```
|
|
|
|
**Cause 2**: Refresh token not rotated
|
|
|
|
**Solution 2**:
|
|
|
|
```
|
|
Auth0 Dashboard → Applications → Settings
|
|
|
|
Refresh Token Rotation: Enabled ✅
|
|
Refresh Token Expiration Absolute: 2592000 (30 days)
|
|
```
|
|
|
|
**Cause 3**: Third-party cookies blocked
|
|
|
|
**Solution 3** (for local development):
|
|
|
|
```
|
|
Browser → Settings → Privacy → Cookies
|
|
Allow cookies (at least for localhost)
|
|
```
|
|
|
|
**Test**:
|
|
```typescript
|
|
// Test silent auth
|
|
async function testSilentAuth() {
|
|
const token = await getAccessTokenSilently()
|
|
console.log('Silent auth worked:', token)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Issue: Scope Not in Token
|
|
|
|
**Error**: Token doesn't include required scope
|
|
|
|
**Cause 1**: Scope not requested during login
|
|
|
|
**Solution 1**:
|
|
|
|
```typescript
|
|
// WRONG ❌
|
|
Auth0Provider({
|
|
// No scope specified
|
|
})
|
|
|
|
// RIGHT ✅
|
|
Auth0Provider({
|
|
authorizationParams: {
|
|
scope: 'openid profile email read:items write:items'
|
|
}
|
|
})
|
|
```
|
|
|
|
**Cause 2**: Scope not configured in Auth0
|
|
|
|
**Solution 2**:
|
|
|
|
```
|
|
Auth0 Dashboard → APIs → Your API → Scopes
|
|
|
|
Add:
|
|
+ read:items (Read access)
|
|
+ write:items (Write access)
|
|
+ delete:items (Delete access)
|
|
```
|
|
|
|
**Cause 3**: User not consented to scope
|
|
|
|
**Solution 3**:
|
|
|
|
```
|
|
Auth0 Dashboard → Connections → Settings
|
|
|
|
Require consent: Off (for internal apps)
|
|
Or allow user to accept scopes during login
|
|
```
|
|
|
|
**Test**:
|
|
```javascript
|
|
// Decode token to check scopes
|
|
const decoded = jwt_decode(token)
|
|
console.log('Scopes:', decoded.scope)
|
|
```
|
|
|
|
---
|
|
|
|
## Issue: MFA Login Failing
|
|
|
|
**Error**: "MFA is required" but MFA setup failed
|
|
|
|
**Cause 1**: MFA not configured
|
|
|
|
**Solution 1**:
|
|
|
|
```
|
|
Auth0 Dashboard → Connections → Authenticators
|
|
|
|
Enable:
|
|
✓ Google Authenticator
|
|
✓ SMS (optional)
|
|
✓ Email OTP
|
|
|
|
Require MFA: Off/On (depending on policy)
|
|
```
|
|
|
|
**Cause 2**: User hasn't set up MFA method
|
|
|
|
**Solution 2**:
|
|
|
|
```
|
|
Users must register MFA before login:
|
|
1. First login (without MFA)
|
|
2. Prompted to set up authenticator
|
|
3. Scan QR code or enter backup codes
|
|
4. Subsequent logins require MFA
|
|
```
|
|
|
|
**Cause 3**: Recovery codes not working
|
|
|
|
**Solution 3**:
|
|
|
|
```
|
|
If user loses authenticator:
|
|
1. User clicks "Can't scan?"
|
|
2. Gets backup codes
|
|
3. Use one code to login
|
|
4. Setup new authenticator
|
|
|
|
Or admin can reset MFA:
|
|
Auth0 Dashboard → Users → Select user → Actions → Reset authenticators
|
|
```
|
|
|
|
**Test**:
|
|
```bash
|
|
# Test MFA flow
|
|
# 1. Enable MFA in Auth0
|
|
# 2. Create test user
|
|
# 3. Login → should prompt for authenticator setup
|
|
```
|
|
|
|
---
|
|
|
|
## Issue: Logout Not Clearing Session
|
|
|
|
**Error**: User clicks logout but remains logged in
|
|
|
|
**Cause 1**: Wrong logout URL
|
|
|
|
**Solution 1** (React):
|
|
|
|
```typescript
|
|
// WRONG ❌
|
|
logout() // Doesn't redirect or clear Auth0 session
|
|
|
|
// RIGHT ✅
|
|
logout({
|
|
logoutParams: {
|
|
returnTo: window.location.origin // Redirect after logout
|
|
}
|
|
})
|
|
```
|
|
|
|
**Cause 2**: Logout URL not in Auth0 allowlist
|
|
|
|
**Solution 2**:
|
|
|
|
```
|
|
Auth0 Dashboard → Applications → Settings
|
|
|
|
Allowed Logout URLs:
|
|
http://localhost:3000
|
|
https://myapp.com
|
|
|
|
(Must match where user is redirected after logout)
|
|
```
|
|
|
|
**Cause 3**: Session still active server-side
|
|
|
|
**Solution 3** (Next.js):
|
|
|
|
```typescript
|
|
// API route for logout
|
|
export default async function handler(req, res) {
|
|
await clearSession(res) // Auth0 SDK clears session
|
|
res.redirect('/')
|
|
}
|
|
|
|
// Or explicitly
|
|
import { getSession } from '@auth0/nextjs-auth0'
|
|
|
|
export const GET = async (req) => {
|
|
const session = await getSession()
|
|
if (session) {
|
|
await logout(req) // Clears Auth0 session
|
|
}
|
|
}
|
|
```
|
|
|
|
**Test**:
|
|
```bash
|
|
# Test logout
|
|
# 1. Login successfully
|
|
# 2. Click logout
|
|
# 3. Check:
|
|
# - Redirected to home
|
|
# - Cookies cleared
|
|
# - New login required
|
|
```
|
|
|
|
---
|
|
|
|
## Issue: Social Login Not Working
|
|
|
|
**Error**: Social login button doesn't work or redirects to error
|
|
|
|
**Cause 1**: Social connection not enabled for app
|
|
|
|
**Solution 1**:
|
|
|
|
```
|
|
Auth0 Dashboard → Connections → Social → Google
|
|
|
|
Under "Select Applications":
|
|
Toggle: Your App → Enable ✅
|
|
|
|
(Same for Facebook, GitHub, etc.)
|
|
```
|
|
|
|
**Cause 2**: Social app credentials wrong
|
|
|
|
**Solution 2** (Google example):
|
|
|
|
```
|
|
Auth0 Dashboard → Connections → Social → Google
|
|
|
|
Google OAuth2 settings:
|
|
Client ID: __________ (from Google Cloud Console)
|
|
Client Secret: __________ (from Google Cloud Console)
|
|
|
|
Get credentials:
|
|
1. Google Cloud Console: https://console.cloud.google.com
|
|
2. Create new project
|
|
3. Enable Google+ API
|
|
4. Create OAuth 2.0 Client ID
|
|
5. Copy Client ID and Secret to Auth0
|
|
```
|
|
|
|
**Cause 3**: Redirect URI not configured in social app
|
|
|
|
**Solution 3**:
|
|
|
|
```
|
|
Google Cloud Console:
|
|
Authorized redirect URIs:
|
|
https://YOUR_DOMAIN/login/callback
|
|
|
|
Facebook App Settings:
|
|
Valid OAuth Redirect URIs:
|
|
https://YOUR_DOMAIN/login/callback
|
|
|
|
GitHub:
|
|
Authorization callback URL:
|
|
https://YOUR_DOMAIN/login/callback
|
|
```
|
|
|
|
**Test**:
|
|
```bash
|
|
# Test social login
|
|
# 1. Visit login page
|
|
# 2. Click "Login with Google"
|
|
# 3. Should redirect to Google
|
|
# 4. After signin, should return to app
|
|
```
|
|
|
|
---
|
|
|
|
## General Debugging Techniques
|
|
|
|
### 1. Check Auth0 Logs
|
|
|
|
```
|
|
Auth0 Dashboard → Logs
|
|
|
|
Shows all auth events with:
|
|
- User email
|
|
- Event type (login, logout, error)
|
|
- Timestamp
|
|
- Error details
|
|
```
|
|
|
|
### 2. Inspect Token
|
|
|
|
```javascript
|
|
// In browser console
|
|
const token = await auth0.getAccessTokenSilently()
|
|
|
|
// Decode at jwt.io
|
|
// Or programmatically
|
|
import jwt_decode from 'jwt-decode'
|
|
const decoded = jwt_decode(token)
|
|
console.log(decoded)
|
|
```
|
|
|
|
### 3. Check Request Headers
|
|
|
|
```bash
|
|
# In browser DevTools → Network tab
|
|
# Select API request
|
|
# Check headers:
|
|
# Authorization: Bearer {token}
|
|
# Content-Type: application/json
|
|
```
|
|
|
|
### 4. Enable Debug Mode
|
|
|
|
```typescript
|
|
// React
|
|
<Auth0Provider
|
|
domain={domain}
|
|
clientId={clientId}
|
|
logoutParams={{ returnTo: 'http://localhost:3000' }}
|
|
onError={(error) => console.error('Auth0 Error:', error)}
|
|
>
|
|
```
|
|
|
|
---
|
|
|
|
## Still Having Issues?
|
|
|
|
1. **Check Auth0 Logs**: Auth0 Dashboard → Logs (shows actual errors)
|
|
2. **Run security audit**: `/oauth-security-audit`
|
|
3. **Review implementation**: `/oauth-implement [framework]`
|
|
4. **Check Auth0 docs**: https://auth0.com/docs
|
|
5. **Contact support**: https://support.auth0.com
|
|
|
|
---
|
|
|
|
**Status**: Troubleshooting guide ready!
|