Skip to main content

Authenticate with a JWT

This guide describes how to configure Strongbox to accept JWTs from an external issuer for login. Unlike SPIFFE authentication, which is specific to SPIFFE JWT-SVIDs, the jwt auth method accepts any well-formed JWT and applies configurable claim-based constraints to decide whether to grant access.

Typical use cases:

  • A workload or service authenticates using a JWT issued by an internal identity provider that is not SPIFFE-compatible.
  • A CI/CD pipeline uses a short-lived JWT from a platform like GitLab or GitHub Actions to log in to Strongbox.
  • An application uses a JWT from a third-party identity provider and maps its claims to Strongbox token policies.

Overview

The jwt auth method has two configuration levels:

  • A jwt entry (per tenant) holds the key source and global validation settings: where to get public keys, which signing algorithms are allowed, issuer, and clock skew tolerance.
  • A role (one or more per jwt entry) defines the claim constraints and token settings for a particular class of JWT bearer.

Login requires the caller to supply the tenant, the jwt entry name, the role name, and the raw JWT. Strongbox verifies the signature, runs the claim checks for the named role, and if everything passes creates a Strongbox token.


Configure key sources

JWKS URI discovery

Use a JWKS endpoint when the issuer publishes public keys over HTTPS. Strongbox fetches keys on first login and caches them, refreshing in the background at the configured interval.

supctl create strongbox authentication jwt <<EOF
name: my-idp
jwks-uri: https://idp.example.com/.well-known/openid-configuration
jwks-use-root-ca-certs: true
jwks-tls-verify: true
jwks-refresh-interval: 5m
jwks-request-timeout: 5s
jwks-cache-max-age: 1h
allowed-algorithms:
- es256
allowed-clock-skew: 60s
require-exp: true
issuer: https://idp.example.com
verbose-logging: false
distribute:
to: all
EOF

If the JWKS endpoint is behind a private CA, supply the CA certificate and set jwks-use-root-ca-certs: false to pin it:

supctl create strongbox authentication jwt <<EOF
name: my-internal-idp
jwks-uri: https://idp.internal.example.com/keys
jwks-ca-cert: |
-----BEGIN CERTIFICATE-----
MIIBqTCCAU+gAwIBAgIRAP...
-----END CERTIFICATE-----
jwks-use-root-ca-certs: false
jwks-tls-verify: true
allowed-algorithms:
- es256
- rs256
issuer: https://idp.internal.example.com
distribute:
to: all
EOF

Static PEM keys

Use static keys if you want to pin public keys explicitly or the issuer does not publish a JWKS endpoint:

supctl create strongbox authentication jwt <<EOF
name: static-jwt
pem-keys:
- |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMqF/WCzxlF47KAvHHprweGi8KkTS
JxaYyk6HsdWjmW4l/RGDaNEpmM8mbh4ozbC0/cVzDDijFuGvuL2GHcu9sA==
-----END PUBLIC KEY-----
allowed-algorithms:
- es256
allowed-clock-skew: 30s
require-exp: true
verbose-logging: true
distribute:
to: all
EOF

Configure roles

A role ties claim constraints to token settings. Each jwt entry can have multiple roles for different classes of bearer.

Simple role with audience and policy mapping

supctl create strongbox authentication jwt my-idp roles <<EOF
name: default
bound-audiences:
- my-service
user-claim: sub
token-policies:
- user
token-ttl: 8h
token-max-ttl: 24h
EOF

Role with bound claims

Use bound-claims to restrict login to JWTs that carry specific claim values. All entries must match:

supctl create strongbox authentication jwt my-idp roles <<EOF
name: prod-workload
bound-audiences:
- strongbox
bound-claims:
env: production
team: platform
user-claim: sub
token-policies:
- workload
token-ttl: 1h
token-max-ttl: 4h
EOF

Role with required claims

Use required-claims to ensure certain claims are present in the JWT without constraining their values:

supctl create strongbox authentication jwt my-idp roles <<EOF
name: ci-pipeline
required-claims:
- project_id
- pipeline_id
bound-audiences:
- strongbox
user-claim: sub
token-policies:
- ci
token-ttl: 15m
token-num-uses: 10
EOF

Role with claim mappings

Map JWT claim values into Strongbox token metadata. The metadata can be used by policies for fine-grained access control:

supctl create strongbox authentication jwt my-idp roles <<EOF
name: mapped
bound-audiences:
- my-service
user-claim: sub
claim-mappings:
project_path: project
namespace_path: namespace
environment: env
token-policies:
- user
token-ttl: 4h
EOF

Role with policy claim

Allow the JWT to carry its own list of policies:

supctl create strongbox authentication jwt my-idp roles <<EOF
name: self-policy
bound-audiences:
- strongbox
user-claim: sub
policies-claim: strongbox_policies
token-ttl: 8h
EOF

When the JWT contains "strongbox_policies": ["user", "deployer"], those policies are added to the token in addition to any configured token-policies.


Login

Pass the tenant, jwt entry name, role, and the JWT to jwt-login. The resulting Strongbox token is saved automatically to your supctl profile:

supctl do jwt-login --tenant telco --jwt-auth my-idp --role default \
--token eyJhbGciOiJFUzI1NiIsImtpZCI6ImtleS0xIn0.eyJzdWIiOiJ3b3JrbG9hZC0xIiwiYXVkIjpbIm15LXNlcnZpY2UiXSwiZXhwIjoxNzY4OTEyMDA2LCJpYXQiOjE3Njg5MTE3MDYsImlzcyI6Imh0dHBzOi8vaWRwLmV4YW1wbGUuY29tIn0.SIGNATURE
token: 09715875-ac45-4ad7-bfad-00eea3949094
expires-in: 28800
expires: 2026-03-05T22:00:00Z
accessor: 3be91214-176c-4b4d-bae7-4c432f342661
creation-time: 2026-03-05T14:00:00Z
renewal-time: 2026-03-05T14:00:00Z

In scripts or CI pipelines where the JWT is in an environment variable:

supctl do jwt-login --tenant telco --jwt-auth my-idp --role ci-pipeline \
--token "$CI_JOB_JWT"

GitLab CI/CD example

GitLab issues a short-lived OIDC JWT for each pipeline job. Configure Strongbox to accept it:

supctl create strongbox authentication jwt <<EOF
name: gitlab
jwks-uri: https://gitlab.example.com/-/jwks
jwks-use-root-ca-certs: true
jwks-tls-verify: true
allowed-algorithms:
- rs256
issuer: https://gitlab.example.com
distribute:
to: all
EOF

supctl create strongbox authentication jwt gitlab roles <<EOF
name: deploy
bound-audiences:
- strongbox
bound-claims:
project_path: mygroup/myproject
ref: main
ref_type: branch
user-claim: sub
token-policies:
- deployer
token-ttl: 15m
token-num-uses: 5
EOF

In the .gitlab-ci.yml:

deploy:
id_tokens:
STRONGBOX_JWT:
aud: strongbox
script:
- supctl do jwt-login --tenant prod --jwt-auth gitlab --role deploy
--token "$STRONGBOX_JWT"
- supctl do secrets/my-app get

Verbose logging

Enable verbose logging on a jwt entry or a role to troubleshoot claim validation failures. Log entries appear in the system:logs volga topic on the site where the login was processed.

supctl merge strongbox authentication jwt my-idp <<EOF
verbose-logging: true
EOF

Or enable it per role only:

supctl merge strongbox authentication jwt my-idp roles default <<EOF
verbose-logging: true
EOF

Remember to disable verbose logging after debugging - it logs full JWT claim sets which may include sensitive data.


Token settings reference

All token settings available on approles and other auth methods are also available on jwt roles:

SettingDescription
token-ttlInitial TTL for created tokens
token-max-ttlMaximum lifetime, including renewals
token-explicit-max-ttlHard cap on token lifetime
token-policiesPolicies to assign to the token
token-bound-cidrsRestrict token use to these source CIDRs
token-auto-bound-cidrsBind token to host or network of login source
token-num-usesMaximum number of uses before the token is revoked
token-periodIf set, token is periodic (renewable indefinitely within the period)
token-no-default-policyExclude the default policy from the token
token-typedefault, user, or service
token-spiffe-jwtIssue a SPIFFE JWT-SVID alongside the token
token-spiffe-x509Issue a SPIFFE X.509-SVID alongside the token