Draft: TokenIssuer approach

POC from our conversation in #605019 (comment 3628872793)

What does this MR do and why?

Implements the interim TokenIssuer approach agreed in #605019 (closed) for replacing Duo Workflow's direct Doorkeeper OAuth token creation with short-lived RS256 JWTs.

Duo Workflows currently mints OAuth tokens by writing rows directly into oauth_access_tokens via Doorkeeper, which will break on Protocells/Cells where OAuth moves to the IAM service. This POC reuses and extends the JWT-based token exchange built for Artifact Registry.

Changes

  1. Extended Authn::TokenExchange::TokenIssuer with:

    • Optional gitlab.identities claim (composite identity: human user embedded in JWT instead of user:<id> Doorkeeper scope)
    • gitlab.scopes claim (OAuth-style scopes for Rails enforcement without DB lookup)
    • RAILS_AUDIENCE constant (gitlab-rails) alongside existing AR audience
    • scopes: and identities: constructor params; longer TTLs supported
  2. New Authn::Tokens::DuoWorkflowToken (ee/lib/authn/tokens/duo_workflow_token.rb):

    • Verifies RS256 JWTs against the CloudConnector::Keys JWKS
    • Validates aud/iss/exp/nbf
    • Resolves sub (GlobalID) to user/service-account
    • Exposes #scopes for validate_and_save_access_token!
    • Exposes #identity_user / #scope_user for composite identity from gitlab.identities
    • Gated by duo_workflow_use_token_issuer feature flag
  3. Auth wiring (EE extensions):

    • EE::Gitlab::Auth::AuthFinders#find_oauth_access_token: tries DuoWorkflowToken before IamOauthToken/Doorkeeper
    • EE::Gitlab::Auth#oauth_access_token_check: also accepts DuoWorkflowToken for git-over-HTTPS
    • Composite identity linking reuses existing Identity.link_from_oauth_token path (DuoWorkflowToken#scope_user delegates to #identity_user)
  4. Service changes (behind duo_workflow_use_token_issuer flag, default off):

    • CreateCompositeOauthAccessTokenService: JWT path (sub=service account, identities=[human user], ttl=1h); skips ensure_oauth_application!
    • CreateOauthAccessTokenService: JWT path (sub=current_user, no identities, ttl=2h); skips DB load-balancer sticking
    • WorkflowContextGenerationService#already_scoped_for_ai_workflows?: handles DuoWorkflowToken by checking identity_user instead of user:<id> scope
    • FlowTriggers::RunService#can_use_composite_identity?: JWT path doesn't require OAuth application
  5. Feature flag: duo_workflow_use_token_issuer (wip, default off)

Amazon Q (ee/lib/gitlab/llm/q_ai/client.rb) is explicitly out of scope.

References

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.

Merge request reports

Loading
Loading