Install
openclaw skills install oauth-debuggerDebug OAuth 2.0 and OIDC flows. Trace authorization code, PKCE, client credentials, and implicit flows. Diagnose redirect URI mismatches, scope issues, token exchange failures, and JWKS configuration problems.
openclaw skills install oauth-debuggerDebug OAuth 2.0 and OpenID Connect flows without guessing. Trace each step of the authorization flow, identify where it breaks (redirect URI mismatch, scope issue, token exchange failure, JWKS misconfiguration), and provide the exact fix.
Use when: "oauth not working", "redirect URI mismatch", "invalid_grant", "login flow broken", "OIDC configuration", "token exchange failing", "PKCE error", "authorization code error", or when SSO/social login breaks.
trace — Trace OAuth Flow Step by Step# OpenID Connect discovery
curl -s "https://$AUTH_DOMAIN/.well-known/openid-configuration" | python3 -c "
import json, sys
config = json.load(sys.stdin)
print('Authorization endpoint:', config.get('authorization_endpoint', '❌ MISSING'))
print('Token endpoint:', config.get('token_endpoint', '❌ MISSING'))
print('JWKS URI:', config.get('jwks_uri', '❌ MISSING'))
print('Supported flows:', config.get('grant_types_supported', ['not listed']))
print('Supported scopes:', config.get('scopes_supported', ['not listed']))
print('Token signing:', config.get('id_token_signing_alg_values_supported', ['not listed']))
"
# Check JWKS endpoint
curl -s "https://$AUTH_DOMAIN/.well-known/jwks.json" | python3 -c "
import json, sys
jwks = json.load(sys.stdin)
for key in jwks.get('keys', []):
print(f'Key ID: {key.get(\"kid\")} | Algorithm: {key.get(\"alg\")} | Use: {key.get(\"use\")}')
"
For Authorization Code + PKCE flow:
# Check the authorization URL construction
# Required parameters:
# - response_type=code
# - client_id=<your app>
# - redirect_uri=<must match exactly>
# - scope=<requested scopes>
# - state=<CSRF protection>
# - code_challenge=<PKCE S256 hash>
# - code_challenge_method=S256
python3 -c "
import hashlib, base64, secrets
verifier = secrets.token_urlsafe(32)
challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).rstrip(b'=').decode()
print(f'code_verifier: {verifier}')
print(f'code_challenge: {challenge}')
print(f'code_challenge_method: S256')
"
# Test token exchange
curl -s -X POST "https://$AUTH_DOMAIN/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=$AUTH_CODE" \
-d "redirect_uri=$REDIRECT_URI" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "code_verifier=$CODE_VERIFIER" | python3 -c "
import json, sys
resp = json.load(sys.stdin)
if 'access_token' in resp:
print('✅ Token exchange successful')
print(f'Access token type: {resp.get(\"token_type\")}')
print(f'Expires in: {resp.get(\"expires_in\")}s')
print(f'Scopes: {resp.get(\"scope\")}')
if 'id_token' in resp:
print(f'ID token: present')
if 'refresh_token' in resp:
print(f'Refresh token: present')
else:
print(f'❌ Error: {resp.get(\"error\")}')
print(f'Description: {resp.get(\"error_description\")}')
"
diagnose — Common OAuth Errorsinvalid_grant:
redirect_uri_mismatch:
http://localhost:3000 ≠ http://localhost:3000/http ≠ httpslocalhost:3000 ≠ localhost:3001/callback ≠ /auth/callbackinvalid_client:
invalid_scope:
email → openid email)access_denied:
# OAuth Debug Report
## Flow: Authorization Code + PKCE
## Provider: Auth0 (tenant.auth0.com)
## Trace
1. ✅ Discovery: .well-known/openid-configuration found
2. ✅ Authorization request: valid parameters
3. ✅ User authenticated and consented
4. ❌ Token exchange: `invalid_grant`
## Diagnosis
- Redirect URI in token request: `http://localhost:3000/callback`
- Redirect URI in authorize request: `http://localhost:3000/callback/`
- 🔴 **Trailing slash mismatch** — must be identical in both requests
## Fix
Change redirect_uri in token request to `http://localhost:3000/callback/` (with trailing slash)
OR update authorize request to omit trailing slash.
Also update provider's registered redirect URIs to match.
test-flow — Simulate OAuth FlowsClient Credentials (machine-to-machine):
curl -s -X POST "https://$AUTH_DOMAIN/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "audience=$API_AUDIENCE"
Refresh Token:
curl -s -X POST "https://$AUTH_DOMAIN/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=$REFRESH_TOKEN" \
-d "client_id=$CLIENT_ID"
security-check — Audit OAuth ImplementationFlag security issues: