Initial commit
This commit is contained in:
555
commands/oauth-troubleshoot.md
Normal file
555
commands/oauth-troubleshoot.md
Normal file
@@ -0,0 +1,555 @@
|
||||
---
|
||||
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!
|
||||
Reference in New Issue
Block a user