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:
| Setting | Description |
|---|---|
token-ttl | Initial TTL for created tokens |
token-max-ttl | Maximum lifetime, including renewals |
token-explicit-max-ttl | Hard cap on token lifetime |
token-policies | Policies to assign to the token |
token-bound-cidrs | Restrict token use to these source CIDRs |
token-auto-bound-cidrs | Bind token to host or network of login source |
token-num-uses | Maximum number of uses before the token is revoked |
token-period | If set, token is periodic (renewable indefinitely within the period) |
token-no-default-policy | Exclude the default policy from the token |
token-type | default, user, or service |
token-spiffe-jwt | Issue a SPIFFE JWT-SVID alongside the token |
token-spiffe-x509 | Issue a SPIFFE X.509-SVID alongside the token |