Scenario — Inspecting Authentication
🔐 Lesson 8 — Scenario: Inspecting Authentication
A scenario lesson that applies the tools from lessons 1–7 to one real task: sit down in front of any web application and determine exactly how it authenticates users, read the tokens it sends, trace the full login-to-API flow, and diagnose why authentication is failing.
2. Press Escape to open the Console drawer at the bottom — now you can watch requests and run JS at the same time.
3. Enable Persist Logs in the Network toolbar. Authentication involves redirects — without this you lose the login request the moment the redirect fires.
Different sites use different approaches. Filter the Network panel by XHR/Fetch and look for these signals:
access_tokenAPI requests carry
Authorization: Bearer eyJ…Token stored in localStorage or a cookie
Token is a three-part base64 string
Browser redirects to third-party URL
Returns with
?code=… in URLBackend exchanges code for tokens
Authorization orcustom header
X-Api-Key, orquery parameter
?api_key=…No login flow — key used on every request
With Persist Logs enabled, log out, then log back in and watch the Network panel. Here is what a complete JWT login sequence looks like:
access_token and refresh_token.Authorization: Bearer eyJ…Reading the Login Request
Click the login request → Request tab to see what was sent:
"email": "alice@example.com",
"password": "••••••••" never stored in plain text
}
Reading the Login Response
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTczMDAwMDAwMH0.Xk3Q" ← JWT,
"token_type": "Bearer",
"expires_in": 3600
}
A JWT is three base64url-encoded sections separated by dots. The header and payload are readable by anyone — they are signed, not encrypted.
"alg": "HS256",
"typ": "JWT"
}
"sub": "42",
"role": "admin",
"exp": 1730000000
}
Decode any JWT in the Console — no third-party tool needed:
// Paste the token from the Authorization header or localStorage
const token = localStorage.getItem('access_token');
const payload = JSON.parse(atob(token.split('.')[1]));
console.table([payload]);
// Check expiry
console.log('Expires:', new Date(payload.exp * 1000).toLocaleString());
console.log('Expired?', Date.now() > payload.exp * 1000);
HttpOnly flag present on session cookieSecure flag present — cookie only sent over HTTPSSameSite=Lax or Strict — not None without SecureMax-Age or Expires — session doesn't live foreverexp claim is reasonable — hours, not yearsAuthorization present in the request?Authorization → client isn't sending credentials. Check localStorage/cookies for the token.Authorization present but 401 → token is invalid or expired. Decode exp in the Console."token_expired", "invalid_signature"role or scope claim — does it include the required permission?"insufficient_scope"Access-Control-Allow-Origin present and matching?Access-Control-Allow-Headers include Authorization? A wildcard origin doesn't help if the auth header is blocked.OPTIONS preflight request — what status did it return?Location header on each redirect — where is the server sending the browser?Annotated 401 Response
📋 Copy as cURL — Replay Any Request
curl command with all headers — including the Authorization token — ready to paste into a terminal or import into Postman for testing outside the browser.
# What "Copy as cURL" produces — paste directly into a terminal
curl 'https://api.example.com/api/user/profile' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0MiJ9.Xk3Q' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
✏️ Exercises
-
Identify the mechanism. Open the Network panel on a site you're logged into. Filter by XHR/Fetch. Click an API request → Headers. Does it use
Cookie,Authorization: Bearer, or something else? -
Decode a JWT. If the site uses Bearer auth, copy the token value and run this in the Console:
const t = 'PASTE_TOKEN_HERE'; console.table(JSON.parse(atob(t.split('.')[1])));What claims does the payload contain? When does it expire? - Find the login request. Enable Persist Logs. Log out and log back in. Find the login POST in the Network panel. Click Request tab — what format is the body? What does the Response tab contain?
- Audit cookie flags. Open Storage → Cookies. Check the session cookie. Does it have HttpOnly, Secure, and SameSite set? Note anything missing.
- Simulate a 401. Edit the session cookie or localStorage token to an invalid string. Trigger an API call. Does the server return 401? Does the app handle it gracefully (redirect to login, show an error)?
-
Test logout completeness. Log out. Open Storage → check Cookies and localStorage. Are all tokens gone? Navigate directly to a protected URL (
/dashboardor similar). Are you redirected to login? - Copy as cURL. Right-click any authenticated API request → Copy Value → Copy as cURL. Paste it into a terminal or note it. Can you see the exact auth token that was used?
📌 Key Takeaways
- Enable Persist Logs before any login flow — the login request is gone the moment the redirect fires without it.
- Session cookies show a
Set-Cookieon the login response; JWT shows a token in the response body andAuthorization: Beareron subsequent calls. - Decode any JWT in the Console with
JSON.parse(atob(token.split('.')[1]))— no external tools needed. - 401 = credentials missing or invalid; 403 = credentials valid but wrong role/scope — always check the Response body for the server's error message.
- CORS errors on authenticated requests usually mean
Access-Control-Allow-Headersis missingAuthorization— a server-side misconfiguration. - Copy as cURL (right-click any request) gives you an instantly replayable command with all headers — invaluable for testing APIs outside the browser.
- After logout, open the Storage panel — all tokens and session cookies must be gone; any that remain after logout are a security bug.