Initial commit
This commit is contained in:
417
commands/oauth-implement.md
Normal file
417
commands/oauth-implement.md
Normal file
@@ -0,0 +1,417 @@
|
||||
---
|
||||
description: Framework-specific implementation guide for adding Auth0 OAuth to your application
|
||||
---
|
||||
|
||||
# OAuth Implementation Guide
|
||||
|
||||
Detailed code walkthrough for implementing Auth0 authentication in your framework.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
/oauth-implement [framework]
|
||||
```
|
||||
|
||||
Supported frameworks:
|
||||
- `react` - React with Create React App or Vite
|
||||
- `nextjs` - Next.js (13+ with App Router recommended)
|
||||
- `nodejs` - Node.js/Express backend
|
||||
- `fastapi` - Python FastAPI
|
||||
- `django` - Python Django
|
||||
- `vue` - Vue.js
|
||||
- `svelte` - Svelte
|
||||
- `flutter` - Flutter mobile
|
||||
- `react-native` - React Native mobile
|
||||
|
||||
## Implementation Steps by Framework
|
||||
|
||||
### React (SPA)
|
||||
|
||||
#### 1. Install Auth0 React SDK
|
||||
|
||||
```bash
|
||||
npm install @auth0/auth0-react
|
||||
```
|
||||
|
||||
#### 2. Wrap App with Auth0Provider
|
||||
|
||||
```typescript
|
||||
// src/main.tsx
|
||||
import { Auth0Provider } from '@auth0/auth0-react'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<Auth0Provider
|
||||
domain={import.meta.env.VITE_AUTH0_DOMAIN}
|
||||
clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}
|
||||
authorizationParams={{
|
||||
redirect_uri: window.location.origin,
|
||||
scope: 'openid profile email read:items',
|
||||
audience: `https://${import.meta.env.VITE_AUTH0_DOMAIN}/api/v2/`
|
||||
}}
|
||||
cacheLocation="memory"
|
||||
>
|
||||
<App />
|
||||
</Auth0Provider>
|
||||
)
|
||||
```
|
||||
|
||||
#### 3. Create Login/Logout Components
|
||||
|
||||
```typescript
|
||||
// src/components/Auth.tsx
|
||||
import { useAuth0 } from '@auth0/auth0-react'
|
||||
|
||||
export function LoginButton() {
|
||||
const { loginWithRedirect, isAuthenticated } = useAuth0()
|
||||
if (isAuthenticated) return null
|
||||
return <button onClick={() => loginWithRedirect()}>Login</button>
|
||||
}
|
||||
|
||||
export function LogoutButton() {
|
||||
const { logout, isAuthenticated } = useAuth0()
|
||||
if (!isAuthenticated) return null
|
||||
return <button onClick={() => logout({ logoutParams: { returnTo: window.location.origin } })}>Logout</button>
|
||||
}
|
||||
|
||||
export function Profile() {
|
||||
const { user, isAuthenticated } = useAuth0()
|
||||
if (!isAuthenticated) return <p>Not logged in</p>
|
||||
return (
|
||||
<div>
|
||||
<img src={user?.picture} alt={user?.name} />
|
||||
<h2>{user?.name}</h2>
|
||||
<p>{user?.email}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Protected Routes
|
||||
|
||||
```typescript
|
||||
// src/components/ProtectedRoute.tsx
|
||||
import { useAuth0 } from '@auth0/auth0-react'
|
||||
|
||||
export function ProtectedRoute({ children }: any) {
|
||||
const { isAuthenticated, isLoading } = useAuth0()
|
||||
|
||||
if (isLoading) return <div>Loading...</div>
|
||||
if (!isAuthenticated) return <div>Please log in</div>
|
||||
|
||||
return children
|
||||
}
|
||||
|
||||
// In App.tsx
|
||||
<Routes>
|
||||
<Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
|
||||
</Routes>
|
||||
```
|
||||
|
||||
#### 5. Call Protected API
|
||||
|
||||
```typescript
|
||||
// src/hooks/useApi.ts
|
||||
import { useAuth0 } from '@auth0/auth0-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export function useApi<T>(url: string) {
|
||||
const { getAccessTokenSilently } = useAuth0()
|
||||
const [data, setData] = useState<T | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const token = await getAccessTokenSilently()
|
||||
const response = await fetch(url, {
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
})
|
||||
if (!response.ok) throw new Error('API error')
|
||||
const result = await response.json()
|
||||
if (isMounted) setData(result)
|
||||
} catch (err) {
|
||||
if (isMounted) setError(err instanceof Error ? err : new Error('Unknown error'))
|
||||
} finally {
|
||||
if (isMounted) setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
fetchData()
|
||||
return () => { isMounted = false }
|
||||
}, [url, getAccessTokenSilently])
|
||||
|
||||
return { data, error, loading }
|
||||
}
|
||||
```
|
||||
|
||||
#### 6. Environment Variables
|
||||
|
||||
```env
|
||||
VITE_AUTH0_DOMAIN=YOUR_DOMAIN.auth0.com
|
||||
VITE_AUTH0_CLIENT_ID=YOUR_CLIENT_ID
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Next.js (Full-Stack)
|
||||
|
||||
#### 1. Install SDK
|
||||
|
||||
```bash
|
||||
npm install @auth0/nextjs-auth0
|
||||
```
|
||||
|
||||
#### 2. Create Auth Routes
|
||||
|
||||
**App Router** (Recommended):
|
||||
```typescript
|
||||
// app/api/auth/[auth0]/route.ts
|
||||
import { handleAuth } from '@auth0/nextjs-auth0'
|
||||
|
||||
export const GET = handleAuth()
|
||||
```
|
||||
|
||||
**Pages Router**:
|
||||
```typescript
|
||||
// pages/api/auth/[auth0].ts
|
||||
import { handleAuth } from '@auth0/nextjs-auth0'
|
||||
|
||||
export default handleAuth()
|
||||
```
|
||||
|
||||
#### 3. Access User in Components
|
||||
|
||||
**App Router**:
|
||||
```typescript
|
||||
// app/profile/page.tsx
|
||||
import { getSession } from '@auth0/nextjs-auth0'
|
||||
import { redirect } from 'next/navigation'
|
||||
|
||||
export default async function ProfilePage() {
|
||||
const session = await getSession()
|
||||
|
||||
if (!session) {
|
||||
redirect('/api/auth/login')
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Welcome, {session.user.name}</h1>
|
||||
<img src={session.user.picture} alt={session.user.name} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Login/Logout Links
|
||||
|
||||
```typescript
|
||||
// app/components/Nav.tsx
|
||||
import { getSession } from '@auth0/nextjs-auth0'
|
||||
|
||||
export async function Nav() {
|
||||
const session = await getSession()
|
||||
|
||||
return (
|
||||
<nav>
|
||||
{session ? (
|
||||
<>
|
||||
<span>{session.user.name}</span>
|
||||
<a href="/api/auth/logout">Logout</a>
|
||||
</>
|
||||
) : (
|
||||
<a href="/api/auth/login">Login</a>
|
||||
)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. Call API with Token
|
||||
|
||||
```typescript
|
||||
// app/api/items/route.ts
|
||||
import { getSession } from '@auth0/nextjs-auth0'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET() {
|
||||
const session = await getSession()
|
||||
|
||||
if (!session) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const response = await fetch('http://localhost:3001/items', {
|
||||
headers: { Authorization: `Bearer ${session.accessToken}` }
|
||||
})
|
||||
|
||||
return NextResponse.json(await response.json())
|
||||
}
|
||||
```
|
||||
|
||||
#### 6. Environment Variables
|
||||
|
||||
```env
|
||||
AUTH0_SECRET='[generated secret]'
|
||||
AUTH0_BASE_URL='http://localhost:3000'
|
||||
AUTH0_ISSUER_BASE_URL='https://YOUR_DOMAIN.auth0.com'
|
||||
AUTH0_CLIENT_ID='YOUR_CLIENT_ID'
|
||||
AUTH0_CLIENT_SECRET='YOUR_CLIENT_SECRET'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Node.js/Express Backend
|
||||
|
||||
#### 1. Install Dependencies
|
||||
|
||||
```bash
|
||||
npm install express-jwt jwks-rsa
|
||||
```
|
||||
|
||||
#### 2. Create JWT Middleware
|
||||
|
||||
```typescript
|
||||
// middleware/auth.ts
|
||||
import { expressjwt as jwt } from 'express-jwt'
|
||||
import jwksRsa from 'jwks-rsa'
|
||||
|
||||
const checkJwt = jwt({
|
||||
secret: jwksRsa.expressJwtSecret({
|
||||
cache: true,
|
||||
jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`
|
||||
}),
|
||||
audience: `https://${process.env.AUTH0_DOMAIN}/api/v2/`,
|
||||
issuer: `https://${process.env.AUTH0_DOMAIN}/`,
|
||||
algorithms: ['RS256']
|
||||
})
|
||||
|
||||
export default checkJwt
|
||||
```
|
||||
|
||||
#### 3. Protect Routes
|
||||
|
||||
```typescript
|
||||
// routes/items.ts
|
||||
import express from 'express'
|
||||
import checkJwt from '../middleware/auth'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.get('/public', (req, res) => {
|
||||
res.json({ message: 'Public' })
|
||||
})
|
||||
|
||||
router.get('/items', checkJwt, (req, res) => {
|
||||
// req.auth contains decoded JWT
|
||||
res.json({ userId: req.auth.sub, items: [] })
|
||||
})
|
||||
|
||||
// Scope validation
|
||||
router.delete('/items/:id', checkJwt, (req, res) => {
|
||||
const scope = req.auth.scope?.split(' ') || []
|
||||
|
||||
if (!scope.includes('delete:items')) {
|
||||
return res.status(403).json({ error: 'Insufficient permissions' })
|
||||
}
|
||||
|
||||
res.json({ message: 'Item deleted' })
|
||||
})
|
||||
|
||||
export default router
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### FastAPI (Python)
|
||||
|
||||
```python
|
||||
# main.py
|
||||
from fastapi import FastAPI, Depends, HTTPException
|
||||
from fastapi.security import HTTPBearer, HTTPAuthCredentials
|
||||
import jwt
|
||||
from jwt import algorithms
|
||||
|
||||
app = FastAPI()
|
||||
security = HTTPBearer()
|
||||
|
||||
DOMAIN = "YOUR_DOMAIN.auth0.com"
|
||||
|
||||
async def verify_token(credentials: HTTPAuthCredentials = Depends(security)):
|
||||
token = credentials.credentials
|
||||
|
||||
try:
|
||||
# Get public key
|
||||
jwks_client = jwt.PyJWKClient(f"https://{DOMAIN}/.well-known/jwks.json")
|
||||
signing_key = jwks_client.get_signing_key_from_jwt(token)
|
||||
|
||||
# Verify token
|
||||
payload = jwt.decode(
|
||||
token,
|
||||
signing_key.key,
|
||||
algorithms=["RS256"],
|
||||
audience=f"https://{DOMAIN}/api/v2/",
|
||||
issuer=f"https://{DOMAIN}/"
|
||||
)
|
||||
|
||||
return payload
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=401, detail="Invalid token")
|
||||
|
||||
@app.get("/items")
|
||||
async def get_items(user: dict = Depends(verify_token)):
|
||||
return { "user_id": user["sub"], "items": [] }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Implementation
|
||||
|
||||
### Test Login Flow
|
||||
|
||||
```bash
|
||||
# 1. Visit login page
|
||||
open http://localhost:3000/api/auth/login
|
||||
|
||||
# 2. Complete auth flow in Auth0
|
||||
# 3. Check if callback works
|
||||
|
||||
# 4. Test API
|
||||
curl -H "Authorization: Bearer <ACCESS_TOKEN>" \
|
||||
http://localhost:3001/api/items
|
||||
```
|
||||
|
||||
### Inspect Token (React)
|
||||
|
||||
```javascript
|
||||
// In browser console
|
||||
const token = await auth0.getAccessTokenSilently()
|
||||
console.log(token)
|
||||
|
||||
// Decode at jwt.io
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Issues & Solutions
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Token expired | SDK auto-refreshes, check refresh token rotation enabled |
|
||||
| CORS error | Configure CORS in backend, check Auth0 allowed origins |
|
||||
| Silent auth fails | Check `cacheLocation: "memory"`, try explicit login |
|
||||
| Scope not in token | Add to Auth0 rule or API scopes config |
|
||||
| Token validation fails | Verify audience, issuer, algorithms match |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Test implementation with `/oauth-security-audit`
|
||||
2. Run `/oauth-troubleshoot` for any issues
|
||||
3. Deploy and update callback URLs to production domain
|
||||
|
||||
**Status**: Implementation guide ready!
|
||||
417
commands/oauth-migrate.md
Normal file
417
commands/oauth-migrate.md
Normal file
@@ -0,0 +1,417 @@
|
||||
---
|
||||
description: Migration guide for moving from other auth providers to Auth0
|
||||
---
|
||||
|
||||
# OAuth Migration Guide
|
||||
|
||||
Migrate from other authentication providers to Auth0.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
/oauth-migrate [provider]
|
||||
```
|
||||
|
||||
Supported providers:
|
||||
- `firebase` - Firebase Authentication
|
||||
- `okta` - Okta
|
||||
- `cognito` - AWS Cognito
|
||||
- `keycloak` - Keycloak
|
||||
- `custom` - Custom JWT/OAuth implementation
|
||||
- `social-only` - Only social logins (no user DB)
|
||||
|
||||
---
|
||||
|
||||
## Migration: Firebase → Auth0
|
||||
|
||||
### Phase 1: Planning (1-2 hours)
|
||||
|
||||
**Assess current setup**:
|
||||
- How many users? (1k, 100k, 1M+)
|
||||
- Authentication methods? (email/password, social, custom)
|
||||
- Custom claims? (roles, metadata)
|
||||
- Token usage? (access tokens, ID tokens)
|
||||
|
||||
**Timeline**:
|
||||
- Small (1k users): 1 day
|
||||
- Medium (10k users): 3-5 days
|
||||
- Large (100k+ users): 1-2 weeks
|
||||
|
||||
**Parallel running** (recommended):
|
||||
- Keep Firebase running while testing Auth0
|
||||
- Gradual migration: 10% → 25% → 50% → 100%
|
||||
- Rollback plan: If Auth0 issues, fall back to Firebase
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Set Up Auth0 (1-2 hours)
|
||||
|
||||
```bash
|
||||
# Step 1: Create Auth0 account
|
||||
# https://auth0.com/signup
|
||||
|
||||
# Step 2: Create application
|
||||
/oauth-setup-auth0
|
||||
|
||||
# Step 3: Configure connections
|
||||
# - Enable database connection
|
||||
# - Enable same social connections as Firebase
|
||||
# - Copy social app credentials from Firebase
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: User Migration (1-3 days)
|
||||
|
||||
#### Option A: Automatic Migration (Recommended)
|
||||
|
||||
Use Auth0 extensibility to migrate users on first login:
|
||||
|
||||
```javascript
|
||||
// Auth0 Rule: Migrate users from Firebase
|
||||
module.exports = function(user, context, callback) {
|
||||
// Check if user already in Auth0
|
||||
if (user.identities && user.identities.length > 0) {
|
||||
return callback(null, user, context)
|
||||
}
|
||||
|
||||
// Try to authenticate against Firebase
|
||||
const firebase = require('firebase')
|
||||
|
||||
firebase.initializeApp({
|
||||
apiKey: process.env.FIREBASE_API_KEY,
|
||||
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
|
||||
databaseURL: process.env.FIREBASE_DATABASE_URL
|
||||
})
|
||||
|
||||
firebase.auth().signInWithEmailAndPassword(user.email, context.request.query.password)
|
||||
.then(function(firebaseUser) {
|
||||
// Get Firebase custom claims
|
||||
firebaseUser.getIdTokenResult()
|
||||
.then(function(idTokenResult) {
|
||||
// Copy Firebase claims to Auth0
|
||||
user.app_metadata = user.app_metadata || {}
|
||||
user.app_metadata.firebase_id = firebaseUser.uid
|
||||
user.app_metadata.roles = idTokenResult.claims.roles || []
|
||||
|
||||
// Create Auth0 user
|
||||
context.clientMetadata = {
|
||||
firebase_migrated: 'true'
|
||||
}
|
||||
|
||||
callback(null, user, context)
|
||||
})
|
||||
})
|
||||
.catch(function(error) {
|
||||
callback(new Error('Firebase auth failed: ' + error.message))
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- ✅ No downtime
|
||||
- ✅ Users migrate automatically on first login
|
||||
- ✅ Can run parallel with Firebase
|
||||
- ✅ Easy rollback
|
||||
|
||||
**Timeline**: 2-3 weeks (let users naturally migrate)
|
||||
|
||||
---
|
||||
|
||||
#### Option B: Bulk Migration
|
||||
|
||||
Export Firebase users and import to Auth0:
|
||||
|
||||
```bash
|
||||
# Step 1: Export users from Firebase
|
||||
firebase auth:export users.json --format json
|
||||
|
||||
# Step 2: Transform to Auth0 format
|
||||
node transform-firebase-to-auth0.js
|
||||
|
||||
# Step 3: Import to Auth0
|
||||
# Auth0 Dashboard → Users → Import (drag CSV)
|
||||
# Or use Management API
|
||||
```
|
||||
|
||||
**transform-firebase-to-auth0.js**:
|
||||
```javascript
|
||||
const fs = require('fs')
|
||||
|
||||
const firebaseUsers = JSON.parse(fs.readFileSync('users.json', 'utf8'))
|
||||
|
||||
const auth0Users = firebaseUsers.users.map(user => ({
|
||||
email: user.email,
|
||||
email_verified: user.emailVerified,
|
||||
user_id: user.localId,
|
||||
identities: [{
|
||||
connection: 'Username-Password-Authentication',
|
||||
user_id: user.localId,
|
||||
provider: 'auth0',
|
||||
isSocial: false
|
||||
}],
|
||||
user_metadata: user.customClaims || {},
|
||||
app_metadata: {
|
||||
roles: user.customClaims?.roles || [],
|
||||
firebase_id: user.localId
|
||||
},
|
||||
blocked: !user.emailVerified,
|
||||
created_at: new Date(parseInt(user.createdAt)).toISOString(),
|
||||
updated_at: new Date(parseInt(user.lastLoginAt)).toISOString()
|
||||
}))
|
||||
|
||||
fs.writeFileSync('auth0-users.json', JSON.stringify(auth0Users, null, 2))
|
||||
console.log(`Converted ${auth0Users.length} users`)
|
||||
```
|
||||
|
||||
**Timeline**: 1-2 hours (bulk import) + verification
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Update Application Code (2-4 hours)
|
||||
|
||||
#### Frontend Migration (React)
|
||||
|
||||
```typescript
|
||||
// BEFORE (Firebase)
|
||||
import { initializeApp } from 'firebase/app'
|
||||
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth'
|
||||
|
||||
const app = initializeApp(firebaseConfig)
|
||||
const auth = getAuth(app)
|
||||
|
||||
async function login(email, password) {
|
||||
const userCredential = await signInWithEmailAndPassword(auth, email, password)
|
||||
const token = await userCredential.user.getIdToken()
|
||||
return token
|
||||
}
|
||||
|
||||
// AFTER (Auth0)
|
||||
import { useAuth0 } from '@auth0/auth0-react'
|
||||
|
||||
function LoginPage() {
|
||||
const { loginWithRedirect } = useAuth0()
|
||||
|
||||
return (
|
||||
<button onClick={() => loginWithRedirect()}>
|
||||
Login with Auth0
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const { getAccessTokenSilently } = useAuth0()
|
||||
|
||||
async function getToken() {
|
||||
return await getAccessTokenSilently()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Backend Migration (Node.js)
|
||||
|
||||
```typescript
|
||||
// BEFORE (Firebase)
|
||||
import * as admin from 'firebase-admin'
|
||||
|
||||
const app = admin.initializeApp()
|
||||
|
||||
function verifyToken(token) {
|
||||
return admin.auth().verifyIdToken(token)
|
||||
}
|
||||
|
||||
// AFTER (Auth0)
|
||||
import { expressjwt } from 'express-jwt'
|
||||
import jwksRsa from 'jwks-rsa'
|
||||
|
||||
const checkJwt = expressjwt({
|
||||
secret: jwksRsa.expressJwtSecret({
|
||||
jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`
|
||||
}),
|
||||
audience: `https://${process.env.AUTH0_DOMAIN}/api/v2/`,
|
||||
issuer: `https://${process.env.AUTH0_DOMAIN}/`,
|
||||
algorithms: ['RS256']
|
||||
})
|
||||
|
||||
app.get('/api/protected', checkJwt, (req, res) => {
|
||||
res.json({ user: req.auth.sub })
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: Testing (1-2 days)
|
||||
|
||||
```typescript
|
||||
// Test list
|
||||
- [ ] User login (email/password)
|
||||
- [ ] User signup
|
||||
- [ ] Social login (Google, GitHub, etc)
|
||||
- [ ] Password reset
|
||||
- [ ] Token refresh
|
||||
- [ ] API access with token
|
||||
- [ ] MFA (if enabled)
|
||||
- [ ] Logout clears session
|
||||
- [ ] Session persistence
|
||||
- [ ] Error handling (invalid password, etc)
|
||||
```
|
||||
|
||||
**Test script**:
|
||||
```bash
|
||||
# Test login flow
|
||||
curl -X POST https://YOUR_DOMAIN/oauth/token \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"client_id": "YOUR_CLIENT_ID",
|
||||
"client_secret": "YOUR_CLIENT_SECRET",
|
||||
"audience": "https://YOUR_DOMAIN/api/v2/",
|
||||
"grant_type": "client_credentials"
|
||||
}'
|
||||
|
||||
# Test protected API
|
||||
curl -H "Authorization: Bearer ACCESS_TOKEN" \
|
||||
http://localhost:3001/api/protected
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: Deployment (2-4 hours)
|
||||
|
||||
**Deployment checklist**:
|
||||
- [ ] Update environment variables
|
||||
- [ ] Deploy frontend to staging
|
||||
- [ ] Deploy backend to staging
|
||||
- [ ] Run full test suite
|
||||
- [ ] Smoke test in production-like environment
|
||||
- [ ] Deploy to production
|
||||
- [ ] Monitor Auth0 logs for errors
|
||||
- [ ] Monitor application errors
|
||||
- [ ] Have rollback plan ready
|
||||
|
||||
**Gradual rollout**:
|
||||
1. Deploy to 10% of users
|
||||
2. Monitor for 2-4 hours
|
||||
3. If OK, deploy to 50%
|
||||
4. Monitor for 2-4 hours
|
||||
5. Deploy to 100%
|
||||
|
||||
---
|
||||
|
||||
## Migration: Cognito → Auth0
|
||||
|
||||
### Key Differences
|
||||
|
||||
| Feature | Cognito | Auth0 |
|
||||
|---------|---------|-------|
|
||||
| Setup | AWS Dashboard (complex) | Auth0 Dashboard (simple) |
|
||||
| Scopes | Fixed scopes | Custom scopes |
|
||||
| Rules | Limited | Powerful (JavaScript) |
|
||||
| User management | Via AWS API | Via UI or API |
|
||||
| Custom claims | userAttributes | app_metadata, user_metadata |
|
||||
| MFA | Built-in | Multiple options |
|
||||
| Cost | Pay per user | Fixed tier |
|
||||
|
||||
### Migration Steps
|
||||
|
||||
1. **Export Cognito users**:
|
||||
```bash
|
||||
aws cognito-idp list-users --user-pool-id us-east-1_xxx > cognito-users.json
|
||||
```
|
||||
|
||||
2. **Transform to Auth0 format**:
|
||||
```javascript
|
||||
// Similar to Firebase migration
|
||||
// Map Cognito attributes to Auth0 metadata
|
||||
```
|
||||
|
||||
3. **Update token validation**:
|
||||
```typescript
|
||||
// Change from Cognito to Auth0 jwks URI
|
||||
jwksUri: `https://${AUTH0_DOMAIN}/.well-known/jwks.json`
|
||||
```
|
||||
|
||||
4. **Update frontend SDK**:
|
||||
```typescript
|
||||
// Replace Amplify with Auth0 SDK
|
||||
import { useAuth0 } from '@auth0/auth0-react'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
### If issues occur:
|
||||
|
||||
1. **Immediately revert** to previous provider
|
||||
2. **Keep both systems running** during migration
|
||||
3. **Have database backup** before migration
|
||||
4. **Monitor error rates** during rollout
|
||||
|
||||
```bash
|
||||
# Revert frontend
|
||||
git revert <commit> # Go back to old SDK
|
||||
npm install # Reinstall old packages
|
||||
npm run deploy # Deploy rollback
|
||||
|
||||
# Check status
|
||||
Auth0 Dashboard → Logs (verify no new logins via Auth0)
|
||||
Firebase Console → Logs (verify resumed usage)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Post-Migration
|
||||
|
||||
### Cleanup
|
||||
- [ ] Decommission old auth system
|
||||
- [ ] Delete old user data (after retention period)
|
||||
- [ ] Update documentation
|
||||
- [ ] Train team on new system
|
||||
- [ ] Close old auth provider account
|
||||
|
||||
### Optimization
|
||||
- [ ] Review Auth0 rules for performance
|
||||
- [ ] Optimize token expiration
|
||||
- [ ] Set up audit logging
|
||||
- [ ] Monitor costs
|
||||
- [ ] Plan security hardening
|
||||
|
||||
---
|
||||
|
||||
## Common Migration Issues
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| User exists error on import | Deduplicate by email before import |
|
||||
| Scopes missing | Manually add to Auth0 API config |
|
||||
| Custom claims lost | Map to user_metadata in Auth0 |
|
||||
| Password reset broken | Reconfigure email templates in Auth0 |
|
||||
| Social login not working | Re-add social app credentials |
|
||||
|
||||
---
|
||||
|
||||
## Timeline Summary
|
||||
|
||||
| Provider | Automatic | Bulk | Total |
|
||||
|----------|-----------|------|-------|
|
||||
| Firebase (1k users) | 2-3 weeks | 3-5 days | 1 week |
|
||||
| Firebase (100k users) | 3-4 weeks | 2-3 weeks | 4-5 weeks |
|
||||
| Cognito (any size) | 2-3 weeks | Similar to Firebase | 3-5 weeks |
|
||||
| Custom OAuth (simple) | 1 week | 2-3 days | 1 week |
|
||||
| Custom OAuth (complex) | 2-3 weeks | 1-2 weeks | 3-4 weeks |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Choose migration strategy** (automatic vs bulk)
|
||||
2. **Set up Auth0 tenant** (`/oauth-setup-auth0`)
|
||||
3. **Test integration** (`/oauth-implement [framework]`)
|
||||
4. **Run security audit** (`/oauth-security-audit`)
|
||||
5. **Execute migration** (follow phase-by-phase plan)
|
||||
6. **Monitor closely** during first week
|
||||
|
||||
---
|
||||
|
||||
**Status**: Migration guide ready!
|
||||
**Need help?** `/oauth-troubleshoot` or contact Auth0 support
|
||||
303
commands/oauth-security-audit.md
Normal file
303
commands/oauth-security-audit.md
Normal file
@@ -0,0 +1,303 @@
|
||||
---
|
||||
description: Security audit checklist for Auth0 OAuth implementation
|
||||
---
|
||||
|
||||
# OAuth Security Audit
|
||||
|
||||
Run a comprehensive security audit on your Auth0 implementation.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
/oauth-security-audit
|
||||
```
|
||||
|
||||
This will check:
|
||||
- Token security (storage, expiration, rotation)
|
||||
- OAuth flow security (PKCE, state parameter, CSRF)
|
||||
- Compliance (GDPR, HIPAA, SOC2)
|
||||
- Configuration hardening
|
||||
- Common vulnerabilities
|
||||
|
||||
---
|
||||
|
||||
## Security Checklist
|
||||
|
||||
### Frontend Security
|
||||
|
||||
- [ ] **Token Storage**: In-memory or HTTP-only cookies only (NO localStorage)
|
||||
- Check: `grep -r "localStorage.*token" src/`
|
||||
- Should be empty
|
||||
|
||||
- [ ] **PKCE Enabled**: For SPAs (Authorization Code + PKCE)
|
||||
- Check: Auth0 React SDK handles automatically ✅
|
||||
- Or verify custom code includes `code_verifier`
|
||||
|
||||
- [ ] **State Parameter**: CSRF protection
|
||||
- Check: Auth0 SDKs handle automatically ✅
|
||||
- Or verify `state` parameter in custom auth flow
|
||||
|
||||
- [ ] **HTTPS Enforced**: All auth requests over HTTPS
|
||||
- Check: No `http://` in production callback URLs
|
||||
- Production callback URLs use `https://`
|
||||
|
||||
- [ ] **Token Expiration Short**: Access tokens < 15 minutes
|
||||
- Check: Auth0 Dashboard → Applications → Settings → Token Expiration
|
||||
- Should be: 300-900 seconds (5-15 minutes)
|
||||
|
||||
- [ ] **Refresh Token Rotation**: Enabled for token refresh
|
||||
- Check: Auth0 Dashboard → Applications → Settings → Refresh Token Rotation
|
||||
- Should be: Enabled ✅
|
||||
|
||||
- [ ] **Content Security Policy (CSP)**: Restrict script sources
|
||||
- Check: HTTP header `Content-Security-Policy`
|
||||
- Should include: `default-src 'self'`
|
||||
|
||||
---
|
||||
|
||||
### Backend Security
|
||||
|
||||
- [ ] **JWT Signature Validation**: Verify token signature
|
||||
- Check: Code uses `jwt.verify()` with public key
|
||||
- Should NOT use: `jwt.decode()` (no verification)
|
||||
|
||||
- [ ] **Audience Validation**: Check `aud` claim matches API
|
||||
- Check: `jwt.verify(token, key, { audience: 'https://api.example.com' })`
|
||||
- Token `aud` must match expected audience
|
||||
|
||||
- [ ] **Issuer Validation**: Check `iss` claim matches Auth0 domain
|
||||
- Check: `jwt.verify(token, key, { issuer: 'https://YOUR_DOMAIN/' })`
|
||||
- Token `iss` must match Auth0 domain
|
||||
|
||||
- [ ] **Algorithm Validation**: Only RS256 (asymmetric)
|
||||
- Check: `jwt.verify(token, key, { algorithms: ['RS256'] })`
|
||||
- Should NOT allow: `HS256` (symmetric, security risk)
|
||||
|
||||
- [ ] **Scope Validation**: Check scopes for authorization
|
||||
- Check: Code validates `token.scope` includes required scope
|
||||
- Example: `if (!scopes.includes('delete:items')) return 403`
|
||||
|
||||
- [ ] **No Token in Logs**: Sensitive tokens not logged
|
||||
- Check: `grep -r "token\|password\|secret" logs/`
|
||||
- Should be: Sanitized or empty
|
||||
|
||||
- [ ] **CORS Configured Properly**: Only allow trusted origins
|
||||
- Check: `app.use(cors({ origin: ['https://myapp.com'] }))`
|
||||
- Should NOT be: `origin: '*'` (allows any origin)
|
||||
|
||||
---
|
||||
|
||||
### Auth0 Configuration Security
|
||||
|
||||
- [ ] **MFA Enabled**: Multi-factor authentication required
|
||||
- Check: Auth0 Dashboard → Connections → Authenticators
|
||||
- Should have: Google Authenticator, SMS, or Email OTP enabled
|
||||
|
||||
- [ ] **Password Policy**: Strong passwords required
|
||||
- Check: Auth0 Dashboard → Connections → Database → Password Policy
|
||||
- Should be: "Good" or "Excellent"
|
||||
|
||||
- [ ] **Suspicious Activity Detection**: Enabled
|
||||
- Check: Auth0 Dashboard → Security → Attack Protection
|
||||
- Should have: Brute force, suspicious IP protection enabled
|
||||
|
||||
- [ ] **Logout Clears Session**: User properly logged out
|
||||
- Check: `/api/auth/logout` clears all session data
|
||||
- Should have: `logoutParams: { returnTo: safe_url }`
|
||||
|
||||
- [ ] **No Overpermissioned Scopes**: Only request necessary scopes
|
||||
- Check: Auth0 Dashboard → Applications → Settings → Default Audience
|
||||
- Should be: Minimal (e.g., `openid profile email`)
|
||||
|
||||
- [ ] **API Keys Secure**: Secrets not in version control
|
||||
- Check: `.env` is in `.gitignore` ✅
|
||||
- Should NOT be in: `git log`, `public files`, `comments`
|
||||
|
||||
- [ ] **Rules/Actions Audited**: Custom logic secure
|
||||
- Check: Auth0 Dashboard → Rules → [Review each rule]
|
||||
- Should NOT: Grant extra permissions, log passwords, call untrusted APIs
|
||||
|
||||
---
|
||||
|
||||
### Data Protection & Compliance
|
||||
|
||||
- [ ] **GDPR Compliant**: User consent, deletion, portability
|
||||
- [ ] Consent shown before social login
|
||||
- [ ] User can request data deletion (via API or form)
|
||||
- [ ] Data deletion implemented (removes from Auth0 + your DB)
|
||||
- [ ] Privacy policy links from login page
|
||||
|
||||
- [ ] **HIPAA Compliant**: (if handling health data)
|
||||
- [ ] Business Associate Agreement (BAA) signed with Auth0
|
||||
- [ ] MFA enforced
|
||||
- [ ] Audit logging enabled
|
||||
- [ ] Data encrypted in transit (HTTPS) and at rest
|
||||
|
||||
- [ ] **SOC2 Compliant**: If required for compliance
|
||||
- [ ] Change logs available (Auth0 Logs)
|
||||
- [ ] Access controls documented
|
||||
- [ ] Incident response plan in place
|
||||
- [ ] Regular security assessments done
|
||||
|
||||
- [ ] **Data Residency**: Data stored in correct region
|
||||
- Check: Auth0 Dashboard → Tenants → Region
|
||||
- EU apps: Select "Europe" region
|
||||
- US apps: Select "United States" region
|
||||
|
||||
---
|
||||
|
||||
### Error Handling & Logging
|
||||
|
||||
- [ ] **Errors Don't Leak Info**: Auth errors are generic
|
||||
- Check: Error messages in UI
|
||||
- Should be: "Login failed" (NOT "Email doesn't exist" or "Invalid password")
|
||||
|
||||
- [ ] **Webhook Errors Handled**: Failures don't break auth flow
|
||||
- Check: Webhook error handler has try/catch
|
||||
- Should have: Retry logic with exponential backoff
|
||||
|
||||
- [ ] **Audit Logs Enabled**: All auth events logged
|
||||
- Check: Auth0 Dashboard → Logs (shows all login events)
|
||||
- Should have: 100+ entries with timestamps
|
||||
|
||||
- [ ] **Sensitive Data Redacted**: Logs don't contain secrets
|
||||
- Check: grep -r "password\|token\|secret" logs/
|
||||
- Should be: Redacted or not logged
|
||||
|
||||
---
|
||||
|
||||
### Testing
|
||||
|
||||
- [ ] **Unit Tests**: Auth components tested
|
||||
- Check: `npm test` includes auth tests
|
||||
- Should have: Mock Auth0, test protected routes
|
||||
|
||||
- [ ] **Integration Tests**: Auth flow tested end-to-end
|
||||
- Check: Test login → callback → API call
|
||||
- Should verify: Token exchange, API access
|
||||
|
||||
- [ ] **Security Tests**: Vulnerabilities tested
|
||||
- [ ] Test expired token handling
|
||||
- [ ] Test invalid token rejection
|
||||
- [ ] Test missing scope error (403)
|
||||
- [ ] Test logout clears session
|
||||
|
||||
---
|
||||
|
||||
## Security Scoring
|
||||
|
||||
**Count your checkmarks**:
|
||||
|
||||
- 40+ checked: ✅ **Excellent** (Production ready)
|
||||
- 30-39 checked: ⚠️ **Good** (Address medium priority items)
|
||||
- 20-29 checked: ❌ **Fair** (Address high priority items)
|
||||
- <20 checked: 🚨 **Critical** (Major issues, don't deploy)
|
||||
|
||||
---
|
||||
|
||||
## Common Vulnerabilities to Fix
|
||||
|
||||
### 1. Token Leakage (Critical)
|
||||
|
||||
```javascript
|
||||
// WRONG ❌
|
||||
localStorage.setItem('token', accessToken)
|
||||
sessionStorage.setItem('token', accessToken)
|
||||
|
||||
// RIGHT ✅
|
||||
// Use Auth0 SDK (in-memory storage)
|
||||
// Or for Next.js (HTTP-only cookies)
|
||||
```
|
||||
|
||||
### 2. Missing PKCE (High)
|
||||
|
||||
```javascript
|
||||
// WRONG ❌
|
||||
// No code_verifier or code_challenge
|
||||
|
||||
// RIGHT ✅
|
||||
// Use Auth0 React SDK (automatic PKCE)
|
||||
// Or custom: include code_verifier in token exchange
|
||||
```
|
||||
|
||||
### 3. Wrong Token Type (High)
|
||||
|
||||
```javascript
|
||||
// WRONG ❌
|
||||
const idToken = getIDToken()
|
||||
fetch('/api/items', {
|
||||
headers: { Authorization: `Bearer ${idToken}` }
|
||||
})
|
||||
|
||||
// RIGHT ✅
|
||||
const accessToken = getAccessToken()
|
||||
fetch('/api/items', {
|
||||
headers: { Authorization: `Bearer ${accessToken}` }
|
||||
})
|
||||
```
|
||||
|
||||
### 4. No Audience Validation (High)
|
||||
|
||||
```typescript
|
||||
// WRONG ❌
|
||||
jwt.verify(token, publicKey) // No audience check
|
||||
|
||||
// RIGHT ✅
|
||||
jwt.verify(token, publicKey, {
|
||||
audience: 'https://api.myapp.com'
|
||||
})
|
||||
```
|
||||
|
||||
### 5. Scope Not Checked (Medium)
|
||||
|
||||
```typescript
|
||||
// WRONG ❌
|
||||
app.delete('/items/:id', checkJwt, (req, res) => {
|
||||
// Delete without scope check
|
||||
res.json({ deleted: true })
|
||||
})
|
||||
|
||||
// RIGHT ✅
|
||||
app.delete('/items/:id', checkJwt, (req, res) => {
|
||||
const scopes = req.auth.scope?.split(' ') || []
|
||||
if (!scopes.includes('delete:items')) {
|
||||
return res.status(403).json({ error: 'Insufficient permissions' })
|
||||
}
|
||||
res.json({ deleted: true })
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Remediation Priority
|
||||
|
||||
### Priority 1 (Fix immediately - before production)
|
||||
- [ ] Token storage (localStorage → in-memory)
|
||||
- [ ] JWT signature validation
|
||||
- [ ] Audience validation
|
||||
- [ ] HTTPS enforced
|
||||
|
||||
### Priority 2 (Fix within 1 week)
|
||||
- [ ] Scope validation in API
|
||||
- [ ] MFA enabled
|
||||
- [ ] Audit logging
|
||||
- [ ] CSRF protection (state parameter)
|
||||
|
||||
### Priority 3 (Fix within 1 month)
|
||||
- [ ] Compliance (GDPR, HIPAA)
|
||||
- [ ] Webhook error handling
|
||||
- [ ] Security testing
|
||||
- [ ] Incident response plan
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Review this checklist** with your team
|
||||
2. **Fix high-priority items** (Priority 1)
|
||||
3. **Run again**: `/oauth-security-audit`
|
||||
4. **If issues remain**: `/oauth-troubleshoot` for help
|
||||
|
||||
---
|
||||
|
||||
**Score**: [X] / 45 items checked
|
||||
452
commands/oauth-setup-auth0.md
Normal file
452
commands/oauth-setup-auth0.md
Normal file
@@ -0,0 +1,452 @@
|
||||
---
|
||||
description: Interactive Auth0 setup wizard for configuring OAuth applications, connections, and tenant settings
|
||||
---
|
||||
|
||||
# Auth0 OAuth Setup Wizard
|
||||
|
||||
Interactive guided setup for Auth0 authentication from scratch.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
/oauth-setup-auth0
|
||||
```
|
||||
|
||||
This will walk you through:
|
||||
1. **Auth0 Tenant Creation** - Create new Auth0 account/tenant
|
||||
2. **Application Configuration** - Set up app (SPA, Web, Mobile, M2M)
|
||||
3. **Connection Setup** - Configure database, social, enterprise login
|
||||
4. **Callback URL Configuration** - Set up redirect URIs
|
||||
5. **Basic Security** - Enable MFA, set token expiration
|
||||
6. **Environment Variables** - Generate .env file template
|
||||
|
||||
## Step-by-Step Wizard
|
||||
|
||||
### Step 1: Auth0 Tenant Setup
|
||||
|
||||
If you don't have an Auth0 account:
|
||||
|
||||
1. **Visit**: https://auth0.com/signup
|
||||
2. **Create account** with:
|
||||
- Email: Your business email
|
||||
- Password: Strong password
|
||||
- Company: Your company name
|
||||
3. **Choose region**: US, EU, or AU
|
||||
4. **Create tenant** - Auth0 generates unique domain:
|
||||
- Example: `mycompany.auth0.com`
|
||||
|
||||
**What you'll get**:
|
||||
- Auth0 Dashboard access
|
||||
- Domain: `YOUR_DOMAIN.auth0.com`
|
||||
- Client ID & Secret for dashboard
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Select Application Type
|
||||
|
||||
**Choose based on your tech stack**:
|
||||
|
||||
#### Option 1: Single Page Application (SPA)
|
||||
**For**: React, Vue, Angular, Svelte, Next.js (client-side)
|
||||
|
||||
**Characteristics**:
|
||||
- Code runs in browser
|
||||
- Public client (no secret needed)
|
||||
- OAuth Flow: Authorization Code + PKCE
|
||||
- Token Storage: In-memory (secure)
|
||||
|
||||
**Setup**:
|
||||
```
|
||||
Auth0 Dashboard → Applications → Create Application
|
||||
Name: My React App
|
||||
Type: Single Page Application
|
||||
|
||||
Settings:
|
||||
- Allowed Callback URLs: http://localhost:3000/callback
|
||||
- Allowed Logout URLs: http://localhost:3000
|
||||
- Allowed Web Origins: http://localhost:3000
|
||||
- Token Endpoint Authentication: None
|
||||
```
|
||||
|
||||
#### Option 2: Web Application (Server-Side)
|
||||
**For**: Next.js, Express, Django, Rails (with server)
|
||||
|
||||
**Characteristics**:
|
||||
- Code runs on server
|
||||
- Confidential client (has secret)
|
||||
- OAuth Flow: Authorization Code (no PKCE needed)
|
||||
- Token Storage: HTTP-only cookies
|
||||
|
||||
**Setup**:
|
||||
```
|
||||
Auth0 Dashboard → Applications → Create Application
|
||||
Name: My Next.js App
|
||||
Type: Regular Web Applications
|
||||
|
||||
Settings:
|
||||
- Allowed Callback URLs: http://localhost:3000/api/auth/callback
|
||||
- Allowed Logout URLs: http://localhost:3000
|
||||
- Token Endpoint Authentication: Post
|
||||
- Secret: [Auto-generated, copy to .env]
|
||||
```
|
||||
|
||||
#### Option 3: Machine-to-Machine (M2M)
|
||||
**For**: Backend services, scheduled jobs, CLI tools
|
||||
|
||||
**Characteristics**:
|
||||
- No user interaction
|
||||
- Confidential client
|
||||
- OAuth Flow: Client Credentials
|
||||
- Use Case: Calling Auth0 Management API
|
||||
|
||||
**Setup**:
|
||||
```
|
||||
Auth0 Dashboard → Applications → Create Application
|
||||
Name: My Backend Service
|
||||
Type: Machine-to-Machine Applications
|
||||
|
||||
Settings:
|
||||
- Grant Types: client_credentials
|
||||
- Audience: https://YOUR_DOMAIN/api/v2/
|
||||
- Secret: [Auto-generated, copy to .env]
|
||||
```
|
||||
|
||||
#### Option 4: Native Application
|
||||
**For**: iOS, Android, React Native apps
|
||||
|
||||
**Characteristics**:
|
||||
- Runs on device
|
||||
- Public client (no secret)
|
||||
- OAuth Flow: Authorization Code + PKCE
|
||||
- Callback: Custom URL scheme or App Link
|
||||
|
||||
**Setup**:
|
||||
```
|
||||
Auth0 Dashboard → Applications → Create Application
|
||||
Name: My Mobile App
|
||||
Type: Native
|
||||
|
||||
Settings:
|
||||
- Allowed Callback URLs: com.myapp://callback
|
||||
- Allowed Logout URLs: com.myapp://logout
|
||||
- Token Endpoint Authentication: None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Configure Connections
|
||||
|
||||
Choose how users authenticate:
|
||||
|
||||
#### Option 1: Username/Password Database
|
||||
**Built-in Auth0 database**
|
||||
|
||||
**Enable**:
|
||||
```
|
||||
Auth0 Dashboard → Connections → Database → Username-Password-Authentication
|
||||
|
||||
Settings:
|
||||
- Allow Signup: Yes (if you want user self-registration)
|
||||
- Require Email Verification: Yes
|
||||
- Password Policy: Good (mixed case, numbers, symbols)
|
||||
- Disable signup for: [Your application]
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
```
|
||||
Users can sign up at: YOUR_DOMAIN/signup
|
||||
Or you create users via API
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Option 2: Social Connections
|
||||
**Let users login with Google, GitHub, etc.**
|
||||
|
||||
##### Google OAuth Setup
|
||||
|
||||
1. **Create Google Cloud project**:
|
||||
- Visit: https://console.cloud.google.com
|
||||
- Create new project: "My App Auth"
|
||||
- Enable Google+ API
|
||||
|
||||
2. **Create OAuth credentials**:
|
||||
- APIs & Services → Credentials → Create OAuth 2.0 Client ID
|
||||
- Authorized redirect URIs:
|
||||
- `https://YOUR_DOMAIN/login/callback`
|
||||
- `https://YOUR_DOMAIN/login/callback?connection=google-oauth2`
|
||||
- Copy: Client ID and Client Secret
|
||||
|
||||
3. **Add to Auth0**:
|
||||
```
|
||||
Auth0 Dashboard → Connections → Social → Google
|
||||
|
||||
Client ID: [Paste from Google Cloud]
|
||||
Client Secret: [Paste from Google Cloud]
|
||||
|
||||
Enable for: [Your application]
|
||||
```
|
||||
|
||||
##### GitHub OAuth Setup
|
||||
|
||||
1. **Create GitHub OAuth App**:
|
||||
- Visit: https://github.com/settings/developers
|
||||
- Register new OAuth application
|
||||
- Authorization callback URL:
|
||||
- `https://YOUR_DOMAIN/login/callback`
|
||||
- Copy: Client ID and Client Secret
|
||||
|
||||
2. **Add to Auth0**:
|
||||
```
|
||||
Auth0 Dashboard → Connections → Social → GitHub
|
||||
|
||||
Client ID: [Paste from GitHub]
|
||||
Client Secret: [Paste from GitHub]
|
||||
|
||||
Enable for: [Your application]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Option 3: Enterprise Connections (Active Directory/LDAP)
|
||||
**For company employees using company email**
|
||||
|
||||
**Setup Active Directory**:
|
||||
```
|
||||
Auth0 Dashboard → Connections → Enterprise → Active Directory/LDAP
|
||||
|
||||
Name: Company AD
|
||||
LDAP URL: ldap://ad.company.com:389
|
||||
Bind DN: admin@company.com
|
||||
Bind Password: [AD admin password]
|
||||
Base DN: cn=users,dc=company,dc=com
|
||||
|
||||
Mapping:
|
||||
- Email: mail
|
||||
- Name: displayName
|
||||
- Username: sAMAccountName
|
||||
|
||||
Enable for: [Your application]
|
||||
```
|
||||
|
||||
**Result**: Users can login with company credentials, no password duplication
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Create API
|
||||
|
||||
If your frontend needs to call protected APIs:
|
||||
|
||||
```
|
||||
Auth0 Dashboard → APIs → Create API
|
||||
|
||||
Name: My API
|
||||
Identifier: https://api.myapp.com
|
||||
Signing Algorithm: RS256
|
||||
|
||||
Scopes:
|
||||
+ read:items (Read access to items)
|
||||
+ write:items (Write access to items)
|
||||
+ delete:items (Delete items)
|
||||
+ admin (Full admin access)
|
||||
```
|
||||
|
||||
**Usage in App**:
|
||||
```typescript
|
||||
// Request these scopes during login
|
||||
Auth0Provider({
|
||||
authorizationParams: {
|
||||
scope: 'openid profile email read:items write:items'
|
||||
}
|
||||
})
|
||||
|
||||
// Access token will include these scopes
|
||||
// Backend validates scopes before allowing access
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 5: Basic Security Configuration
|
||||
|
||||
#### Enable Multi-Factor Authentication (MFA)
|
||||
|
||||
```
|
||||
Auth0 Dashboard → Connections → Authenticators
|
||||
|
||||
Enable:
|
||||
- Google Authenticator ✅
|
||||
- SMS (optional)
|
||||
- Email OTP
|
||||
|
||||
For application:
|
||||
- Require MFA: Yes / No / Per-user rule
|
||||
```
|
||||
|
||||
**Optional**: Require MFA for certain users:
|
||||
```javascript
|
||||
// Auth0 Rule: Enforce MFA for admin users
|
||||
module.exports = function(user, context, callback) {
|
||||
if (user.email.endsWith('@admin.company.com')) {
|
||||
context.multifactor = {
|
||||
provider: 'google-authenticator',
|
||||
allowRememberBrowser: false
|
||||
}
|
||||
}
|
||||
callback(null, user, context)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Set Token Expiration
|
||||
|
||||
```
|
||||
Auth0 Dashboard → Applications → Settings → Advanced
|
||||
|
||||
Token Settings:
|
||||
- ID Token Expiration: 36000 (10 hours)
|
||||
- Access Token Expiration: 600 (10 minutes) ← IMPORTANT
|
||||
- Refresh Token Expiration: 2592000 (30 days)
|
||||
- Refresh Token Rotation: Enabled ✅
|
||||
```
|
||||
|
||||
**Why short access token?**
|
||||
- If leaked, attacker has limited time window
|
||||
- Refresh tokens can be rotated automatically
|
||||
- Best practice: 5-15 minutes
|
||||
|
||||
---
|
||||
|
||||
#### Enable HTTPS for Callback URLs
|
||||
|
||||
```
|
||||
Auth0 Dashboard → Applications → Settings
|
||||
|
||||
Allowed Callback URLs:
|
||||
- Production: https://myapp.com/callback ✅
|
||||
- Staging: https://staging.myapp.com/callback ✅
|
||||
- Local dev: http://localhost:3000/callback (only for dev)
|
||||
|
||||
Allowed Logout URLs:
|
||||
- Production: https://myapp.com ✅
|
||||
- Staging: https://staging.myapp.com ✅
|
||||
- Local dev: http://localhost:3000 (only for dev)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 6: Generate Environment Variables
|
||||
|
||||
Based on your setup, create `.env.local`:
|
||||
|
||||
```env
|
||||
# Auth0 Configuration
|
||||
AUTH0_DOMAIN=YOUR_DOMAIN.auth0.com
|
||||
AUTH0_CLIENT_ID=YOUR_CLIENT_ID
|
||||
AUTH0_CLIENT_SECRET=YOUR_CLIENT_SECRET
|
||||
AUTH0_CALLBACK_URL=http://localhost:3000/callback
|
||||
|
||||
# For Next.js
|
||||
AUTH0_BASE_URL=http://localhost:3000
|
||||
AUTH0_SECRET=use [node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"]
|
||||
|
||||
# For APIs
|
||||
AUTH0_AUDIENCE=https://api.myapp.com
|
||||
AUTH0_SCOPE=openid profile email read:items
|
||||
|
||||
# API Configuration
|
||||
API_URL=http://localhost:3001
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### URLs to Save
|
||||
|
||||
- **Auth0 Dashboard**: https://manage.auth0.com
|
||||
- **Your Tenant**: https://YOUR_DOMAIN.auth0.com
|
||||
- **Login Page**: https://YOUR_DOMAIN.auth0.com/login
|
||||
|
||||
### Common Task Commands
|
||||
|
||||
**View users**:
|
||||
```
|
||||
Auth0 Dashboard → User Management → Users
|
||||
```
|
||||
|
||||
**View logs**:
|
||||
```
|
||||
Auth0 Dashboard → Logs (Real-time logs of auth events)
|
||||
```
|
||||
|
||||
**View rules**:
|
||||
```
|
||||
Auth0 Dashboard → Rules (Custom logic for auth flow)
|
||||
```
|
||||
|
||||
**Reset user password**:
|
||||
```
|
||||
Auth0 Dashboard → Users → Select user → Actions → Reset password
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
- [ ] Auth0 tenant created
|
||||
- [ ] Application configured (SPA/Web/M2M/Native)
|
||||
- [ ] Callback URLs set (http://localhost:3000/callback)
|
||||
- [ ] Logout URLs set (http://localhost:3000)
|
||||
- [ ] Connection enabled (Database/Google/GitHub/AD)
|
||||
- [ ] API created with scopes
|
||||
- [ ] MFA configured
|
||||
- [ ] Token expiration set (10 min for access tokens)
|
||||
- [ ] Environment variables generated
|
||||
- [ ] Test login from Auth0 dashboard works
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
After setup completes:
|
||||
|
||||
1. **Install SDK for your framework**:
|
||||
- React: `/oauth-implement react`
|
||||
- Next.js: `/oauth-implement nextjs`
|
||||
- Node.js: `/oauth-implement nodejs`
|
||||
|
||||
2. **Run security audit**:
|
||||
- `/oauth-security-audit`
|
||||
|
||||
3. **Test your implementation**:
|
||||
- Try login flow
|
||||
- Check token in browser dev tools
|
||||
- Verify API access
|
||||
|
||||
4. **Deploy to production**:
|
||||
- Update callback URLs to production domain
|
||||
- Update environment variables
|
||||
- Enable HTTPS everywhere
|
||||
- Consider Auth0 compliance (GDPR, HIPAA, SOC2)
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Q: Callback URL mismatch error?**
|
||||
A: Ensure your callback URL in Auth0 exactly matches redirect_uri in your app (including http/https and port)
|
||||
|
||||
**Q: Connection not showing in login?**
|
||||
A: Check that connection is enabled for your application (Connections → Select connection → Applications toggle)
|
||||
|
||||
**Q: Can't create users via signup?**
|
||||
A: Ensure "Allow Signup" is enabled in Database connection settings
|
||||
|
||||
**Q: Users can't login with social connection?**
|
||||
A: Verify social connection is enabled for your application
|
||||
|
||||
---
|
||||
|
||||
**Status**: Setup wizard complete!
|
||||
**Next command**: `/oauth-implement [framework]` to add auth to your app
|
||||
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