Replace risky static credentials with OpenID Connect, a passwordless authentication based on short-lived tokens for AWS, GCP, and Azure, while applying the least privilege principle.

In episode 11 we secured the controller: disabled anonymous access, chose an authentication scheme, and implemented RBAC. But there is one security gap that often slips through: static cloud credentials.
Many teams store AWS Access Keys, GCP Service Account Keys, or Azure Client Secrets permanently in Jenkins Credentials. Such credentials are like a master key — valid forever, never changing, and once leaked, anyone can take over all your cloud resources. The risk is real: hacker bots scan repositories and public files within minutes, and once a key is exposed, the impact can spread into the production environment.
The modern solution is OpenID Connect (OIDC). The concept is simple: Jenkins no longer stores static credentials, but instead requests a short-lived token each time a build runs, then exchanges it for temporary credentials in the cloud. In this episode we will understand the flow, why it is safer, and how to implement it on AWS, GCP, and Azure.
Storing static credentials in Jenkins means carrying three burdens at once:
Warning
Do not write static access keys directly in a Jenkinsfile or pipeline parameters. If one is already stored in Jenkins Credentials, plan a migration to OIDC and rotate the key immediately.
OIDC is built on top of the OAuth 2.0 protocol and introduces the ID Token — a JSON Web Token (JWT) containing claims such as issuer, audience, and subject. The key properties that make it suitable for CI/CD:
Jenkins acts as the OIDC Provider: when a build runs, Jenkins issues an ID Token, then the cloud provider verifies the token's signature via Jenkins's JWKS endpoint and exchanges it for temporary credentials. The analogy is a plane boarding pass: valid once, contains a specific destination, and expires after the flight — unlike a room key that works forever.
On AWS, this exchange uses STS AssumeRoleWithWebIdentity. The flow:
withOIDC step to request an ID token.An example of that role's trust policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/jenkins.example.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"jenkins.example.com:aud": "aws-deploy",
"jenkins.example.com:sub": "repo:my-org/my-app:environment:prod"
}
}
}
]
}Once the role is ready, the pipeline simply requests a token and runs the AWS CLI inside it:
pipeline {
agent { label 'linux-runner' }
stages {
stage('Deploy ke AWS Tanpa Password') {
steps {
withOIDC(id: 'aws-deploy', provider: 'aws-oidc') {
sh 'aws sts get-caller-identity'
sh 'aws s3 sync ./dist s3://my-app-bucket --delete'
}
}
}
}
}Tip
The audience value in the pipeline must match exactly the audience in the trust policy. Also restrict the subject claim per repository and environment so tokens from other jobs cannot abuse the same role.
On Google Cloud Platform, the similar concept is called Workload Identity Federation. You create a workload identity pool along with an OIDC provider pointing at the Jenkins issuer, then impersonate a service account. Create the pool and provider with gcloud:
gcloud iam workload-identity-pools create jenkins-pool \
--location=global --display-name="Jenkins Pool"
gcloud iam workload-identity-pools providers create-oidc jenkins-provider \
--location=global --workload-identity-pool=jenkins-pool \
--issuer-uri="https://jenkins.example.com" \
--attribute-mapping="google.subject=assertion.sub"On Microsoft Azure, use federated identity credentials on an App Registration or Managed Identity. Each federated credential defines the issuer, subject, and audience that the Jenkins token must satisfy:
az identity federated-credential create \
--name jenkins-cred \
--identity-name my-app-id \
--resource-group rg-prod \
--issuer "https://jenkins.example.com" \
--subject "repo:my-org/my-app:environment:prod" \
--audiences "azure-deploy"Let's compare the old and new approaches side by side:
| Aspect | Static Credentials | OIDC |
|---|---|---|
| Credential lifetime | Permanent until manually rotated | A few minutes per build |
| Impact if leaked | Full access without time limit | Token expires quickly |
| Rotation | Manual, prone to being forgotten | Automatic every build |
| Access scope | Hard to trace per job | Can be limited per subject & audience |
| Operations | Backup keys, service account files | No cloud secrets stored |
With OIDC, the least privilege principle applies naturally: each job requests the lowest role sufficient for its task, tokens are only valid for a specific context, and there are no permanent credentials that can be stolen and reused repeatedly.
Important
OIDC reduces the risk of key leakage but does not eliminate the need for authorization. Still keep the role permissions as small as possible and separate roles for staging and production.
In episode 12 you have understood why static credentials are dangerous for pipelines, how OIDC works with short-lived ID Tokens, and how Jenkins acts as an OIDC provider exchanging tokens with AWS via STS AssumeRoleWithWebIdentity, GCP via Workload Identity, and Azure via federated identity credentials. You also saw how this approach applies least privilege naturally.
Interestingly, almost all the configuration we have done manually through the UI so far — including security settings, RBAC, and credentials — can actually be written as code.
In episode 13 we will discuss Jenkins Configuration as Code (JCasC): managing the entire controller via a jenkins.yaml file and creating an identical Jenkins in seconds. See you there!