Lesson 1 · Authentication — who are you?

The two questions & the map

Every access decision this platform makes is one of two questions — and the whole system is the sequence of gates that answers them.

Your win: name the two security questions, distinguish them cleanly, and draw the full gate sequence a request passes through — so every later lesson has a place to live on the map.

Two questions, never confused

All of access control reduces to two questions asked in order:

🔑 Authentication (authN)

"Who are you?" Proving identity. Here: a JWT presented with every request, its signature verified against a public key set. Answered once, at the door.

🚪 Authorization (authZ)

"What may you do?" Deciding what a known identity can access. Here: three tiers — a role gate, a granular permission scoped to a place, and row-level filtering. Answered repeatedly, per resource.

Keeping these apart is the first discipline. A valid token means you're authenticated — it says nothing about whether you may read a given student's record. That's a separate, later question.1

The gate sequence — the spine of this course

A request that reaches your data has passed, in order, through every gate below. Memorize this; it's the skeleton every lesson hangs on:

TOKEN a Manabie JWT in gRPC metadata "token" │ ① AUTH verify the JWT vs a key set (JWKS); put identity on the context ── Part 1 ── │ (userID, userGroup, roles, claims) authN │ ② ROLE GATE rbacDecider: is this method allowed for one of my roles? ┐ │ │ ③ SERVICE CHECK ownership (is it MY data?) + granular permission ├─ Part 2 ── │ (do I hold "user.user.read" on this location?) │ authZ │ │ ④ REPO WHERE resource_path = … AND deleted_at IS NULL ┘ │ ⑤ RLS Postgres filters every row by tenant (+ location), from the JWT ── Part 3 ── ▼ authZ (in the DB) your data — a row survives only if the repo query AND the RLS policy both allow it

The three tiers of authorization

Notice authZ isn't one check — it's three, deliberately layered. This is defense in depth: each tier catches something the others can't.

TierQuestionMechanismPart
Role (RBAC)Is your role allowed to call this method?rbacDecider in the interceptor2
Permission + locationDo you hold this permission on this place?granted permissions ⋈ access_paths2
Row-Level SecurityDoes this row belong to your tenant/scope?Postgres policies on resource_path3
The one idea to carry from this lesson authN is the door; authZ is everything inside. The door checks a JWT (Part 1). Inside, three tiers answer "what may you do?" — a coarse role gate, a fine permission scoped to a location, and the database's own row filter. Where a topic lives on this map tells you what it's for.
One orientation warning up front There are two Go packages named interceptors with the same type names (Auth, GroupDecider): the legacy internal/golibs/interceptors and the current internal/usermgmt/pkg/interceptors. Every service uses the usermgmt one for the live interceptor; the golibs one still hosts the shared token verifier, the claims struct, and the context accessors. When you grep, check which package you're in. We'll flag it again in Lesson 4.
Where this lives The whole map is real code: authN in internal/usermgmt/pkg/interceptors/auth.go + internal/shamir; the role gate in each cmd/server/<svc>/auth.go (rbacDecider); permissions/locations in migrations/bob/1182 + migrations/auth/1001; RLS in pool_gcp.go + those same migrations. The repo map has every anchor.
Read this next

Authentication vs authorization, precisely

The clean distinction between "who you are" (ID token / OIDC) and "what you may access" (OAuth / access token) — the mental model this whole course rests on.

Auth0 — ID token vs access token
OpenID Connect Core 1.0 (OIDC = authN layer on OAuth 2.0) · NIST — RBAC

Check yourself (from memory)

Q1. A request arrives with a valid, unexpired JWT. What has been established?

A valid token = authenticated. What they may do is a separate, later question answered by the three authZ tiers.

Q2. Which is NOT one of the three authorization tiers?

Verifying the signature is authentication (the door). The three authZ tiers are role, permission+location, and RLS.

Q3. Why layer three authorization tiers instead of one?

Layering means a bug in the role gate can't leak data RLS still guards, and vice versa. Redundant, independent gates.
Recall: the two questions + the full gate sequence.
say both questions + all five gates, then reveal
Two questions: authN = "who are you?" (a JWKS-verified JWT, answered once at the door); authZ = "what may you do?" (three tiers, answered per resource). Gate sequence: ① token → ① auth interceptor (verify JWT vs JWKS → put userID/ userGroup/roles/claims on ctx) → ② role gate (rbacDecider) → ③ service check (ownership + granular permission on a location) → ④ repo (WHERE resource_path, deleted_at IS NULL) → ⑤ RLS (Postgres filters rows by tenant + location). A row survives only if the repo query and the RLS policy both allow it — defense in depth. Part 1 = authN; Parts 2–3 = the three authZ tiers.
🎯 Interview one-liner "How does access control work in your system?" → "Two questions. Authentication is a JWKS-verified JWT at the gRPC interceptor. Authorization is three layered tiers — a role gate per method, a granular permission scoped to a location subtree, and Postgres row-level security isolating the tenant. Defense in depth: a request has to clear every gate."
Next lesson: the JWT itself — its format, and the Manabie claim that carries your identity and your tenant. Confused about where a topic sits on the map? Ask me — that's what I'm here for.

1. authN vs authZ: Auth0, OIDC Core. In-repo map: docs/auth/reference/repo-auth-map.md.