# Glossary — Authentication & Authorization (this repo)

The canonical vocabulary. Opinionated: where the repo has a house term, that wins; the general
industry term is noted so you can map to the interview. Adhere to these names in every lesson.

---

### authentication (authN)
"**Who are you?**" Proving identity. Here: a **JWKS-verified JWT** presented in gRPC metadata as
`token`, checked by the auth interceptor. (Interview: "verify signature + issuer + audience + expiry.")

### authorization (authZ)
"**What may you do?**" Deciding access for a known identity. Here it's **three tiers**: the
`rbacDecider` role gate, the granular permission/location model, and Postgres RLS.

### JWT / Manabie token
A JSON Web Token (`header.payload.signature`, [RFC 7519]). The repo's own token is issued by Shamir
with issuer `"manabie"` and carries the **`Manabie` claim**. (A "Manabie token" = a JWT this platform
minted for itself, as opposed to a raw Firebase/Auth0 token.)

### `ManabieClaims`
The tenant-critical claim block inside the JWT (`auth_claims.go:101`):
`AllowedRoles`, `DefaultRole`, `UserID`, `SchoolIDs`, `UserGroup`, **`ResourcePath`** (= org id),
`TenantID`. The same values are *also* minted as `x-hasura-*` (`HasuraClaims`) for Hasura's RLS.

### Shamir
The central **token authority**: verifies external tokens, **exchanges/mints** Manabie JWTs (RS256,
signed with a primary RSA key), and **hosts the JWKS** at `/.well-known/jwks.json`. Despite the name,
it is *not* Shamir secret-sharing — it's multi-key JWT signing. Every other service verifies tokens by
fetching Shamir's JWKS.

### token exchange
Trading one token for another (the STS pattern, [RFC 8693]). Here: a client logs in to Firebase/
Identity Platform (password → Firebase idToken), then calls `ExchangeToken` → Shamir verifies the
Firebase token, looks up the user in the DB, and mints a **Manabie JWT** with the user's roles/tenant.

### JWKS (JSON Web Key Set)
The set of public keys a verifier uses to check a JWT's signature, served at a URL. Each service's
`TokenVerifier` fetches Shamir's JWKS (and Firebase's) and caches it, re-fetching once on a miss.

### issuer (`TokenIssuerConfig`)
`{Issuer, Audience, JWKSEndpoint}`. A service verifies against **multiple** issuers (Manabie,
Firebase, per-org Firebase projects); the first verifier that succeeds wins.

### auth interceptor
The gRPC `UnaryServerInterceptor` (`internal/usermgmt/pkg/interceptors/auth.go:79`) that runs on
every RPC: verify the JWT → put `UserID`/`UserGroup`/`UserRoles`/`JWTClaims` on the context → run the
role gate. The single entry checkpoint. **Two packages share the type name** — services use the
usermgmt one; the golibs one is legacy (but hosts the shared verifier/claims/accessors).

### `ignoreAuthEndpoint`
The allow-list of methods that **bypass JWT verification entirely** (`skipAuthMethods`): health
checks, `ExchangeToken`, `GetOAuthUrl`, `ResetPassword` — things unauthenticated by design.

### `fakeJwtCtxEndpoint` / `FakeJwtContext`
**Service-to-service auth**: for internal methods (NATS/Kafka-originated), the interceptor
**synthesizes** a School-Admin `CustomClaims` from the request body (`ResourcePath =
req.GetOrganizationId()`) instead of verifying a real token. Paired with `ignoreAuthEndpoint`.

### RBAC (role-based access control)
Access by **role**. Here: the coarse first gate. (Interview: NIST model — flat / hierarchical /
constrained.)

### `rbacDecider`
The per-service `map[string][]string` (gRPC method → allowed roles) in `cmd/server/<svc>/auth.go`,
wired into `GroupDecider.AllowedGroups`. **Semantics:** role list → must hold ≥1; **`nil`** → any
authenticated user (with ≥1 role); **absent key** → **`PermissionDenied` at runtime**.

### the panic myth
`.claude/rules/security.md` claims a missing `rbacDecider` entry **panics at startup**. It does
**not** — `GroupDecider.Check` returns `sttNoDeciderProvided` (`PermissionDenied`) at call time. There
is no startup cross-check. Know the real behavior for the interview.

### role (`constant.Role*`)
A named job function (~27 of them: School Admin, HQ Staff, Teacher, Parent, Student, Centre
Manager/Lead/Staff, …), `internal/usermgmt/pkg/constant`. A user holds roles via user-groups; the
registerable set is `AllowListRoles`.

### permission
A dotted capability string — `user.user.read`, `master.location.write`. Defined in the `permission`
table, attached to a role via `permission_role`. The **granular** authZ unit (finer than a role).

### user_group / granted_role / granted_role_access_path / user_group_member
The join chain that grants permissions to users, **scoped to a location**:
`user_group_member` (user → user_group) → `granted_role` (user_group → role) → `permission_role`
(role → permission), with `granted_role_access_path` pinning each grant to a **location** (subtree).

### `granted_permission` (table) / `granted_permissions` (view)
The **denormalized** `(user_group, role, permission, location, resource_path)` fast-path table, and
the **view** that joins the whole chain and expands each grant to its **location subtree**. What you
query to answer "where does this user hold permission X?".

### location / `location_types`
The org's place hierarchy (org → brand → center → …). `locations` has `parent_location_id` and an
`access_path`; `location_types` is a parallel tree of the *kinds* of place.

### `access_path` (materialized path)
A location's full ancestry encoded as a `/`-joined string of `location_id`s (e.g. `org/brand/center`).
A grant at `L` covers the **subtree** — every location whose `access_path` starts with `L`'s, matched
by `access_path ~~ (L.access_path || '%')`. (Interview: materialized-path tree model.)

### `user_access_paths`
Which locations a user *belongs to* (`user_id`, `location_id`, `access_path`). Used with granted
permissions in the location-aware RLS policies.

### `LocationRestricted` interceptor
A second interceptor (mastermgmt) that gates by location: empty requested locations → require org-root
access; otherwise every requested `location_id` must be granted, else `PermissionDenied`.

### RLS (Row-Level Security)
Postgres filtering rows by a policy predicate. Here it's the **last** authZ tier — enforced in the DB
so even a raw query is tenant- and location-scoped. Tables use `ENABLE` + **`FORCE ROW LEVEL
SECURITY`** (binds the owner too).

### `resource_path`
The **tenant / organization id** (a string; seed orgs are negative ints, `-2147483648` = Manabie).
It rides in the JWT's `Manabie` claim, becomes a Postgres session var, and is the key every tenant
RLS policy checks. **Never taken from the request** — always from the JWT.

### `setPostgres` / `BeforeAcquire`
The pgx pool hook (`pool_gcp.go:63,161`) that runs on **every connection acquire**:
`set_config('permission.resource_path' / 'permission.user_group' / 'app.user_id', …)` from the JWT
claims, verifying the echo and **refusing the connection** on mismatch. The JWT → session-var bridge.

### `permission_check()` / `autofillresourcepath()`
The two SQL functions RLS rests on. `permission_check(rp, t)` = `rp = current_setting('permission.resource_path')`
(the tenant test in every policy). `autofillresourcepath()` = `current_setting('permission.resource_path')`,
used as the **DEFAULT** for `resource_path` columns so inserts auto-stamp the tenant.

### location-aware RLS policy
A policy that combines **tenant + permission + location**: a `users` row is visible only if
`granted_permissions ⋈ user_access_paths` shows the caller (`app.user_id`) holds the right permission
(`user.user.read`) on that user's location. Pushes the app-layer scope (L8) into the database.

### defense in depth
The design principle behind the whole chain: the interceptor (role), the service (ownership/
permission), and RLS (tenant/location) each catch what the others miss — a bug in one layer doesn't
breach the data.

### `rls_scan`
A daily job (`cmd/server/rls_scan`) that enumerates every public table and flags any missing the
expected `permission_check` RLS (vs a `public_tables.json` allow-list). Drift/misconfig detector.
