Skill: charlie-morrison/jwt-debugger (version 1.0.1)

--- name: jwt-debugger description: Decode, validate, and debug JSON Web Tokens. Inspect headers, payloads, signatures, expiration, claims, and key mismatches. Diagnose common JWT issues in authentication flows without external tools. --- # JWT Debugger Decode and debug JWTs without pasting them into random websites. Inspect headers, validate signatures, check expiration and claims, diagnose key mismatches, and trace authentication failures — all locally, keeping tokens secure. Use when: "debug jwt", "decode this token", "why is auth failing", "jwt expired", "invalid signature", "token validation error", "check jwt claims", or when troubleshooting authentication. ## Commands ### 1. `decode` — Decode and Inspect JWT ```bash # Decode without signature verification (inspection only) echo "$TOKEN" | python3 -c " import sys, json, base64 [REDACTED_SECRET]).strip() parts = token.split('.') if len(parts) != 3: print(f'❌ Invalid JWT: expected 3 parts, got {len(parts)}') sys.exit(1) def decode_part(s): padding = 4 - len(s) % 4 s += '=' * padding return json.loads(base64.urlsafe_b64decode(s)) header = decode_part(parts[0]) payload = decode_part(parts[1]) print('=== HEADER ===') print(json.dumps(header, indent=2)) print() print('=== PAYLOAD ===') print(json.dumps(payload, indent=2)) print() # Check expiration import time if 'exp' in payload: exp = payload['exp'] now = int(time.time()) remaining = exp - now if remaining < 0: print(f'🔴 EXPIRED: {abs(remaining)//3600}h {abs(remaining)%3600//60}m ago') elif remaining < 300: print(f'🟡 EXPIRING SOON: {remaining}s remaining') else: print(f'🟢 Valid: {remaining//3600}h {remaining%3600//60}m remaining') if 'iat' in payload: from datetime import datetime, timezone issued = datetime.fromtimestamp(payload['iat'], tz=timezone.utc) print(f'Issued at: {issued.isoformat()}') if 'nbf' in payload: nbf = payload['nbf'] now = int(time.time()) if now < nbf: print(f'🔴 NOT YET VALID: becomes valid in {nbf - now}s') # Common claims for claim, label in [('sub', 'Subject'), ('iss', 'Issuer'), ('aud', 'Audience'), ('scope', 'Scopes'), ('roles', 'Roles')]: if claim in payload: print(f'{label}: {payload[claim]}') print(f'\\nAlgorithm: {header.get(\"alg\", \"MISSING\")}') print(f'Key ID: {header.get(\"kid\", \"not set\")}') print(f'Type: {header.get(\"typ\", \"not set\")}') " ``` ### 2. `validate` — Full Signature Verification ```bash # Verify with known secret (HS256) python3 -c " import sys, hmac, hashlib, base64 [REDACTED_SECRET]' [REDACTED_SECRET]' parts = token.split('.') signing_input = f'{parts[0]}.{parts[1]}'.encode() signature = base64.urlsafe_b64decode(parts[2] + '==') expected = hmac.new(secret.encode(), signing_input, hashlib.sha256).digest() if hmac.compare_digest(signature, expected): print('✅ Signature VALID') else: print('❌ Signature INVALID — secret mismatch or token tampered') " # Verify with public key (RS256/ES256) python3 -c " import jwt # pip install PyJWT[crypto] try: decoded = jwt.decode('$TOKEN', '$PUBLIC_KEY', algorithms=['RS256'], audience='$EXPECTED_AUD') print('✅ Valid:', decoded) except jwt.ExpiredSignatureError: print('❌ Token expired') except jwt.InvalidSignatureError: print('❌ Invalid signature — wrong key or tampered token') except jwt.InvalidAudienceError: print('❌ Audience mismatch') except Exception as e: print(f'❌ {e}') " ``` ### 3. `diagnose` — Common JWT Problems Check for these issues: **Expiration issues:** - Token expired → check clock sync between issuer and validator - Token not yet valid (nbf) → clock skew between services - Very short TTL (< 5 min) → may cause issues with slow requests **Signature issues:** - "none" algorithm → security vulnerability, reject immediately - Algorithm mismatch → server expects RS256, token has HS256 - Wrong key → key rotation happened, old key used - kid mismatch → key ID in header doesn't match available keys **Claims issues:** - Missing required claims (iss, sub, aud, exp) - Audience mismatch → token issued for service A, used with service B - Issuer mismatch → token from wrong identity provider - Scope insufficient → token has read scope, endpoint requires write **Security red flags:** - `alg: "none"` → algorithm confusion attack - `alg: "HS256"` with RSA public key → key confusion attack - Token in URL query parameter → logged in server logs, browser history - Token size > 8KB → may exceed header size limits - Sensitive data in payload (passwords, SSN) → payload is base64, not encrypted ```markdown # JWT Diagnostic Report ## Token Summary - Algorithm: RS256 - Issuer: auth.example.com - Subject: user-12345 - Issued: 2026-04-29 01:00:00 UTC - Expires: 2026-04-29 02:00:00 UTC (🔴 EXPIRED 31m ago) ## Issues Found 1. 🔴 **Expired** — token expired 31 minutes ago Fix: refresh token or re-authenticate 2. 🟡 **No audience claim** — token doesn't specify intended audience Risk: token accepted by unintended services Fix: add `aud` claim to token issuer config 3. 🟢 Algorithm: RS256 (secure) 4. 🟢 Key ID present: matches current JWKS ``` ### 4. `compare` — Diff Two Tokens Compare tokens side-by-side to identify what changed: - Different claims (permissions changed?) - Different expiry (session settings changed?) - Different issuer (wrong auth provider?) - Different kid (key rotated?) ### 5. `generate` — Create Test JWT Generate a signed JWT for testing: ```bash python3 -c " import jwt, time, json payload = { 'sub': 'test-user', 'iss': 'test-issuer', 'aud': 'test-audience', 'iat': int(time.time()), 'exp': int(time.time()) + 3600, 'scope': 'read write' } [REDACTED_SECRET], 'test-secret', algorithm='HS256') print(token) " ```


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)