Skip to main content

Documentation Index

Fetch the complete documentation index at: https://snakysec.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

ADR-003 — X.509 certificate auth over client_secret for Entra ID

Date: 2025-02-01 Status: Accepted Deciders: Nicolas (founder)

Context

The platform authenticates to Microsoft Entra ID as a multi-tenant application to obtain access tokens for Microsoft Graph API and Exchange Online. Two auth methods are available: client_secret (shared password) and certificate-based (client_assertion JWT signed with RSA private key).

Decision

X.509 certificate auth (client_assertion / private_key_jwt) is the primary authentication method. client_secret remains supported as a fallback for dev/legacy environments.

Rationale

  • Security: Certificates cannot be extracted from Entra ID portal and used elsewhere; secrets can be copy-pasted. Recommended by Microsoft for production applications.
  • Compliance: Microsoft’s own SCuBA guidelines (MS.AAD) and CIS M365 recommend service principal certificate auth
  • No secret expiry surprises: Certificates have explicit expiry dates managed in Vault; client secrets have hard 2-year max in Entra ID and are often forgotten until they expire
  • Audit trail: Certificate thumbprint (x5t) is logged in Entra ID sign-in logs — attribution is unambiguous

Implementation

  • lib/auth-cert.ts: createClientAssertion() → RS256 JWT, 10-minute window, unique jti (replay protection)
  • x5t thumbprint computed from DER-encoded cert (SHA-1, base64url) — cached in module scope
  • applyClientAuth() transparently selects cert vs secret based on env vars
  • NextAuth v5 token.request override handles the initial code exchange with client_assertion

Certificate rotation procedure

See runbook: docs/runbooks/key-rotation.md

Consequences

  • ENTRA_CERT_PRIVATE_KEY + ENTRA_CERT_PEM stored in Vault; set via vault kv patch
  • Certificate must be registered in Entra ID app registration (Certificates & secrets)
  • openssl req -x509 -newkey rsa:2048 ... — RSA-2048 minimum; RSA-3072 recommended for new certs post-2026
  • Tests: src/lib/__tests__/auth-cert.test.ts validates JWT structure + replay protection

Alternatives rejected

  • Managed Identity: Only works on Azure-hosted infrastructure; our Docker/VPS deployment target doesn’t support it
  • Federated credentials (OIDC): Viable for GitLab CI pipelines — partially implemented via ENTRA_OIDC_TOKEN in the audit worker