feat(oauth): add token prefixes for routable tokens
Summary
- Adds prefixes to three token types (
gliamac-authorization code,gliamat-access token,gliamrt-refresh token), for the OAuth gradual rollout and Cells routing. Part 1 of gitlab-org/gitlab#604548 (closed). - Enables Workhorse to distinguish IAM-issued tokens from Doorkeeper tokens during the gradual rollout. Enables the Cells HTTP Router to recognize tokens for cell-based routing (a follow-up adds the actual routing claim; this MR adds the prefix it will key off).
- Wraps fosite's core strategy to inject and strip prefixes without reimplementing any cryptographic logic.
What changed
-
auth/oauth/core/token_prefix.go (new):
prefixedCoreStrategywraps fosite'soauth2.CoreStrategyinterface. It overrides the token generation and validation methods to prepend/strip the appropriate prefix around calls to the wrapped strategy. All HMAC and JWT operations remain delegated to the wrapped strategy, unchanged. Prefix stripping usesstrings.TrimPrefix, a no-op when the prefix is absent — but that's not a backward-compatibility guarantee: IAM has never been deployed, so there's no pre-existing population of unprefixed tokens to support. -
auth/oauth/core/provider.go: switched from fosite's
compose.NewOAuth2HMACStrategy(which silently applies fosite's own defaultory_ac_/ory_rt_prefixes — unnoticed until now, since nothing in this codebase asserted on token format) tooauth2.NewHMACSHAStrategyUnPrefixed, soprefixedCoreStrategyis the only source of a prefix rather than doubling up. The existing strategy chain (newJTIJWTStrategy(...)) is now wrapped withnewPrefixedCoreStrategy(...). -
auth/oauth/server/handler_integration_test.go: two tests that parsed the access token as a bare JWT now strip
core.AccessTokenPrefixfirst. Rollout coordination point for reviewers: any current or future consumer that treats IAM's access token as a bare JWT (an RFC 9068 resource server, introspection, etc.) will break the moment this ships unless it stripsgliamat-first. -
auth/oauth/server/revokedtokens/revokedtokens.go: removed a stale doc comment referencing the placeholder prefix name
giat_(an earlier draft value from the ticket, never implemented). -
No migration:
signaturecolumns on both backends areTEXTand store only the token's HMAC/JWT signature, never the full token, so the prefix never touches them.
Testing
- Unit tests (
go test ./...): pass - Integration tests against both backends (
ENV=development-l2andENV=development-l1,go test -tags=integration ./auth/oauth/...): pass golangci-lint: 0 issues./scripts/verify-agent-docs.sh: all documented claims hold
Out of scope (tracked separately)
- Cell/user-ID routing claims on the access token (ticket section 8.2): !590 (merged), stacked on this MR. Sourced from
gitlab-railsviaLoginService.Accept, not from a Topology Service call in IAM — investigating that turned out to be unnecessary and blocked on infra access IAM doesn't have. - Claiming the prefixes in gitlab-org/gitlab's
docs/security/tokens/_index.md: separate repo, separate MR. - The Cells HTTP Router ruleset change (
gitlab-org/cells/http-router, TypeScript) that will parsegliamat-: separate repo; IAM team committed to authoring it per the issue thread.