Reference · gazar.dev ← All cheat sheets

Security · identity

The security & auth cheat sheet.

Everything security and identity for a system design, on one page, with the flows drawn out. Authentication vs authorization; how identity is carried (server sessions, self-contained JWTs, reference tokens); the protocols (OAuth 2.1, OpenID Connect, SAML, passkeys); how a request's identity context propagates across microservices (mTLS, SPIFFE, token exchange); the authorization models (RBAC, ABAC, ReBAC); plus password storage, encryption in transit and at rest, and the common web attacks (injection, XSS, CSRF, SSRF, IDOR) with their defenses. Sequence diagrams for each. Companion to S5 · API Design & Networking.

01

Authentication vs authorization vs identity

Two different questions people constantly merge. Authentication (authN) establishes who the caller is. Authorization (authZ) decides what that caller may do. Identity is proven once, then carried as a session or token and re-checked on every request.

TermIsIn practice
Principal / subjectthe entity making the requesta user, a device, or a service (a workload)
Credentialthe secret that proves identitypassword, private key, client secret, passkey
Sessionserver-side record of a logged-in principala row keyed by a cookie id
Tokena portable, verifiable proof of identity + rightsJWT (self-contained) or opaque (a reference)
Claima fact asserted inside a tokensub, email, roles, scope
Scopea coarse permission granted to a clientread:orders, openid

The trap

Solving authZ with authN. "The user is logged in" does not mean "the user may delete this record." A valid token answers who; you still owe a separate what check (roles, scopes, ownership) on every sensitive action. IDOR bugs are exactly this gap: authenticated, but never authorized for that specific object.

02

How identity is carried: session vs self-contained vs reference token

The central design axis. Once a principal is authenticated, something must carry that fact to the next request and the next service. There are three shapes, and the whole rest of this sheet is variations on them.

ShapeState livesValidated byRevokeCost
Server sessionserver (session store)look up the idinstant, delete the rowa store read per request; needs shared store to scale
Self-contained (JWT)the token itselfverify signature locallyhard, valid until it expiresno lookup; larger payload; keep TTL short
Reference / opaqueserver (auth server)introspect at the issuerinstant, revoke at issuera network call to validate, or cache it
03

Session-based auth (stateful, cookie-carried)

The classic web model. Log in once, the server stores a session and hands back a cookie holding only an opaque session id. Every later request carries the cookie; the server looks up the session. Simple, instantly revocable, and the default for server-rendered apps.

sequenceDiagram
    autonumber
    actor U as User
    participant B as Browser
    participant S as Server
    participant DB as Session store
    U->>B: enter credentials
    B->>S: POST /login with username and password
    S->>S: verify password with argon2 or bcrypt
    S->>DB: create session, store server-side
    S-->>B: 200, Set-Cookie sid HttpOnly Secure SameSite
    Note over B,S: every later request carries the cookie
    B->>S: GET /account with Cookie sid
    S->>DB: look up session by sid
    DB-->>S: session gives user id and roles
    S-->>B: 200 protected data
The cookie holds no data, only a random id; all state stays server-side, so logout or a security event can kill the session instantly by deleting the row.

Cookie flags are the security

HttpOnly (JavaScript can't read it, blunts XSS theft), Secure (HTTPS only), SameSite=Lax/Strict (blunts CSRF), and the __Host- prefix (locks it to the exact origin). To scale beyond one server, the session store must be shared (Redis), or you sign a stateless session cookie and accept you cannot revoke it before expiry.

04

JWT: structure, claims, and validation

A JSON Web Token is three base64url parts joined by dots: a header, a payload of claims, and a signature. Anyone can read it; only the issuer can sign it. Validation is a local check, which is what makes JWTs fast and stateless.

header.payload.signature      # three base64url parts, dot-separated

header   { "alg": "RS256", "kid": "k1", "typ": "JWT" }
payload  { "iss": issuer,  "sub": user id,  "aud": your api,
           "exp": expiry,  "iat": issued,   "scope": "read:orders",
           "jti": token id }                       # claims, readable by anyone
signature = sign( base64(header) + "." + base64(payload), key )
flowchart TB
  T["incoming request with a JWT"] --> A["fetch signing key from JWKS by kid"]
  A --> B["verify signature, RS256 or ES256"]
  B --> C{signature valid?}
  C -->|no| X["401 reject"]
  C -->|yes| D["check iss, aud, exp, nbf"]
  D --> E{claims ok?}
  E -->|no| X
  E -->|yes| F["check scope or roles for this route"]
  F --> G["allow, set security context"]
  classDef good fill:#D6F5E3,stroke:#1B7F46,color:#09244B
  classDef bad fill:#FEE4E2,stroke:#C8102E,color:#09244B
  class G good
  class X bad
Validation order matters: signature first (is it authentic?), then claims (is it for me, still valid?), then authorization (does this scope allow this route?).

Two rules that catch most JWT mistakes

1. A JWT is signed, not encrypted. The payload is plain base64, readable by anyone, so never put secrets or PII in it. 2. Prefer asymmetric signing (RS256 / ES256): the auth server holds the private key, every service verifies with the public key from the JWKS endpoint, so no shared secret is spread around. Pin the algorithm on verify, never trust the token's own alg (that is the alg=none and RS/HS confusion attack).

05

Access & refresh tokens (rotation and revocation)

Because a JWT can't be revoked mid-life, you keep the access token short-lived (minutes) and pair it with a long-lived, revocable refresh token that mints new access tokens. Rotating the refresh token on every use, and detecting reuse, is how you make a stateless scheme safely revocable.

sequenceDiagram
    autonumber
    participant C as Client
    participant AS as Auth server
    participant API as Resource server
    C->>API: request with access token
    API-->>C: 401, access token expired
    C->>AS: POST /token grant_type refresh_token
    AS->>AS: validate refresh token, check for reuse
    AS-->>C: new short access token and rotated refresh token
    Note over AS: the old refresh token is now invalid
    C->>API: retry with the new access token
    API-->>C: 200 data
Refresh-token rotation: each refresh returns a new refresh token and invalidates the old one. If an old (already-used) refresh token appears again, that signals theft, so the whole token family is revoked.
06

Opaque tokens, introspection & the phantom-token pattern

An opaque token is a random string that means nothing on its own; the resource server asks the issuer to introspect it. That gives instant revocation and leaks no data outward, at the cost of a network call. The phantom-token pattern gets both: an opaque token outside, a JWT inside.

sequenceDiagram
    autonumber
    participant C as Client
    participant GW as API gateway
    participant AS as Auth server
    participant SVC as Microservice
    C->>GW: request with an opaque token
    GW->>AS: introspect or exchange the opaque token
    AS-->>GW: valid plus claims, issue a short-lived JWT
    GW->>SVC: forward request with the JWT
    SVC->>SVC: verify the JWT locally, no network call
    SVC-->>C: response
The client and the public internet only ever see an opaque, revocable token; the gateway swaps it for a signed JWT that internal services verify locally. The related split-token pattern avoids the introspection call by caching, keyed on the token's signature.

Why this is the common enterprise choice

Opaque outward means no claims leak to the browser and you can revoke on logout instantly. JWT inward means internal services validate with a local signature check and never call the auth server on the hot path. You spend one exchange at the edge to buy privacy plus revocation outside and speed inside.

07

OAuth 2.1: roles and grant types

OAuth is a delegated authorization framework: it lets an app act on a resource on your behalf without ever seeing your password. Four roles, and a small set of grant types (flows). OAuth 2.1 is the consolidation that bakes in a decade of security lessons.

The four roles

  • Resource owner: the user who owns the data.
  • Client: the app wanting access (public or confidential).
  • Authorization server: issues tokens after login and consent.
  • Resource server: the API that accepts the access token.
GrantFor
Auth code + PKCEweb, SPA, mobile, all interactive logins
Client credentialsservice-to-service, no user present
Device codeTVs, CLIs, input-constrained devices
Implicitremoved in 2.1 (token leaked in URL)
Password (ROPC)removed in 2.1 (app sees the password)
08

The flow to know: authorization code + PKCE

This is the canonical OAuth flow and the single most asked-about diagram. PKCE (Proof Key for Code Exchange) binds the token request to the same client that started the flow, so a stolen authorization code is useless to an attacker.

sequenceDiagram
    autonumber
    actor U as User
    participant App as Client app
    participant AS as Auth server
    participant API as Resource API
    App->>App: generate code_verifier and code_challenge S256
    U->>App: click log in
    App->>AS: /authorize response_type code, code_challenge, redirect_uri
    AS->>U: show login and consent
    U->>AS: authenticate and approve
    AS-->>App: redirect back with an authorization code
    App->>AS: POST /token with code and code_verifier
    AS->>AS: check that hash of verifier equals the challenge
    AS-->>App: access token, refresh token, id token if OIDC
    App->>API: call API with the access token
    API-->>App: 200 data
The code_verifier never leaves the client until the final token call; the auth server only saw its hash. A stolen code cannot be exchanged without the original verifier, which defeats code-interception attacks.

Why the code, why not just return a token?

The authorization code is a short-lived, one-time voucher returned through the browser (a less trusted channel); the actual tokens come back over a direct, back-channel POST. PKCE ties the two legs together. That indirection is exactly what the removed implicit flow skipped, and why it was insecure.

09

OpenID Connect: authentication on top of OAuth

OAuth alone answers "may this app access this API?" It does not, by itself, tell the app who logged in. OpenID Connect is a thin identity layer over OAuth 2 that adds an ID token (a JWT about the user) and a standard /userinfo endpoint. "Sign in with Google" is OIDC.

sequenceDiagram
    autonumber
    actor U as User
    participant RP as App, the relying party
    participant OP as Identity provider
    U->>RP: click sign in
    RP->>OP: /authorize scope openid profile email, code plus PKCE
    U->>OP: authenticate
    OP-->>RP: authorization code
    RP->>OP: exchange code at /token
    OP-->>RP: id_token the who, access_token the what
    RP->>RP: verify id_token, establish local user session
    RP->>OP: optional /userinfo with the access token
    OP-->>RP: profile claims
Two tokens with two jobs: the ID token is for the app to learn who the user is (verify it, then create a session); the access token is for calling APIs. Never use an ID token to call an API, and never use an access token to identify the user.
OAuth 2.xOpenID Connect
Questionwhat may this app do?who is this user?
Answeraccess token (for APIs)id token (for the app)
Usedelegated authorizationauthentication, login, SSO
10

SAML & enterprise SSO

SAML is the older, XML-based SSO standard that still runs most corporate and B2B logins. A Service Provider (the app) redirects you to an Identity Provider (Okta, Entra ID, the corporate IdP), which returns a signed assertion vouching for you. Same idea as OIDC, different wire format.

sequenceDiagram
    autonumber
    actor U as User
    participant SP as App, service provider
    participant IdP as Identity provider
    U->>SP: open the protected app
    SP-->>U: redirect with a SAML AuthnRequest
    U->>IdP: authenticate with corporate login
    IdP-->>U: signed SAML assertion in a POST form
    U->>SP: browser posts the assertion to the ACS endpoint
    SP->>SP: verify signature, read the user attributes
    SP-->>U: application session established
The assertion is a signed XML document carrying the user's identity and attributes (groups, email). The app trusts it because it is signed by the IdP's key. This is SP-initiated SSO; IdP-initiated (start at the portal) also exists.
SAMLOpenID Connect
FormatXML assertionsJSON / JWT
Best forenterprise, workforce SSO, legacyweb, mobile, APIs, modern apps
Transportbrowser POST / redirectREST + JSON, mobile-friendly
11

SSO & federation: one login, many apps

Single sign-on works because the session lives at the identity provider, not the app. Log in once at the IdP; every other app that trusts that IdP gets you silently, because your IdP session is already established. Federation extends this across organizations and IdPs.

sequenceDiagram
    autonumber
    actor U as User
    participant A as App A
    participant B as App B
    participant IdP as Identity provider
    U->>A: open App A
    A->>IdP: redirect to authenticate
    U->>IdP: sign in, IdP creates its own session
    IdP-->>A: token, App A session created
    Note over U,IdP: later, a different app
    U->>B: open App B
    B->>IdP: redirect to authenticate
    IdP-->>B: existing IdP session, silent token, no new login
    B-->>U: signed in automatically
App B never prompts because the IdP recognizes an existing session and issues a token immediately. Single logout is the mirror problem: killing the IdP session, and ideally each app session, in one action.
12

Passwordless: passkeys (WebAuthn / FIDO2) & MFA

Passkeys replace the password with a device-held key pair. The private key never leaves your phone or laptop; the site stores only the public key. Because the signature is bound to the site's origin, passkeys are phishing-resistant in a way passwords and OTP codes are not.

sequenceDiagram
    autonumber
    actor U as User
    participant B as Browser and authenticator
    participant RP as Server, relying party
    RP-->>B: challenge, a random nonce plus the site id
    B->>U: verify with biometric or PIN
    U-->>B: local gesture, the private key stays on device
    B->>B: sign the challenge with the private key
    B-->>RP: signed assertion plus credential id
    RP->>RP: verify the signature with the stored public key
    RP-->>U: authenticated, phishing-resistant and per-origin
No shared secret ever crosses the wire, so there is nothing to phish, replay, or leak in a breach. The authenticator refuses to sign for the wrong origin, which is what defeats phishing sites.

MFA: something you know, have, are

Multi-factor combines categories: a password (know), a phone or security key (have), a fingerprint (are). Strength order, roughly: SMS OTP (weak, SIM-swappable) < TOTP app < push approval < WebAuthn / passkey (phishing-resistant). Use step-up auth to demand a stronger factor only for sensitive actions (a payment, a settings change), not on every request.

13

Browser apps: token storage & the BFF pattern

Where a single-page app keeps its tokens is a genuine security decision. localStorage is readable by any XSS; a cookie is sent automatically and invites CSRF. The modern answer is to keep tokens out of the browser entirely with a Backend-for-Frontend.

WhereRiskVerdict
localStorageany XSS reads the tokenavoid for real tokens
JS-readable cookieXSS + CSRFavoid
HttpOnly cookieCSRF (mitigate with SameSite)ok for a session id
Not in the browser (BFF)tokens never exposed to JSthe recommendation
sequenceDiagram
    autonumber
    actor U as User
    participant SPA as Browser SPA
    participant BFF as BFF, a confidential client
    participant AS as Auth server
    participant API as API
    U->>SPA: click log in
    SPA->>BFF: start login
    BFF->>AS: authorization code plus PKCE, server-side
    AS-->>BFF: tokens, kept server-side only
    BFF-->>SPA: Set-Cookie session, HttpOnly SameSite
    Note over SPA,BFF: the browser never sees a token
    SPA->>BFF: /api/orders with the session cookie
    BFF->>API: forward the request with the access token
    API-->>BFF: data
    BFF-->>SPA: data
The BFF (also called the token-handler pattern) runs the OAuth flow server-side and holds the tokens. The browser gets only a hardened, HttpOnly session cookie, so an XSS cannot steal a token because there is none to steal. This is now the IETF-recommended approach for browser apps.
14

Service-to-service auth & identity context propagation

Inside a system, two questions run in parallel: which service is calling (workload identity) and on whose behalf (the user context). You carry both, and you often mint a fresh, narrowly-scoped token for each hop rather than forwarding the original everywhere.

sequenceDiagram
    autonumber
    participant GW as API gateway
    participant AS as Auth server
    participant A as Service A
    participant B as Service B
    GW->>GW: validate the user JWT, iss aud exp signature
    GW->>A: forward request plus user context over mTLS
    A->>AS: token exchange RFC 8693, user token to downstream token
    AS-->>A: new token scoped for Service B, on behalf of the user
    A->>B: call B with the exchanged token over mTLS
    B->>B: verify the token and the caller identity, SPIFFE SVID
    B-->>A: response
Two layers of trust: mTLS proves the calling workload's identity (a SPIFFE SVID), independent of the user; token exchange (RFC 8693) narrows the user's token to exactly what the next service needs, so a downstream service cannot replay a broad token upstream.
15

Where to enforce auth: edge, service, data

Do coarse checks once at the edge, fine-grained checks where the context lives, and defense-in-depth all the way down. Centralize the policy, distribute the enforcement.

flowchart LR
  C["client"] --> GW["gateway: authenticate, coarse checks, signature iss exp aud"]
  GW --> SVC["service: fine-grained authZ, RBAC ABAC or ReBAC"]
  SVC --> DATA["data layer: row and field checks, the last line"]
  GW -. mTLS plus SPIFFE .-> SVC
  classDef edge fill:#DCEBFE,stroke:#09244B,color:#09244B
  classDef good fill:#D6F5E3,stroke:#1B7F46,color:#09244B
  class GW edge
  class DATA good
The gateway authenticates and rejects the obviously-invalid so bad traffic never reaches services. Each service still authorizes the specific action, because only it knows the resource. The data layer is the final backstop against a bug upstream.

Centralized policy, decentralized enforcement

Do not scatter authorization logic as ad-hoc if checks. Externalize the policy (who may do what) to one place, an engine like OPA / Rego or a Zanzibar-style service, and let every service ask it, often via a sidecar in a service mesh. You get one auditable source of truth and consistent decisions, without a single network chokepoint on the hot path.

16

Authorization models: RBAC, ABAC, ReBAC, ACL, scopes

How the "what may you do" decision is actually modelled. Pick by how fine-grained and relationship-heavy your permissions are.

ModelDecides byExampleWhen
ACLa list per object of who may do whatthis file: alice=read, bob=writefew objects, simple sharing
RBACthe role assigned to the useradmins may deletemost apps; simple, coarse
ABACattributes of user, resource, contextmanagers in EU during work hourspolicy-rich, contextual rules
ReBACrelationships between entitieseditors of a doc's parent folderfine-grained sharing at scale
Scopescoarse grants on a token (OAuth)read:orderswhat a client app may do, not a user
flowchart LR
  U["user alice"] -->|member| G["group eng"]
  G -->|editor| D["doc design"]
  U -->|owner| F["folder specs"]
  F -->|parent of| D
  Q["can alice edit doc design?"] --> R["yes, via group editor or folder ownership"]
  classDef good fill:#D6F5E3,stroke:#1B7F46,color:#09244B
  class R good
ReBAC, the model behind Google's Zanzibar (and OpenFGA, SpiceDB): permissions are stored as relationship tuples and answered by walking the graph. It is how "share this doc with everyone in the parent folder's team" stays fast at Google scale.
17

What to put in a token, and what to leave out

A token is a claim you sign and cannot easily take back before it expires, so encode only what is stable, non-sensitive, and needed at every hop. Everything else, look up at request time. This one decision drives token size, staleness, and blast radius.

Put in the tokenLeave out, look it up instead
stable subject id (sub), tenant / org idfast-changing data (balances, feature flags, live status)
issuer, audience, expiry (iss, aud, exp, iat, jti)fine-grained per-object permissions (use a lookup / ReBAC)
a few coarse, stable scopes or roleslarge role or group lists (token bloat, header limits)
auth context: how and when (amr, acr, auth_time)secrets, passwords, keys (the payload is readable)
just enough to authorize this requestPII beyond the minimum (tokens land in logs and caches)
flowchart TB
  A["a piece of data"] --> B{stable for the whole token lifetime?}
  B -->|no| L["look it up at request time"]
  B -->|yes| C{sensitive, PII, or a secret?}
  C -->|yes| L
  C -->|no| D{needed to authorize this request?}
  D -->|no| L
  D -->|yes| T["encode it as a claim"]
  classDef good fill:#D6F5E3,stroke:#1B7F46,color:#09244B
  classDef alt fill:#DCEBFE,stroke:#4A90D9,color:#09244B
  class T good
  class L alt
The default answer is "look it up." A claim earns its place only if it is stable, safe to expose, and needed to make an authorization decision without another round trip.

Two more strategies

  • Downscope per audience. Don't carry one fat token everywhere; mint a per-service token (via token exchange) holding only the claims that service needs, so a leak has a smaller blast radius.
  • Mind the size. Tokens ride in headers and cookies with roughly 4 to 8 KB limits; a token stuffed with groups can break requests or blow the cookie limit. Reference the data instead (an opaque or phantom token).
18

Token security: the pitfalls that get systems breached

The failure modes are well known and mostly cheap to prevent. Catch these in a design review.

Do

  • Short TTL on access tokens; revocable refresh tokens with rotation.
  • Restrict the audience (aud): a token for service A must not work on service B.
  • Sender-constrain tokens (DPoP or mTLS) so a stolen bearer token is useless.
  • Pin the algorithm on verify; fetch keys from JWKS by kid.
  • Validate at every hop, and re-scope with token exchange, not blind forwarding.

Never

  • Trust the token's own alg: the alg=none and RS/HS confusion attacks.
  • Put secrets or PII in a JWT: the payload is readable base64.
  • Store tokens in localStorage: one XSS and they are gone. Use a BFF.
  • Skip the state / PKCE check: that is how CSRF and code interception land.
  • Forward a broad token everywhere: over-privileged tokens amplify any breach.
19

Encryption: in transit and at rest

Two places data must be protected, two different mechanisms. Both are table stakes, and both are distinct from password hashing (which is one-way, not encryption).

In transitTLS / HTTPS
What
TLS encrypts data on the wire; the handshake authenticates the server via certificates.
Do
HTTPS everywhere, HSTS, modern TLS 1.3. Terminate at the edge, re-encrypt internally if needed.
Watch
mixed content and expired certs; never send tokens or passwords over plain HTTP.
At restdisk / db / fields
What
encrypt stored data: full-disk/volume, database TDE, or specific sensitive fields.
Do
field-level encryption for the most sensitive data; manage keys in a KMS, rotate them.
Watch
at-rest encryption only protects a stolen disk; it does not stop an app-level breach. Keys are the crown jewels.

The nuance to state

Encryption in transit stops eavesdropping; at rest stops a stolen disk or backup. Neither stops an attacker who already has app access, that is what authz and least-privilege are for. Hashing (passwords) is one-way and is not encryption; encryption (reversible) is for data you must read back, and then the key management is the real problem.

20

Common web attacks & defenses

The handful that come up constantly. Each has a standard defense; naming both is the interview win.

AttackWhat it isDefense
SQL injectionuntrusted input becomes SQLparameterized queries / prepared statements; never string-concat SQL
XSSinjected script runs in a victim's browseroutput-encode, a Content-Security-Policy, HttpOnly cookies
CSRFa site makes the victim's browser send an authed requestSameSite cookies, CSRF tokens, check Origin
SSRFserver tricked into requesting an internal URLallowlist outbound hosts; block link-local/metadata IPs
Broken object authzchange an id, read another user's data (IDOR)authorize every object access against the caller
21

What to reach for, by context

Start from the caller and the trust boundary, then pick the mechanism.

ContextReach forNot
Server-rendered web appsession cookiea JWT you can't revoke
Single-page app (browser)BFF + auth code / PKCEtokens in localStorage
Mobile / native appauth code + PKCEpassword grant
Customer / social loginOpenID Connectrolling your own
Enterprise / workforce SSOOIDC or SAMLlocal accounts
Service-to-service, same meshmTLS + SPIFFEa shared API key
Service on behalf of a usertoken exchange (8693)forwarding the raw user token
Machine / CLI / cronclient credentialsa human's token
Fine-grained resource sharingReBAC / Zanzibarroles bolted on
Passwordless, phishing-resistantpasskeys (WebAuthn)SMS OTP
Store a passwordargon2 + unique saltmd5/sha, encryption, plaintext
Protect data on the wireTLS 1.3 + HSTSplain HTTP, self-signed in prod
Protect sensitive data at restfield encryption + KMSapp-readable plaintext
Stop injectionparameterized queriesstring-concatenated SQL
22

Interview questions, with crisp answers

The security and auth questions that come up in a system-design round, and the sentence or two that shows you understand the trade-off.

Q · Sessions or JWTs?

Sessions are stateful (instant revoke, needs a shared store); JWTs are stateless (fast local check, hard to revoke). Use sessions for server-rendered apps, short-lived JWTs plus a refresh token for APIs and mobile.

Q · How do you revoke a JWT?

You mostly don't; you keep the TTL short and revoke the refresh token, or maintain a small deny-list of jtis, or use opaque/phantom tokens if instant revocation is a hard requirement.

Q · Why is PKCE mandatory now?

It binds the token exchange to the client that started the flow, so an intercepted authorization code can't be redeemed. OAuth 2.1 requires it for all clients and drops the implicit flow entirely.

Q · ID token vs access token?

The ID token (OIDC) tells the app who logged in; verify it and drop it. The access token is for calling APIs. Don't identify users with an access token, and don't call APIs with an ID token.

Q · OAuth vs OIDC vs SAML?

OAuth is delegated authorization (access to APIs). OIDC adds authentication (login) on top, in JSON/JWT. SAML is the older XML equivalent of OIDC login, still dominant in enterprise SSO.

Q · Where do you store tokens in a browser?

Ideally nowhere: use a BFF so tokens stay server-side and the browser holds only an HttpOnly session cookie. If you must, an HttpOnly cookie beats localStorage, which any XSS can read.

Q · How does identity cross microservices?

The gateway validates the user token once; workloads authenticate to each other with mTLS/SPIFFE; and you mint a per-hop token with token exchange (RFC 8693) so each service gets a narrowly-scoped, on-behalf-of token.

Q · RBAC vs ABAC vs ReBAC?

RBAC decides by role (simple, coarse). ABAC by attributes and context (flexible policy). ReBAC by relationships between entities (Zanzibar), for Google-Docs-style nested sharing at scale. Start with RBAC.

Q · mTLS or a bearer token?

They answer different questions and pair up: mTLS proves which workload is calling; the token proves on whose behalf. A bearer token alone is stealable, so sender-constrain it with mTLS or DPoP.

Q · What makes passkeys phishing-resistant?

The private key never leaves the device and the authenticator only signs for the real origin, so a look-alike phishing site gets nothing. There is no shared secret to type, replay, or leak in a breach.

Q · How do you store passwords?

A slow, salted, one-way hash, argon2 or bcrypt, with a unique salt per user. Never plaintext, never reversible encryption, never a fast hash like SHA-256, so a breach leaks only hashes that are expensive to crack.

Q · How do you prevent SQL injection?

Parameterized queries / prepared statements, so input is always data, never concatenated into SQL. The same "treat input as data" idea defends XSS (encode output) and SSRF (allowlist hosts).

Q · What is IDOR / broken object authz?

A logged-in user reads or edits another's data by changing an id in the request, because the server authenticated them but never checked they own that object. Fix: authorize every object access against the caller.

Q · Encryption in transit vs at rest?

In transit (TLS) stops eavesdropping on the wire; at rest stops a stolen disk or backup. Neither stops an attacker with app access, that is authz and least-privilege. And hashing is not encryption: it is one-way.

23

Traps & the repeatable move

The ways security and auth go wrong, and the habit that catches all of them.

The traps

  • AuthN mistaken for authZ: logged in is not allowed (IDOR).
  • Authz only in the UI: the API must re-check every object access.
  • Fast hash or plaintext passwords: use argon2/bcrypt + salt.
  • Long-lived, un-revocable access tokens: a leak lasts hours.
  • Tokens in localStorage: one XSS empties the vault.
  • Trusting the token's alg: the classic JWT forgery.
  • String-concatenated SQL: parameterize instead.
  • Over-broad tokens forwarded everywhere: any breach spreads.
  • Implicit flow / password grant: removed for good reason.
  • Secrets or PII in a JWT: it is readable by anyone holding it.
  • HTTP for anything sensitive: TLS everywhere, HSTS.
  • SMS as a second factor: SIM-swappable; prefer WebAuthn.

The repeatable move

1. separate authN (who) from authZ (what)
2. pick how identity is carried: session / JWT / opaque
3. use auth code + PKCE for every interactive login
4. keep access tokens short, rotate refresh tokens
5. re-check authz per object, at every hop, on the server
6. never trust input; encrypt in transit + at rest

The tell in a design review: a token that never expires, an authZ check missing on a sensitive route, a raw user token forwarded across services, a password hashed with SHA-256, or a token in localStorage. Name the missing check and push back.

Companion to S5 · API Design & Networking. Further reading: OWASP Top 10 · OAuth 2.1 · RFC 9700 · OAuth Security BCP · OpenID Connect · RFC 8693 · Token Exchange · SPIFFE · FIDO passkeys · Google Zanzibar / ReBAC · OWASP · password storage.