Skip to content

private_key_jwt: missing x5t thumbprint in JWT assertion header breaks Entra ID / Azure AD #149

@coopernetes

Description

@coopernetes

Summary

When auth.oidc.private-key-path is configured, the loadRsaKey helper in SecurityConfig builds the RSAKey with a random UUID keyID but no x5t (SHA-1 certificate thumbprint). Nimbus therefore sets kid in the JWT assertion header, but Entra ID matches registered certificates by x5t, not kid, causing every token exchange to fail with:

AADSTS700027: The certificate with identifier used to sign the client assertion is not registered on application.

Root Cause

SecurityConfig.loadRsaKey (the private_key_jwt path, SecurityConfig.java around line 583):

return new RSAKey.Builder(publicKey)
        .privateKey((RSAPrivateKey) privateKey)
        .keyID(UUID.randomUUID().toString())   // ← random kid, no x5t
        .build();

MSAL (the Node.js reference implementation for this same app registration) explicitly passes a thumbprint alongside the private key — this is the x5t value Entra requires.

Expected Behaviour

When the X.509 certificate corresponding to the private key is available, its SHA-1 thumbprint should be computed and set as x5t on the RSAKey so Nimbus includes it in the JWT assertion header.

Proposed Fix

Add an optional cert-path field to OidcAuthConfig. When set, loadRsaKey loads the certificate, computes SHA-1(cert.getEncoded()), and passes it to RSAKey.Builder.x509CertThumbprint(Base64URL). Providers that match on kid (Keycloak, Dex) are unaffected — cert-path stays blank and behaviour is unchanged.

auth:
  oidc:
    private-key-path: /run/secrets/gitproxy-oidc-key.pem
    cert-path: /run/secrets/gitproxy-oidc-cert.pem   # new — required for Entra ID

Environment

  • git-proxy-java 1.0.0-beta.3
  • IdP: Entra ID (Azure AD) with private_key_jwt client authentication
  • Spring Security NimbusJwtClientAuthenticationParametersConverter

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions