Add license checksum claim to the Orbit JWT

What does this MR do and why?

Orbit (orbit-server) needs to check GitLab credits quota with CustomersDot on self-managed and Dedicated. It can't hold the CustomersDot admin token there because it runs on customer infrastructure.

CustomersDot already authenticates self-managed instances by license checksum (X-License-Token), as Rails does for Duo quota checks. So Rails now puts License.current.checksum in the Orbit JWT, only on the self-managed realm (this includes Dedicated) with an online cloud license; CustomersDot rejects other license types. Only the checksum is sent, never the license. Orbit ignores unknown claims, so this is safe before the Orbit side ships.

What changes:

  • Analytics::KnowledgeGraph::JwtAuth adds a license_checksum claim (the 64-char SHA256 from License#checksum).
  • Added only when CloudConnector.gitlab_realm is self-managed (includes Dedicated) and License.current.online_cloud_license?. Omitted on GitLab.com, and for offline cloud, legacy, or missing licenses.
  • Expired licenses are not filtered here; CustomersDot owns grace-period handling.
  • No feature flag: Orbit ignores unknown claims, quota checks stay off by default on self-managed Orbit, and CustomersDot has its own kill switches.
  • Both ways Rails sends the JWT to Orbit (direct gRPC, and streaming through Workhorse) build it in JwtAuth.build_payload, so this one change covers both.
Security notes

Low risk; AppSec review requested.

  • The checksum can't be used to recover the license and can't reach seat links or Cloud Connector token minting at CustomersDot; those need the raw license key.
  • A leaked checksum lets someone read quota verdicts and trial info for that customer's own subscription. The instance admin already has more than that.
  • The JWT is only sent over TLS or to private/loopback addresses (existing guard in GrpcClient and EE::Gitlab::Workhorse). It is signed, not encrypted, so TLS is recommended for the Rails to Orbit endpoint.
  • Nothing logs the token or payload.
  • The same value is already sent to version.gitlab.com in Service Ping as license_sha256.
  • Orbit-side handling (redaction, marking the header sensitive, the new quota auth mode) is in a separate knowledge-graph MR.

References

Screenshots or screen recordings

Not applicable, backend-only change with no UI.

How to set up and validate locally

  1. Activate an online cloud license on a self-managed GDK (not GitLab.com mode).

  2. In the Rails console:

    user = User.first
    ctx = Analytics::KnowledgeGraph::RequestContext.new(source_type: Analytics::KnowledgeGraph::SourceType::REST)
    token = Analytics::KnowledgeGraph::JwtAuth.generate_token(user: user, request_context: ctx)
    payload = JWT.decode(token, Analytics::KnowledgeGraph::JwtAuth.secret, true, { algorithm: 'HS256' }).first
    payload['license_checksum'] == License.current.checksum # => true
    payload['realm'] # => "self-managed"
  3. With an offline or legacy license, or in GitLab.com mode, payload.key?('license_checksum') returns false.

  4. Specs: bin/rspec ee/spec/lib/analytics/knowledge_graph/jwt_auth_spec.rb

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Sharmad Nachnolkar

Merge request reports

Loading
Loading