█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/Programming Languages/REST APIs/Authentication in REST APIs
55 minIntermediate

Authentication in REST APIs

After this lesson, you will be able to: Implement API keys, Bearer tokens, JWT, and OAuth 2.0 flows correctly.

Authentication is where APIs get insecure. Use the standard patterns; don't invent your own.

Prerequisites:Designing a REST API

API keys

A long random string. Sent in `Authorization: Bearer <key>` or a custom header (`X-API-Key: ...`). Use for: server-to-server, public read APIs, third-party integrations. Rotate them. Store them hashed (like passwords) so a DB breach doesn't expose them. NEVER ship API keys to the browser, they're meant for backend-to-backend.

JWT — JSON Web Tokens

Signed (or encrypted) tokens. Stateless auth.

css
// Structure: header.payload.signature, all base64-encoded
// Example payload (decoded):
{
"sub": "user_42", // subject (user id)
"role": "admin",
"iat": 1716777600, // issued at (unix time)
"exp": 1716781200, // expires at (1 hr later)
"iss": "myapp.com" // issuer
}
// Server signs the payload with a secret. Client cannot tamper without invalidating the signature.
// On each request:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
// Server verifies signature + expiry; trusts the payload.
// Pros: stateless (no DB lookup per request).
// Cons: can't revoke before expiry without a denylist (which defeats statelessness).

JWT implementation (Node + jose)

Modern, no-dependency lib.

python
// npm i jose
import * as jose from 'jose';
const secret = new TextEncoder().encode(process.env.JWT_SECRET!);
// Sign
const token = await new jose.SignJWT({ sub: 'user_42', role: 'admin' })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('1h')
.setIssuer('myapp.com')
.sign(secret);
// Verify
try {
const { payload } = await jose.jwtVerify(token, secret, {
issuer: 'myapp.com',
});
// payload.sub, payload.role available
} catch (e) {
// expired or tampered
}

OAuth 2.0 flows

Authorization Code Flow with PKCE: the modern default for web + mobile + SPA. User clicks 'Sign in with Google,' redirected to Google, authorizes, redirected back with an authorization code, server exchanges code for access + refresh tokens. Client Credentials Flow: server-to-server (no user). Service A sends client_id + secret, gets token, calls service B. Refresh Token Flow: short-lived access tokens (~1 hour) + long-lived refresh tokens (~30 days). Client uses refresh token to silently get a new access token.

💡 Where to store tokens in browsers

BAD: localStorage, vulnerable to XSS (any JS on your origin reads it). BETTER: httpOnly + Secure + SameSite=Strict cookies, JS can't read them, CSRF mitigated. BEST: short-lived JWT in memory + refresh token in httpOnly cookie. For LastWrite-style apps: NextAuth handles this, JWT in httpOnly session cookie. Don't roll your own.

Common mistakes

API keys in client-side JS / mobile app binaries (extractable in seconds). JWTs in localStorage (XSS = total takeover). Skipping `exp` claim → tokens valid forever. Verifying JWTs with the WRONG algorithm (algorithm confusion attack, always explicitly set + check alg). Storing refresh tokens unhashed in DB (DB breach = persistent account access).

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←Designing a REST API
Back to REST APIs
Request Validation and Error Responses→