Why use OIDC instead of an API token?
With a static API token, you need to store anek_live_* value as a Kubernetes Secret. If it leaks, an attacker can read your secrets until someone manually revokes it.
With OIDC, Kubernetes generates a fresh token every hour. There is nothing to store, nothing to rotate and nothing to leak.
| Static API Token | OIDC Workload Identity | |
|---|---|---|
| Token lifetime | Days to months | ~1 hour (auto-rotated) |
| Stored as a secret | Yes (Kubernetes Secret) | No |
| Blast radius if leaked | Until manually revoked | Until token expires (~1 hour max) |
| Tied to pod identity | No | Yes (specific service account only) |
How it works
Prerequisites
- A Kubernetes cluster with OIDC discovery enabled (all major cloud providers enable this by default)
kubectlconfigured for your cluster- An Enkryptify workspace with secrets to access
Setup
Gather your cluster information
You need three values:
Example: Verify your issuer is reachable:You should see a JSON response with
| Field | What it is | Example |
|---|---|---|
| Issuer URL | Your cluster’s OIDC issuer URL | https://oidc.eks.us-east-1.amazonaws.com/id/ABC123 |
| Audience | A string you choose (safety check) | enkryptify |
| Subject | The service account identity | system:serviceaccount:production:my-api |
Finding your OIDC issuer URL
Finding your OIDC issuer URL
- Amazon EKS
- Google GKE
- Azure AKS
- DigitalOcean
- k3s
https://oidc.eks.us-east-1.amazonaws.com/id/ABCDEF1234567890issuer and jwks_uri fields.Understanding the subject claim
Understanding the subject claim
The subject must exactly match the Examples:
To check what service account a deployment uses:If empty, the pod uses the
sub claim in your Kubernetes service account token. The format is always:| Namespace | Service Account | Subject |
|---|---|---|
default | default | system:serviceaccount:default:default |
production | my-api | system:serviceaccount:production:my-api |
staging | backend | system:serviceaccount:staging:backend |
default service account in its namespace.Register the identity in Enkryptify
- Navigate to Credentials in the sidebar
- Click “Create credential”
- Select “Kubernetes” and click Continue
- Fill in the form:
- Name — a label (e.g.
prod-api-k8s) - Issuer URL — your cluster’s OIDC issuer URL
- Audience —
enkryptify(or your chosen audience string) - Subject —
system:serviceaccount:<namespace>:<service-account-name> - Permission —
Read only(unless your workload needs to write secrets) - Scope — restrict to the specific projects and environments this workload needs
- Name — a label (e.g.
- Click “Create identity”
Create a Kubernetes service account
If you don’t already have one, create a service account matching the subject from step 2.
service-account.yaml
Configure your pod with a projected token
Kubernetes can mount a short-lived JWT into your pod with a custom audience and expiration.After applying, Kubernetes will:
deployment.yaml
- Generate a JWT signed by the cluster’s private key
- Write it to
/var/run/secrets/tokens/tokeninside the container - Automatically rotate it before it expires (at ~80% of the expiration time)
Revoking an identity
When you decommission a service, revoke its OIDC identity so it can no longer authenticate:- Go to Credentials
- Click the actions menu on the identity row
- Click Revoke
One identity per service account
Each OIDC identity maps to one specific combination of issuer + audience + subject per workspace. If you have multiple apps that need different permissions, create a separate service account and OIDC identity for each:my-apiwith read-write access to project Amy-workerwith read-only access to project B
Troubleshooting
No matching OIDC identity found
No matching OIDC identity found
Enkryptify couldn’t find an identity matching the token’s Decode it to see the claims:Compare
iss, aud and sub claims.Check:- The issuer URL matches your cluster’s issuer exactly (including trailing slashes)
- The audience matches what you configured in the projected volume
- The subject matches the service account:
system:serviceaccount:<namespace>:<name> - The identity has not been revoked
iss, sub and aud with what you registered in Enkryptify.OIDC token validation failed
OIDC token validation failed
The token’s signature couldn’t be verified.Check:
- The issuer URL’s
/.well-known/openid-configurationis publicly accessible - The token hasn’t expired (check the
expclaim) - Your cluster’s OIDC keys haven’t rotated since the token was issued
Failed to fetch OIDC discovery document
Failed to fetch OIDC discovery document
Enkryptify couldn’t reach the issuer’s discovery endpoint.Check:
- The issuer URL is correct and publicly accessible
- There’s no firewall blocking outbound HTTPS from the Enkryptify API
- Try fetching it manually:
curl https://<issuer-url>/.well-known/openid-configuration
Token file not found in the pod
Token file not found in the pod
The projected volume isn’t mounted correctly.Check:
serviceAccountNamein your pod spec matches the service account you created- The
volumeMountsname matches thevolumesname - Restart the deployment:
kubectl rollout restart deployment <name> -n <namespace>
Too many exchange requests
Too many exchange requests
The OIDC exchange endpoint is rate-limited to 10 requests per minute per identity. Cache the returned JWT for its 15-minute lifetime instead of exchanging on every request.
Best practices
- Use the narrowest scope possible. Only grant access to the projects and environments the pod needs.
- Use read-only unless you need write. Most pods only read secrets.
- One service account per app. Don’t share service accounts across different applications.
- Set a short token expiration.
expirationSeconds: 3600(1 hour) is a good default. Kubernetes rotates it at ~80% of the lifetime. - Revoke identities you no longer need. When you decommission a service, clean up its OIDC identity.