Authenticate workloads with Google Cloud auth libraries
Stay organized with collections
Save and categorize content based on your preferences.
This document shows you how to use Workload Identity Federation with Google Cloud authentication libraries, known as auth libraries, to authenticate workloads from third-party identity providers such as AWS, Microsoft Azure, and providers that support OpenID Connect (OIDC) or SAML 2.0.
Workload Identity Federation lets applications running outside Google Cloud access Google Cloud resources without using service account keys. The Google auth libraries enable this by exchanging external credentials for short-lived Google Cloud access tokens.
For authentication, you can obtain external credentials using the following methods:
- Standard mechanism for common setups.
- Custom credential suppliers for complex workflows that require you to write your own code.
Before you begin
Enable the required APIs.
Roles required to enable APIs
To enable APIs, you need the
serviceusage.services.enablepermission. If you created the project, then you likely already have this permission through the Owner role (roles/owner). Otherwise, you can get this permission through the Service Usage Admin role (roles/serviceusage.serviceUsageAdmin). Learn how to grant roles.Configure Workload Identity Federation with your identity provider.
Authenticate using the standard credential mechanism
For commonly supported third-party identity providers, you can use the Google Cloud auth library's built-in capabilities to authenticate your workloads by generating a credential configuration file. This file provides the necessary information for the auth libraries to federate identities from external providers.
The credential configuration file, typically loaded using the
GOOGLE_APPLICATION_CREDENTIALS environment variable, can instruct the auth
libraries to obtain the third-party subject token using one of the following methods:
- File-sourced: The library reads the subject token from a local file. A separate process must ensure this file contains a valid, unexpired token.
- URL-sourced: The library fetches the subject token by making a request to a specified local URL endpoint.
- Executable-sourced: The library runs a configured executable command. The standard output of the executable is expected to contain the subject token.
Generate the credential configuration file for your specific provider:
- AWS
- Azure
OIDC or SAML identity provider
This page includes instructions for the following:
Use the credential configuration file to authenticate.
To let the Google Cloud client libraries automatically locate and use your credential configuration file, set the
GOOGLE_APPLICATION_CREDENTIALSenvironment variable to the path of the generated JSON file.Export the environment variable in your shell:
bash export GOOGLE_APPLICATION_CREDENTIALS=/path/to/your/config.jsonAfter you set the environment variable, the client libraries handle the authentication flow.
The following code sample shows how to make an authenticated call to a Google Cloud API:
Node.js
To learn how to install and use the client library for IAM, see IAM client libraries. For more information, see the IAM Node.js API reference documentation.
To authenticate to IAM, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
// Imports the Google Cloud client library.
const{Storage}=require('@google-cloud/storage');
// Instantiates a client. If you don't specify credentials when constructing
// the client, the client library will look for credentials in the
// environment.
conststorage=newStorage ();
// Makes an authenticated API request.
asyncfunctionlistBuckets(){
try{
constresults=awaitstorage.getBuckets ();
const[buckets]=results;
console.log('Buckets:');
buckets.forEach(bucket=>{
console.log(bucket.name);
});
}catch(err){
console.error('ERROR:',err);
}
}
listBuckets();Python
To learn how to install and use the client library for IAM, see IAM client libraries. For more information, see the IAM Python API reference documentation.
To authenticate to IAM, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
defimplicit():
fromgoogle.cloudimport storage
# If you don't specify credentials when constructing the client, the
# client library will look for credentials in the environment.
storage_client = storage .Client ()
# Make an authenticated API request
buckets = list(storage_client.list_buckets ())
print(buckets)
Java
To learn how to install and use the client library for IAM, see IAM client libraries. For more information, see the IAM Java API reference documentation.
To authenticate to IAM, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
staticvoidauthImplicit(){
// If you don't specify credentials when constructing the client, the client library will
// look for credentials via the environment variable GOOGLE_APPLICATION_CREDENTIALS.
Storagestorage=StorageOptions.getDefaultInstance().getService();
System.out.println("Buckets:");
Page<Bucket>buckets=storage.list();
for(Bucketbucket:buckets.iterateAll()){
System.out.println(bucket.toString());
}
}Go
To learn how to install and use the client library for IAM, see IAM client libraries. For more information, see the IAM Go API reference documentation.
To authenticate to IAM, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
import(
"context"
"fmt"
"io"
"cloud.google.com/go/storage"
"google.golang.org/api/iterator"
)
// authenticateImplicitWithAdc uses Application Default Credentials
// to automatically find credentials and authenticate.
funcauthenticateImplicitWithAdc(wio.Writer ,projectIdstring)error{
// projectId := "your_project_id"
ctx:=context.Background()
// NOTE: Replace the client created below with the client required for your application.
// Note that the credentials are not specified when constructing the client.
// The client library finds your credentials using ADC.
client,err:=storage.NewClient(ctx)
iferr!=nil{
returnfmt.Errorf("NewClient: %w",err)
}
deferclient.Close()
it:=client.Buckets (ctx,projectId)
for{
bucketAttrs,err:=it.Next()
iferr==iterator.Done{
break
}
iferr!=nil{
returnerr
}
fmt.Fprintf(w,"Bucket: %v\n",bucketAttrs.Name)
}
fmt.Fprintf(w,"Listed all storage buckets.\n")
returnnil
}
Authenticate using custom credential suppliers
If your environment doesn't support the built-in capabilities of the Google auth library or if you want to implement custom logic to supply credentials to the Google auth library, use custom credential suppliers to authenticate your workloads.
Access resources from AWS
When you initialize the authentication client, provide a custom implementation of a credential supplier. The client instance defers to the supplier to retrieve AWS security credentials to exchange for a Google Cloud access token. The supplier must return valid and unexpired credentials when the client calls it.
The authentication client doesn't cache the returned AWS security credentials or region, so implement caching in the supplier to prevent redundant requests for the same resources.
The following code samples show how you can set up access to Google Cloud resources from AWS with a custom credential supplier.
Node.js
To learn how to install and use the client library for IAM, see IAM client libraries. For more information, see the IAM Node.js API reference documentation.
To authenticate to IAM, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
const{AwsClient}=require('google-auth-library');
const{fromNodeProviderChain}=require('@aws-sdk/credential-providers');
constfs=require('fs');
constpath=require('path');
const{STSClient}=require('@aws-sdk/client-sts');
const{Storage}=require('@google-cloud/storage');
/**
* Custom AWS Security Credentials Supplier.
*
* This implementation resolves AWS credentials using the default Node provider
* chain from the AWS SDK. This allows fetching credentials from environment
* variables, shared credential files (~/.aws/credentials), or IAM roles
* for service accounts (IRSA) in EKS, etc.
*/
classCustomAwsSupplier{
constructor(){
this.region=null;
this.awsCredentialsProvider=fromNodeProviderChain();
}
/**
* Returns the AWS region. This is required for signing the AWS request.
* It resolves the region automatically by using the default AWS region
* provider chain, which searches for the region in the standard locations
* (environment variables, AWS config file, etc.).
*/
asyncgetAwsRegion(_context){
if(this.region){
returnthis.region;
}
constclient=newSTSClient({});
this.region=awaitclient.config.region();
if(!this.region){
thrownewError(
'CustomAwsSupplier: Unable to resolve AWS region. Please set the AWS_REGION environment variable or configure it in your ~/.aws/config file.'
);
}
returnthis.region;
}
/**
* Retrieves AWS security credentials using the AWS SDK's default provider chain.
*/
asyncgetAwsSecurityCredentials(_context){
constawsCredentials=awaitthis.awsCredentialsProvider();
if(!awsCredentials.accessKeyId||!awsCredentials.secretAccessKey){
thrownewError(
'Unable to resolve AWS credentials from the node provider chain. '+
'Ensure your AWS CLI is configured, or AWS environment variables (like AWS_ACCESS_KEY_ID) are set.'
);
}
return{
accessKeyId:awsCredentials.accessKeyId,
secretAccessKey:awsCredentials.secretAccessKey,
token:awsCredentials.sessionToken,
};
}
}
/**
* Authenticates with Google Cloud using AWS credentials and retrieves bucket metadata.
*
* @param {string} bucketName The name of the bucket to retrieve.
* @param {string} audience The Workload Identity Pool audience.
* @param {string} [impersonationUrl] Optional Service Account impersonation URL.
*/
asyncfunctionauthenticateWithAwsCredentials(
bucketName,
audience,
impersonationUrl
){
constcustomSupplier=newCustomAwsSupplier();
constclientOptions={
audience:audience,
subject_token_type:'urn:ietf:params:aws:token-type:aws4_request',
service_account_impersonation_url:impersonationUrl,
aws_security_credentials_supplier:customSupplier,
};
constauthClient=newAwsClient (clientOptions);
conststorage=newStorage ({
authClient:authClient,
});
const[metadata]=awaitstorage.bucket(bucketName).getMetadata();
returnmetadata;
}Python
To learn how to install and use the client library for IAM, see IAM client libraries. For more information, see the IAM Python API reference documentation.
To authenticate to IAM, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
importjson
importos
importsys
importboto3
fromgoogle.authimport aws
fromgoogle.authimport exceptions
fromgoogle.cloudimport storage
classCustomAwsSupplier(aws.AwsSecurityCredentialsSupplier):
"""Custom AWS Security Credentials Supplier using Boto3."""
def__init__(self):
"""Initializes the Boto3 session, prioritizing environment variables for region."""
# Explicitly read the region from the environment first.
region = os.getenv("AWS_REGION") or os.getenv("AWS_DEFAULT_REGION")
# If region is None, Boto3's discovery chain will be used when needed.
self.session = boto3.Session(region_name=region)
self._cached_region = None
defget_aws_region(self, context, request) -> str:
"""Returns the AWS region using Boto3's default provider chain."""
if self._cached_region:
return self._cached_region
self._cached_region = self.session.region_name
if not self._cached_region:
raise exceptions .GoogleAuthError(
"Boto3 was unable to resolve an AWS region."
)
return self._cached_region
defget_aws_security_credentials(
self, context, request=None
) -> aws.AwsSecurityCredentials:
"""Retrieves AWS security credentials using Boto3's default provider chain."""
creds = self.session.get_credentials()
if not creds:
raise exceptions .GoogleAuthError(
"Unable to resolve AWS credentials from Boto3."
)
return aws.AwsSecurityCredentials(
access_key_id=creds.access_key,
secret_access_key=creds.secret_key,
session_token=creds.token,
)
defauthenticate_with_aws_credentials(bucket_name, audience, impersonation_url=None):
"""Authenticates using the custom AWS supplier and gets bucket metadata.
Returns:
dict: The bucket metadata response from the Google Cloud Storage API.
"""
custom_supplier = CustomAwsSupplier()
credentials = aws.Credentials(
audience=audience,
subject_token_type="urn:ietf:params:aws:token-type:aws4_request",
service_account_impersonation_url=impersonation_url,
aws_security_credentials_supplier=custom_supplier,
scopes=["https://www.googleapis.com/auth/devstorage.read_only"],
)
storage_client = storage .Client (credentials=credentials)
bucket = storage_client.get_bucket (bucket_name)
return bucket._properties
Java
To learn how to install and use the client library for IAM, see IAM client libraries. For more information, see the IAM Java API reference documentation.
To authenticate to IAM, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
importcom.google.auth.oauth2.AwsCredentials ;
importcom.google.auth.oauth2.AwsSecurityCredentials ;
importcom.google.auth.oauth2.AwsSecurityCredentialsSupplier ;
importcom.google.auth.oauth2.ExternalAccountSupplierContext ;
importcom.google.auth.oauth2.GoogleCredentials ;
importcom.google.cloud.storage.Bucket ;
importcom.google.cloud.storage.Storage ;
importcom.google.cloud.storage.StorageOptions ;
importcom.google.gson.Gson;
importcom.google.gson.reflect.TypeToken;
importjava.io.IOException;
importjava.io.Reader;
importjava.lang.reflect.Type;
importjava.nio.file.Files;
importjava.nio.file.Paths;
importjava.util.Map;
importsoftware.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
importsoftware.amazon.awssdk.auth.credentials.AwsSessionCredentials;
importsoftware.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
importsoftware.amazon.awssdk.regions.Region;
importsoftware.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
publicstaticBucket authenticateWithAwsCredentials(
StringgcpWorkloadAudience,StringsaImpersonationUrl,StringgcsBucketName)
throwsIOException{
CustomAwsSuppliercustomSupplier=newCustomAwsSupplier();
AwsCredentials .BuildercredentialsBuilder=
AwsCredentials .newBuilder()
.setAudience(gcpWorkloadAudience)
// This token type indicates that the subject token is an AWS Signature Version 4 signed
// request. This is required for AWS Workload Identity Federation.
.setSubjectTokenType("urn:ietf:params:aws:token-type:aws4_request")
.setAwsSecurityCredentialsSupplier (customSupplier);
if(saImpersonationUrl!=null){
credentialsBuilder.setServiceAccountImpersonationUrl(saImpersonationUrl);
}
GoogleCredentials credentials=credentialsBuilder.build();
Storage storage=StorageOptions .newBuilder().setCredentials(credentials).build().getService ();
returnstorage.get (gcsBucketName);
}
/**
* Custom AWS Security Credentials Supplier.
*
* <p>This implementation resolves AWS credentials and regions using the default provider chains
* from the AWS SDK (v2). This supports environment variables, ~/.aws/credentials, and EC2/EKS
* metadata.
*/
privatestaticclass CustomAwsSupplierimplementsAwsSecurityCredentialsSupplier {
privatefinalAwsCredentialsProviderawsCredentialsProvider;
privateStringregion;
publicCustomAwsSupplier(){
// The AWS SDK handles caching internally.
this.awsCredentialsProvider=DefaultCredentialsProvider.create();
}
@Override
publicStringgetRegion(ExternalAccountSupplierContext context){
if(this.region==null){
RegionawsRegion=newDefaultAwsRegionProviderChain().getRegion ();
if(awsRegion==null){
thrownewIllegalStateException(
"Unable to resolve AWS region. Ensure AWS_REGION is set or configured.");
}
this.region=awsRegion.id();
}
returnthis.region;
}
@Override
publicAwsSecurityCredentials getCredentials(ExternalAccountSupplierContext context){
software.amazon.awssdk.auth.credentials.AwsCredentials credentials=
this.awsCredentialsProvider.resolveCredentials();
if(credentials==null){
thrownewIllegalStateException("Unable to resolve AWS credentials.");
}
StringsessionToken=null;
if(credentialsinstanceofAwsSessionCredentials){
sessionToken=((AwsSessionCredentials)credentials).sessionToken();
}
returnnewAwsSecurityCredentials (
credentials.accessKeyId(),credentials.secretAccessKey(),sessionToken);
}
}Access resources from OIDC and SAML
When you initialize the authentication client, provide a custom token supplier to provide a subject token that is exchanged for a Google Cloud access token. The supplier must return a valid and unexpired subject token when the client calls it.
The authentication client doesn't cache the returned token, so implement caching in the supplier to prevent redundant requests for the same subject token.
The following code samples show how you can set up access to Google Cloud resources from providers that support OpenID Connect (OIDC) or SAML 2.0 with a custom credential supplier.
Node.js
To learn how to install and use the client library for IAM, see IAM client libraries. For more information, see the IAM Node.js API reference documentation.
To authenticate to IAM, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
const{IdentityPoolClient}=require('google-auth-library');
const{Storage}=require('@google-cloud/storage');
const{Gaxios}=require('gaxios');
constfs=require('fs');
constpath=require('path');
/**
* A custom SubjectTokenSupplier that authenticates with Okta using the
* Client Credentials grant flow.
*/
classOktaClientCredentialsSupplier{
constructor(domain,clientId,clientSecret){
constcleanDomain=domain.endsWith('/')?domain.slice(0,-1):domain;
this.oktaTokenUrl=`${cleanDomain}/oauth2/default/v1/token`;
this.clientId=clientId;
this.clientSecret=clientSecret;
this.accessToken=null;
this.expiryTime=0;
this.gaxios =newGaxios ();
}
/**
* Main method called by the auth library. It will fetch a new token if one
* is not already cached.
* @returns {Promise<string>} A promise that resolves with the Okta Access token.
*/
asyncgetSubjectToken(){
constisTokenValid=
this.accessToken && Date.now() < this.expiryTime-60*1000;
if(isTokenValid){
returnthis.accessToken;
}
const{accessToken,expiresIn}=awaitthis.fetchOktaAccessToken();
this.accessToken=accessToken;
this.expiryTime=Date.now()+expiresIn*1000;
returnthis.accessToken;
}
/**
* Performs the Client Credentials grant flow with Okta.
*/
asyncfetchOktaAccessToken(){
constparams=newURLSearchParams();
params .append('grant_type','client_credentials');
params .append('scope','gcp.test.read');
constauthHeader=
'Basic '+
Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');
try{
constresponse=awaitthis.gaxios .request ({
url:this.oktaTokenUrl,
method:'POST',
headers:{
Authorization:authHeader,
'Content-Type':'application/x-www-form-urlencoded',
},
data:params .toString(),
});
const{access_token,expires_in}=response.data;
if(access_token && expires_in){
return{accessToken:access_token,expiresIn:expires_in};
}else{
thrownewError(
'Access token or expires_in not found in Okta response.'
);
}
}catch(error){
thrownewError(
`Failed to authenticate with Okta: ${error.response?.data||error.message}`
);
}
}
}
/**
* Authenticates with Google Cloud using Okta credentials and retrieves bucket metadata.
*
* @param {string} bucketName The name of the bucket to retrieve.
* @param {string} audience The Workload Identity Pool audience.
* @param {string} domain The Okta domain.
* @param {string} clientId The Okta client ID.
* @param {string} clientSecret The Okta client secret.
* @param {string} [impersonationUrl] Optional Service Account impersonation URL.
*/
asyncfunctionauthenticateWithOktaCredentials(
bucketName,
audience,
domain,
clientId,
clientSecret,
impersonationUrl
){
constoktaSupplier=newOktaClientCredentialsSupplier(
domain,
clientId,
clientSecret
);
constauthClient=newIdentityPoolClient ({
audience:audience,
subject_token_type:'urn:ietf:params:oauth:token-type:jwt',
token_url:'https://sts.googleapis.com/v1/token',
subject_token_supplier:oktaSupplier,
service_account_impersonation_url:impersonationUrl,
});
conststorage=newStorage ({
authClient:authClient,
});
const[metadata]=awaitstorage.bucket(bucketName).getMetadata();
returnmetadata;
}Python
To learn how to install and use the client library for IAM, see IAM client libraries. For more information, see the IAM Python API reference documentation.
To authenticate to IAM, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
importjson
importtime
importurllib.parse
fromgoogle.authimport identity_pool
fromgoogle.cloudimport storage
importrequests
classOktaClientCredentialsSupplier:
"""A custom SubjectTokenSupplier that authenticates with Okta.
This supplier uses the Client Credentials grant flow for machine-to-machine
(M2M) authentication with Okta.
"""
def__init__(self, domain, client_id, client_secret):
self.okta_token_url = f"{domain .rstrip('/')}/oauth2/default/v1/token"
self.client_id = client_id
self.client_secret = client_secret
self.access_token = None
self.expiry_time = 0
defget_subject_token(self, context, request=None) -> str:
"""Fetches a new token if the current one is expired or missing."""
if self.access_token and time.time() < self.expiry_time - 60:
return self.access_token
self._fetch_okta_access_token()
return self.access_token
def_fetch_okta_access_token(self):
"""Performs the Client Credentials grant flow with Okta."""
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
}
data = {
"grant_type": "client_credentials",
"scope": "gcp.test.read", # Set scope as per Okta app config.
}
response = requests.post(
self.okta_token_url,
headers=headers,
data=urllib.parse.urlencode(data),
auth=(self.client_id, self.client_secret),
)
response.raise_for_status()
token_data = response.json()
self.access_token = token_data["access_token"]
self.expiry_time = time.time() + token_data["expires_in"]
defauthenticate_with_okta_credentials(
bucket_name, audience, domain, client_id, client_secret, impersonation_url=None
):
"""Authenticates using the custom Okta supplier and gets bucket metadata.
Returns:
dict: The bucket metadata response from the Google Cloud Storage API.
"""
okta_supplier = OktaClientCredentialsSupplier(domain, client_id, client_secret)
credentials = identity_pool.Credentials(
audience=audience,
subject_token_type="urn:ietf:params:oauth:token-type:jwt",
token_url="https://sts.googleapis.com/v1/token",
subject_token_supplier=okta_supplier,
default_scopes=["https://www.googleapis.com/auth/devstorage.read_only"],
service_account_impersonation_url=impersonation_url,
)
storage_client = storage .Client (credentials=credentials)
bucket = storage_client.get_bucket (bucket_name)
return bucket._properties
Java
To learn how to install and use the client library for IAM, see IAM client libraries. For more information, see the IAM Java API reference documentation.
To authenticate to IAM, set up Application Default Credentials. For more information, see Set up authentication for a local development environment.
importcom.google.api.client.json.GenericJson ;
importcom.google.api.client.json.gson.GsonFactory ;
importcom.google.auth.oauth2.ExternalAccountSupplierContext ;
importcom.google.auth.oauth2.GoogleCredentials ;
importcom.google.auth.oauth2.IdentityPoolCredentials ;
importcom.google.auth.oauth2.IdentityPoolSubjectTokenSupplier ;
importcom.google.cloud.storage.Bucket ;
importcom.google.cloud.storage.Storage ;
importcom.google.cloud.storage.StorageOptions ;
importcom.google.gson.Gson;
importcom.google.gson.JsonSyntaxException;
importcom.google.gson.reflect.TypeToken;
importjava.io.BufferedReader;
importjava.io.DataOutputStream;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.io.Reader;
importjava.lang.reflect.Type;
importjava.net.HttpURLConnection;
importjava.net.URL;
importjava.nio.charset.StandardCharsets;
importjava.nio.file.Files;
importjava.nio.file.Paths;
importjava.time.Instant;
importjava.util.Base64;
importjava.util.Map;
publicstaticBucket authenticateWithOktaCredentials(
StringgcpWorkloadAudience,
StringsaImpersonationUrl,
StringgcsBucketName,
StringoktaDomain,
StringoktaClientId,
StringoktaClientSecret)
throwsIOException{
OktaClientCredentialsSupplieroktaSupplier=
newOktaClientCredentialsSupplier(oktaDomain,oktaClientId,oktaClientSecret);
IdentityPoolCredentials .BuildercredentialsBuilder=
IdentityPoolCredentials .newBuilder()
.setAudience(gcpWorkloadAudience)
// This token type indicates that the subject token is a JSON Web Token (JWT).
// This is required for Workload Identity Federation with an OIDC provider like Okta.
.setSubjectTokenType("urn:ietf:params:oauth:token-type:jwt")
.setTokenUrl("https://sts.googleapis.com/v1/token")
.setSubjectTokenSupplier (oktaSupplier);
if(saImpersonationUrl!=null){
credentialsBuilder.setServiceAccountImpersonationUrl(saImpersonationUrl);
}
GoogleCredentials credentials=credentialsBuilder.build();
Storage storage=StorageOptions .newBuilder().setCredentials(credentials).build().getService ();
returnstorage.get (gcsBucketName);
}
/**
* A custom SubjectTokenSupplier that authenticates with Okta using the Client Credentials grant
* flow.
*/
privatestaticclass OktaClientCredentialsSupplierimplementsIdentityPoolSubjectTokenSupplier {
privatestaticfinallongTOKEN_REFRESH_BUFFER_SECONDS=60;
privatefinalStringoktaTokenUrl;
privatefinalStringclientId;
privatefinalStringclientSecret;
privateStringaccessToken;
privateInstantexpiryTime;
publicOktaClientCredentialsSupplier(Stringdomain,StringclientId,StringclientSecret){
// Ensure domain doesn't have a trailing slash for cleaner URL construction
StringcleanedDomain=
domain.endsWith ("/")?domain.substring(0,domain.length()-1):domain;
this.oktaTokenUrl=cleanedDomain+"/oauth2/default/v1/token";
this.clientId=clientId;
this.clientSecret=clientSecret;
}
/**
* Main method called by the auth library. It will fetch a new token if one is not already
* cached.
*/
@Override
publicStringgetSubjectToken(ExternalAccountSupplierContext context)throwsIOException{
// Check if the current token is still valid (with a 60-second buffer).
booleanisTokenValid=
this.accessToken!=null
&& this.expiryTime!=null
&& Instant.now().isBefore(this.expiryTime.minusSeconds(TOKEN_REFRESH_BUFFER_SECONDS));
if(isTokenValid){
returnthis.accessToken;
}
fetchOktaAccessToken();
returnthis.accessToken;
}
/**
* Performs the Client Credentials grant flow by making a POST request to Okta's token endpoint.
*/
privatevoidfetchOktaAccessToken()throwsIOException{
URLurl=newURL(this.oktaTokenUrl);
HttpURLConnectionconn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
conn.setRequestProperty("Accept","application/json");
// The client_id and client_secret are sent in a Basic Auth header.
Stringauth=this.clientId+":"+this.clientSecret;
StringencodedAuth=
Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
conn.setRequestProperty("Authorization","Basic "+encodedAuth);
conn.setDoOutput(true);
try(java.io.OutputStreamout=conn.getOutputStream()){
// Scopes define the permissions the access token will have.
// Update "gcp.test.read" to match your Okta configuration.
Stringparams="grant_type=client_credentials&scope=gcp.test.read";
out.write(params.getBytes(StandardCharsets.UTF_8));
out.flush();
}
intresponseCode=conn.getResponseCode();
if(responseCode==HttpURLConnection.HTTP_OK){
try(BufferedReaderin=
newBufferedReader(
newInputStreamReader(conn.getInputStream(),StandardCharsets.UTF_8))){
GenericJson jsonObject=
GsonFactory .getDefaultInstance().createJsonParser(in).parse(GenericJson .class);
if(jsonObject.containsKey("access_token") && jsonObject.containsKey("expires_in")){
this.accessToken=(String)jsonObject.get("access_token");
NumberexpiresInNumber=(Number)jsonObject.get("expires_in");
this.expiryTime=Instant.now().plusSeconds(expiresInNumber.longValue());
}else{
thrownewIOException("Access token or expires_in not found in Okta response.");
}
}
}else{
thrownewIOException("Failed to authenticate with Okta. Response code: "+responseCode);
}
}
}What's next
- Learn more about Workload Identity Federation.