Check IAM token revocation via the IsRevoked RPC

What does this MR do and why?

IamOauthToken#revoked? was hardcoded to false, so revoked tokens kept working until expiration. Now it calls IAM's IsRevoked RPC with a 2s deadline. Fails closed: any error counts as revoked and is logged; unavailable and timeout errors skip Sentry. During an IAM outage the client sees the same 401 invalid_token as for a revoked token. Refreshing also needs IAM, so clients recover once IAM is back. On Git, a failed revocation check doesn't count toward the IP ban.

The check is cached per request (keyed by jti).

For Git over HTTPS, a gliamat- password that fails the token check no longer falls through to database or LDAP checks.

GrpcClient gains report_transient_errors: (default true) to suppress Sentry on specific error types. No new feature flag—from_jwt gates on iam_svc_oauth.

Known limitation: IAM doesn't denylist tokens revoked between exp and exp+30s. But Rails rejects any token past exp today (expired? has no leeway), so it only matters if Rails starts honouring the 30s JWT leeway (#633340).

How to set up and validate locally

  1. Run IAM from main with GDK connected via https://gitlab.com/gitlab-org/auth/iam/-/blob/main/docs/gitlab-rails-local-setup.md; requires IsRevoked RPC from gitlab-org/auth/iam!767 (merged).
  2. Get a gliamat- token per https://gitlab.com/gitlab-org/auth/iam/-/blob/main/docs/oauth-pkce-consent-flow.md and export TOKEN=<access_token>.
  3. Verify the token works:
    curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $TOKEN" "$RAILS_HOST/api/v4/user"   # 200
  4. Revoke the token, then verify rejection:
    curl -s -X POST "$IAM_HOST/oauth/revoke" -d "token=$TOKEN" -d "client_id=$CLIENT_ID"
    curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $TOKEN" "$RAILS_HOST/api/v4/user"   # 401
    (Previously returned 200 until expiry.)
  5. Verify Git access fails with the same revoked token:
    git clone "http://oauth2:$TOKEN@gdk.test:3000/<namespace>/<project>.git"   # HTTP Basic: Access denied
  6. Verify fail-closed: fetch a fresh token (step 2) and run step 3 with it (200; this also caches IAM's signing keys), then stop IAM and repeat step 3—expect 401 and IAM token revocation check failed, treating token as revoked in log/auth_json.log.

Closes #593992

References:

🤖 Generated with Claude Code

Edited by Smriti Garg

Merge request reports

Loading
Loading