Configuring OIDC with Google Cloud Platform
Use OpenID Connect within your tasks to authenticate with Google Cloud Platform.
OpenID Connect (OIDC) allows your tasks to access resources in Google Cloud Platform (GCP) without
needing to store the GCP credentials. This enables a seamless and safe interaction between your
tasks and the cloud services they rely on.
This guide will walk you through how to configure GCP to trust Airplane OIDC. See
Airplane OpenID Connect provider for an overview of how to generate tokens and the
token format.
Add Airplane as a Workload Identity Provider
Add Airplane as a Workload Identity Provider
To create a new Workload Identity Provider, you first have to
create a Workload Identity Pool.
Once that is done, you need to
create a new Workload Identity Provider
inside the Workload Identity Pool. Provide the following configuration for your Workflow Identity
Provider:
- Provider type: OpenID Connect (OIDC)
- Issuer (URL): https://api.airplane.dev
- Audiences: You can optionally provide a custom audience that can be set when generating the
OIDC token. For example, if you are generating in a JST as
{{auth.idToken('https://your-app.example.com')}}
, set the audience in your GCP configuration tohttps://your-app.example.com
. - Provider attributes mapping: The attribute mappings map claims in the token JWT to assertions
you can make about the request (like the task slug or email of the user executing a task).
Attributes used in a Common Expression Language (CEL) expression or IAM policy must be mapped from
the incoming token attributes. For example, to use the task slug claim in a policy, you can map
the Google attribute
attribute.task_slug
to the OIDCassertion.task_slug
.
Connect the Workload Identity Pool to a GCP Service Account
Connect the Workload Identity Pool to a GCP Service Account
If you do not already have a GCP Service Account, create one with the desired
IAM permissions you want your
Airplane task to be able to access. See the
GCP documentation for how to create a
service account.
Once you have a service account, you need to grant the external identity permissions to impersonate
your service account. You will need to add an IAM permission on the service account itself. See the
GCP documentation
for how to do so. You can reference the Workload Identity Pool you created in the previous step via
the
principalSet://
protocol.Be sure to configure the external identity to restrict the identities allowed to impersonate the
account. Doing so will ensure that tokens from other Airplane teams or tasks cannot be used to
access your cloud resources. For example, to restrict impersonation to only Airplane tasks with the
slug
test_oidc_gcp
, grant the roles/iam.workloadIdentityUser
IAM role to the external identity
(note the attribute.task_slug/test_oidc_gcp
suffix):Copied1principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/attribute.task_slug/test_oidc_gcp
where
PROJECT_NUMBER
is your Google Cloud project number, and POOL_ID
is the ID of the Workload
Identity Pool you created. This also assumes you added task_slug
as a mapped attribute.Generate a token with the Airplane SDK to impersonate an GCP service account
Generate a token with the Airplane SDK to impersonate an GCP service account
Now that GCP is configured to accept Airplane OIDC tokens, you can generate a token from within an
Airplane task to impersonate your service account.
typescriptCopied1import airplane from "airplane";23const getGCPCredentials = async () => {4const token = await airplane.auth.idToken("https://your-app.example.com");56const resp = await fetch("https://sts.googleapis.com/v1/token", {7method: "POST",8body: JSON.stringify({9// This is the audience of the Workload Identity Pool you created10audience:11"//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",12grantType: "urn:ietf:params:oauth:grant-type:token-exchange",13requestedTokenType: "urn:ietf:params:oauth:token-type:access_token",14scope: "https://www.googleapis.com/auth/cloud-platform",15subjectTokenType: "urn:ietf:params:oauth:token-type:jwt",16subjectToken: token,17}),18});1920// credentials will look like:21// {22// access_token: ...,23// issued_token_type: 'urn:ietf:params:oauth:token-type:access_token',24// token_type: 'Bearer',25// expires_in: 359926// }27const credentials = await resp.json();28return credentials.access_token;29};
pythonCopied1import airplane2import requests34def get_gcp_credentials():5token = airplane.auth.id_token("gcp")67resp = requests.post(8"https://sts.googleapis.com/v1/token",9data={10# This is the audience of the Workload Identity Pool you created11"audience": "//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",12"grantType": "urn:ietf:params:oauth:grant-type:token-exchange",13"requestedTokenType": "urn:ietf:params:oauth:token-type:access_token",14"scope": "https://www.googleapis.com/auth/cloud-platform",15"subjectTokenType": "urn:ietf:params:oauth:token-type:jwt",16"subjectToken": token,17},18)1920# credentials will look like:21# {22# "access_token": ...,23# "issued_token_type": 'urn:ietf:params:oauth:token-type:access_token',24# "token_type": 'Bearer',25# "expires_in": 359926# }27credentials = resp.json()28return credentials['access_token']
Use the token to make an GCP API call
Use the token to make an GCP API call
You can now use the generated GCP credentials to interact with the GCP API.
typescriptCopied1import airplane from "airplane";23export default airplane.task(4{5slug: "get_gcs_bucket",6},7async (params) => {8const accessToken = getGCPCredentials();910const bucketName = "your-bucket-name";11const objectName = "your-object-name";1213const resp = await fetch(`https://storage.googleapis.com/${bucketName}/${objectName}`, {14headers: {15Authorization: `Bearer ${accessToken}`,16},17});1819console.log(resp.text());20},21);2223const getGCPCredentials = async () => {24const token = await airplane.auth.idToken("https://your-app.example.com");2526const resp = await fetch("https://sts.googleapis.com/v1/token", {27method: "POST",28body: JSON.stringify({29// This is the audience of the Workload Identity Pool you created30audience:31"//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",32grantType: "urn:ietf:params:oauth:grant-type:token-exchange",33requestedTokenType: "urn:ietf:params:oauth:token-type:access_token",34scope: "https://www.googleapis.com/auth/cloud-platform",35subjectTokenType: "urn:ietf:params:oauth:token-type:jwt",36subjectToken: token,37}),38});3940const credentials = await resp.json();41return credentials.access_token;42};
pythonCopied1import airplane234@airplane.task()5def get_gcs_bucket():6access_token = get_gcp_credentials()78bucket_name = 'your-bucket-name'9object_name = 'your-object-name'1011response = requests.get(12f"https://storage.googleapis.com/{bucket_name}/{object_name}",13headers={"Authorization": f"Bearer {access_token}"},14)1516print(response.content)171819def get_gcp_credentials():20token = airplane.auth.id_token("gcp")2122resp = requests.post(23"https://sts.googleapis.com/v1/token",24data={25# This is the audience of the Workload Identity Pool you created26"audience": "//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",27"grantType": "urn:ietf:params:oauth:grant-type:token-exchange",28"requestedTokenType": "urn:ietf:params:oauth:token-type:access_token",29"scope": "https://www.googleapis.com/auth/cloud-platform",30"subjectTokenType": "urn:ietf:params:oauth:token-type:jwt",31"subjectToken": token,32},33)3435credentials = resp.json()36return credentials["access_token"]
SDK reference
SDK reference
See Auth SDK reference for more information.