Skip to content

Add JWT IdP login endpoint (POST /auth/jwt/login)#10376

Open
gmintoco wants to merge 1 commit into
treeverse:masterfrom
gmintoco:feature/jwt-idp-login
Open

Add JWT IdP login endpoint (POST /auth/jwt/login)#10376
gmintoco wants to merge 1 commit into
treeverse:masterfrom
gmintoco:feature/jwt-idp-login

Conversation

@gmintoco
Copy link
Copy Markdown

@gmintoco gmintoco commented Apr 20, 2026

Closes #10375

Change Description

Background

lakeFS today supports OIDC (deprecated browser flow), AWS IAM via
ExternalPrincipalLogin, and the remote STS authenticator. None of these
cover the common case where a client already holds a JWT from its own
identity provider (Okta, Keycloak, Entra ID, AWS Outbound Identity
Federation, ...). See #10375 and prior art in
HashiCorp Vault JWT auth
and StarRocks JWT authentication.

New Feature

POST /auth/jwt/login accepts { "token": "<jwt>" } and returns a
standard lakeFS AuthenticationToken. Enabled iff auth.jwt.jwks_url is
set; responds 501 Not Implemented otherwise.

  • pkg/auth/jwtidp verifier: JWKS-backed signature verification (RS* /
    ES* / PS*, HMAC rejected), on-demand refresh on unknown kid, 60s
    clock-skew leeway, iss/aud/exp/iat/nbf validation, and
    required-claim exact-match checks.
  • pkg/auth/jwtidp resolver: RFC 6901 JSON Pointer selection of the
    external user id and friendly name claims (handles nested claims such
    as AWS STS session tags), and a JMESPath expression for initial group
    assignment. Group assignment runs only on first provisioning.
  • Controller wiring: verifier -> resolver -> auth.GenerateJWTLogin.
  • contrib/auth/acl ListUsers?external_id= short-circuit so the
    resolver's GetUserByExternalID works through the HTTP auth service.
  • Config surface under auth.jwt.* with defaults
    (external_user_id_claim_ref=/sub, auth_source=jwt).
  • Regenerated python / java / rust SDKs for the new endpoint + model.
  • Docs: dedicated security/jwt-login.md (nav: Security -> Authentication
    -> JWT IdP Login), brief pointer from security/authentication.md, and
    a new auth.jwt block in reference/configuration.md.

Dependency changes: github.com/go-jose/go-jose/v4 and
github.com/go-openapi/jsonpointer promoted from indirect to direct
(already in go.sum).

Testing Details

  • Unit tests under pkg/auth/jwtidp/ cover:
    • Verifier: valid RS256/ES256 tokens, bad signature, expired/nbf,
      wrong iss/aud, HMAC rejection, unknown kid triggering JWKS
      refresh, JWKS fetch failure.
    • Resolver: JSON-pointer extraction for existing vs. new users,
      friendly-name persist on/off, JMESPath group derivation, missing
      required claims.
    • Claims helper: JSON-pointer traversal including nested objects.
  • make gen clean; make checks-validator (lint + client diff) clean.
  • Manually exercised against a local JWKS server and a test IdP to
    confirm the 501 path, the provisioning path, and token refresh on
    unknown kid.

Breaking Change?

No. The endpoint is additive and returns 501 Not Implemented unless
auth.jwt.jwks_url is configured. No existing API / CLI / client
behaviour changes; SDK regeneration only adds the new model and call.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 20, 2026

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions Bot added area/API Improvements or additions to the API area/testing Improvements or additions to tests docs Improvements or additions to documentation dependencies Pull requests that update a dependency file area/auth IAM, authorization, authentication, audit, AAA, and integrations with all those area/sdk/python labels Apr 20, 2026
@gmintoco gmintoco force-pushed the feature/jwt-idp-login branch 3 times, most recently from ef4def6 to c4318b3 Compare April 20, 2026 08:43
Token-exchange endpoint that verifies a JWT issued by an external IdP
against a configured JWKS and returns a lakeFS AuthenticationToken.
Enabled iff auth.jwt.jwks_url is set; responds 501 Not Implemented
otherwise.

- pkg/auth/jwtidp: JWKS-backed verifier (RS*/ES*/PS*; HMAC rejected)
  with on-demand refresh on unknown kid, 60s clock-skew leeway, and
  required-claim exact-match checks.
- Claim-to-user resolver using RFC 6901 JSON pointers for external id
  and friendly name, and a JMESPath expression for initial group
  assignment. Group assignment runs only on first provisioning.
- Controller wiring: verifier -> resolver -> auth.GenerateJWTLogin.
- contrib/auth/acl ListUsers?external_id= short-circuit so the
  resolver's GetUserByExternalID works through the HTTP auth service.
- Config surface under auth.jwt.* with defaults
  (external_user_id_claim_ref=/sub, auth_source=jwt).
- Regenerated python/java/rust SDKs for the new endpoint + model.
- Docs: dedicated security/jwt-login.md (nav: Security ->
  Authentication -> JWT IdP Login), brief pointer from
  security/authentication.md, new auth.jwt block in reference/
  configuration.md.

Dependency changes: go-jose/v4 and go-openapi/jsonpointer promoted
from indirect to direct (already in go.sum).
@gmintoco gmintoco force-pushed the feature/jwt-idp-login branch from c4318b3 to d132ce3 Compare April 20, 2026 08:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/API Improvements or additions to the API area/auth IAM, authorization, authentication, audit, AAA, and integrations with all those area/sdk/python area/testing Improvements or additions to tests contributor dependencies Pull requests that update a dependency file docs Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support plain JWT authentication (vendor-agnostic token exchange)

3 participants