Lesson 3 · Authentication — who are you?

Shamir — the token authority

Where a Manabie token is born: a login is verified, a user is looked up, and a fresh JWT is minted with their roles and tenant baked in.

Your win: explain the login → exchange → mint flow, what makes Shamir the single source of token truth (it hosts the JWKS everyone verifies against), and why "token exchange" is the right pattern here.

The problem Shamir solves

Users log in through Firebase / Google Identity Platform (or Auth0, or Salesforce). That gives them a token from Google — but a Google token doesn't know this platform's roles, permissions, or which resource_path the user belongs to. So the system exchanges the external token for its own Manabie JWT, minted by one central service: Shamir.

The name is a red herring Despite being called "Shamir," this is not Shamir's Secret Sharing. It's a normal JWT authority that signs with RSA keys (it happens to load a glob of keys and pick a primary one for signing). Don't let the name send you down the wrong interview answer.1

Step 1 — Login: password → Firebase idToken

A password login is proxied to Google's Identity Toolkit, which returns a Firebase idToken:

internal/usermgmt/pkg/interceptors/auth.go:344 · constant/common_def.go:89
LoginInAuthPlatform(...)  // POST identitytoolkit.googleapis.com/v1/accounts:signInWithPassword
                          // → a Firebase idToken (signed by Google, RS256)

At this point the user is authenticated to Google, but not yet carrying any Manabie identity. (There's also an OAuth 2.0 PKCE flow via GetOAuthUrl for browser logins, domain_auth.go:212-227.)

Step 2 — Exchange: external token → Manabie JWT

The client calls ExchangeToken, which reaches Shamir. Here's the core of what Shamir does — verify, look up, mint:

internal/shamir/transports/grpc/token_reader.go:139 (exchangeVerifiedToken, condensed)
claims := verify(externalToken)                       // ① is this a real Firebase/Auth0 token?
user   := UserRepoV2.GetByAuthInfo(claims.Subject)    // ② who is this in OUR database?
if user.DeactivatedAt != nil { return ErrDeactivatedUser }  // ③ reject deactivated users
tokenInfo := TokenInfo{                                // ④ gather THIS platform's identity
    DefaultRole:  user.role,
    AllowedRoles: []string{user.group},
    SchoolIds:    user.schoolIDs,
    UserGroup:    user.group,
    ResourcePath: user.resourcePath,                  //    ← the tenant, from the DB
}
newToken := Verifier.ExchangeVerifiedToken(tokenInfo) // ⑤ mint + sign a Manabie JWT

Notice step ④: the roles and resource_path come from the database, keyed by the verified user — not from anything the client sent. That's what makes the minted token trustworthy: Shamir is the only party that decides what identity a token asserts.

This is "token exchange" — the STS pattern Trading a token issued by one authority (Google) for a token issued by another (Manabie), after verifying the first and re-deciding the claims, is exactly the OAuth 2.0 Token Exchange pattern ([RFC 8693]) — a Security Token Service.2 The interview phrasing: "we run an STS that exchanges a federated login token for a first-party access token carrying our own roles and tenant."

Step 3 — Mint & sign

internal/shamir/services/verifier.go:166-210 (condensed)
generateNewToken(...)  // Issuer "manabie"; embeds Manabie + Hasura + Auth0 + Salesforce claims
signNewToken(...)      // RS256, signed with the PRIMARY RSA private key

The result is the JWT you met in Lesson 2: sub = user id, iss = "manabie", and the manabie.* + x-hasura-* claim blocks. This is the token every subsequent request carries.

The other half: Shamir hosts the JWKS

Minting is only useful if others can verify. Shamir publishes the matching public keys as a JWKS:

internal/shamir/transports/rest/jwks.go:13,29
GET /.well-known/jwks.json        // the public keys for Manabie tokens
GET /.well-known/openid-configuration   // OIDC discovery
Why this design scales Shamir signs with a private key; every other service verifies with the public key from the JWKS — no service needs to call Shamir on every request. Each caches the key set and checks signatures locally (Lesson 4). One authority mints; thirty services verify independently. Shamir also registers per-org Firebase issuers dynamically (from the organization_auths table), so a token from any org's Firebase project can be exchanged.
Read this next

Token exchange (RFC 8693) & verifying federated tokens

The STS pattern Shamir implements, and how a backend verifies a Firebase/Identity-Platform token before trusting it.

RFC 8693 — OAuth 2.0 Token Exchange
Firebase — verify ID tokens · Identity Platform — custom claims

Check yourself (from memory)

Q1. In the exchange, where do the roles and resource_path in the new token come from?

Shamir verifies the external token, then reads the user's roles and tenant from the DB — never from client input. That's what makes the minted token trustworthy.

Q2. How do thirty services verify Manabie tokens without calling Shamir each time?

Sign with private, verify with public: services pull the JWKS from /.well-known/jwks.json and check signatures locally.

Q3. What best describes what Shamir does with a Firebase login token?

It's an STS token exchange (RFC 8693). The name "Shamir" is unrelated to Shamir's Secret Sharing — it's a JWT authority.
Recall: the login → exchange → mint flow + Shamir's two jobs.
walk the three steps, then reveal
Login: password → Firebase idToken (Identity Toolkit signInWithPassword; or OAuth PKCE via GetOAuthUrl). Exchange (ExchangeToken → Shamir exchangeVerifiedToken, token_reader.go:139): ① verify the external token → ② look up the user in the DB (GetByAuthInfo) → ③ reject if deactivated → ④ read roles + resource_path from the DB → ⑤ mint. Mint (verifier.go:166-210): generateNewToken (iss "manabie", embeds manabie.*+x-hasura-*) → signNewToken (RS256, primary RSA key). Shamir's two jobs: (1) mint/exchange tokens; (2) host the JWKS at /.well-known/jwks.json so every service verifies locally with the public key. This is OAuth token exchange (STS, RFC 8693) — NOT Shamir secret-sharing.
🎯 Interview one-liner "How are tokens issued in a federated multi-tenant system?" → "An internal token-exchange service. Users log in via Firebase/Identity Platform; we verify that token, look the user up in our database, and mint our own JWT stamped with their roles and tenant. That service also publishes the JWKS, so every other service verifies our tokens locally without calling back — one mint authority, many independent verifiers."
Next: the auth interceptor — the checkpoint on every RPC that fetches that JWKS, verifies the token, and puts your identity on the request context. Ask me about token exchange if the STS framing is new.

1. In-repo: internal/shamir/transports/grpc/token_reader.go:139, internal/shamir/services/verifier.go:166-210, internal/shamir/transports/rest/jwks.go:13,29.

2. RFC 8693 (Token Exchange), Firebase verify ID tokens.