Exchange Generic OIDC Credentials for GCP Credentials using GCP STS Service

This is a sample procedure that will exchange an arbitrary OIDC id_token for a GCP credential.

You can use the GCP credential then to access any service the mapped principal has GCP IAM permissions on.

This article and repo is the second part that explores how to use the workload identity federation capability of GCP which allows for external principals (AWS,Azure or arbitrary OIDC provider) to map to a GCP credential.

The two variations described in this repo will acquire a Google Credential as described here:

  • The “Automatic” way is recommended and is supported by Google
  • The “Manual” way is also covered in this repo but I decided to wrap the steps for that into my own library here github.com/salrashid123/oauth2/google which surfaces the credential as an oauth2.TokenSource for use in any GCP cloud library.

NOTE: the library i’m using for the “manual” way is just there as an unsupported demo of a wrapped oauth2 TokenSource!

You can certainly use either procedure but the Automatic way is included with the supported, standard GCP Client library.

This repository is not supported by Google salrashid123/oauth2/google is also not supported by Google

for AWS based exchanges, see -Exchange AWS Credentials for GCP Credentials using GCP STS Service

You can find the source for this article in the repo here

https://github.com/salrashid123/gcpcompat-oidc

Workload Federation — OIDC

GCP now surfaces a STS Service that will exchange one set of tokens for another using the GCP Secure Token Service (STS) as detailed here. These initial tokens can be either 3rd party OIDC, AWS, Azure or google access_tokens that are downscoped (i.,e attenuated in permission set).

Basically, the STS service will take an OIDC token, validate it, then apply the principal mapping an admin had defined earlier for the provider. A federated or impersonated access_token is then returned to the client that represents this mapped identity. Basically, the client’s token is validated, mapped and exchanged by a GCP service.

To use this tutorial, you need a GCP project with Firebase as the OIDC Provider and the ability to create user/service accounts.

Again, the two types of flows this repo demonstrates:

  • Manual Exchange: In this you manually do all the steps of exchanging a Firebase/Identity Platform id_token for a federated token and then finally use that token
  • Automatic Exchange In this you use the google cloud client libraries to do all the heavy lifting. << This is the recommended approach

It is recommended to do the manual first just to understand this capability and then move onto the automatic

OIDC → GCP Identity → GCP Resource

This tutorial will cover how to use an OIDC token and its claims to a GCP principal:// and principalSet://

  • User: principal:// This maps a unique user identified by OIDC to a GCP identity
  • Group: principalSet:// This maps any user that has a given attrubute declared in the OIDC token to a GCP identity

In both cases, the GCP Identity is a Service Account that the external user impersonates.

Configure OIDC Provider

First we need an OIDC token provider that will give us an id_token. Just for demonstration, we will use Google Cloud Identity Platform as the provider (you can of course use okta, auth0, even google itself).

The GCP project i am using in the example here is called mineral-minutia-820. Identity platform will automatically create a 'bare bones' oidc .well-known endpoint at a url that includes the projectID:

Create OIDC token using Identity Platform

The following shows how to acquire an OIDC token for use with this tutorial. As mentioned, we are using FirebaseAuth/Identity Platform; you can use any other provider as long as the .well-known endpoint is discoverable by GCP

  1. Enable Identity Platform
  2. Add Email/Password as the provider
  3. Note the API_KEY and authDomain value

4. Edit login.js and enter in the API Key/AuthDomain In my case, it is:

var firebaseConfig = {
apiKey: "AIzaSyAf7wesN7auBeyfJQJs5d_QfT24kMH7OG8",
authDomain: "cicp-oidc-test.firebaseapp.com",
projectId: "cicp-oidc-test",
appId: "cicp-oidc-test",
};

5. Create Firebase Service Account

Generate a service account and download it from the firebase console: (note, replace with your projectID in the URL field below)

Save the file as /tmp/svc_account.json

6) Create user

At this pont, user Alice has a custom claim associated with the user. Empirically, the attribute values must be string (i.e, i intentionally set isadmin to (string) true (not boolean))

7) Create id_token

Login as that user using email/password.

The following script actually performs a login and displays the JSON response a firebase/identity platform user would see (i.,e they would see that struct after logging in the browser too)

The access_token is actually a JWT id_token which you can decode at jwt.io:

Notice the isadmin and sub fields there

Some things to note

Configure OIDC Federation

We can now configure the GCP project for OIDC Federation

export PROJECT_ID=`gcloud config get-value core/project`export PROJECT_NUMBER=`gcloud projects describe \
$PROJECT_ID --format='value(projectNumber)'`

Create identity pool

gcloud beta iam workload-identity-pools create oidc-pool-1 \
--location="global" \
--description="OIDC Pool " \
--display-name="OIDC Pool" --project $PROJECT_ID

Configure provider

  • The following command will configure the provider itself. Notice that we specify the issuer URL without the .well-known URL path (since its, well, well-known)

Notice the attribute mapping:

  • google.subject=assertion.sub: This will extract and populate the google subject value from the provided id_token's sub field.
  • attribute.isadmin=assertion.isadmin: This will extract the value of the custom claim isadmin and then make it available for IAM rule later as an assertion

Notice the attribute conditions:

  • attribute.isadmin=='true': This describes the condition that this provider must meet. The provided idToken's isadmin field MUST be set to true
  • attribute.aud=='mineral-minutia-820': This describes the audience value in the token must be set to the project you are using (in my case mineral-minutia-820)

If you set the attribute conditions to something else, you should see an error during authentication:

Unable to exchange token {"error":"unauthorized_client","error_description":"The given credential is rejected by the attribute condition."},

Create GCS Resource

  • Create a test GCP resource like GCS and upload a file
gsutil mb gs://$PROJECT_ID-testecho fooooo > foo.txtgsutil cp foo.txt gs://$PROJECT_ID-test

Create Service Account

  • By default, the OIDC identity will need to map to a Service Account which inturn will have access to the GCS resource. The external identity will get validated by GCP and then will impersonate a GCP Service Account
gcloud iam service-accounts create oidc-federated

Allow federated identity map

This allows a single user alice@domain.com permissions to impersonate the Service Account

This allows any user part of the OIDC issuer that has the claim embedded in the token with key-value isadmin==true

Allow service account access to GCS

Manual

Before you run the sample, you must first get an OIDC Token. See Create OIDC token using Identity Platform above

export OIDC_TOKEN=`node login.js  | jq -r '.user.stsTokenManager.accessToken'`echo $OIDC_TOKEN > /tmp/oidccred.txt

At this point, we are ready to use the OIDC token and exchange it manually

What you should see is the output of the GCS file

Automatic

We are now ready to use the Automatic Application Default Credentials to access the ressource

First configure the ADC bootstrap file:

The output/bootstrap file should look something like this:

Notice the bootstrap file has a pointer to the file where the actual creds exist /tmp/oidccreds.txt. (eventually other cred sources should be supported)

Before you run the sample, you must first get an OIDC Token. See Create OIDC token using Identity Platform below

Remember /tmp/oidccred.txt has the content of the raw OIDC token to use (i.,e is the value of $OIDC_TOKEN)

export OIDC_TOKEN=`node login.js  | jq -r '.user.stsTokenManager.accessToken'`echo $OIDC_TOKEN > /tmp/oidccred.txtexport GOOGLE_APPLICATION_CREDENTIALS=`pwd`/sts-creds.jsongo run main.go    --gcpBucket mineral-minutia-820-cab1 \
--gcpObjectName foo.txt --useADC

At the time of writing (3/14/21), the configuration file (only supports reading of a file that contains the oidc token (/tmp/oidccred.txt) directly. Eventually, other mechanisms like url or executing a binary that returns the oidc token will be supported

Using Federated or IAM Tokens

GCP STS Tokens can be used directly against a few GCP services as described here

Skip step (5) of Exchange Token

What that means is you can skip the step to exchange the GCP Federation token for an Service Account token and directly apply IAM policies on the resource.

This not only saves the step of running the exchange but omits the need for a secondary GCP service account to impersonate.

To use GCS, allow either the mapped identity direct access to the resource. In this case storage.objectAdmin access which we already allowed earlier:

  • Allow Federated Identity IAM access
  • Configure the federated identity access to GCS bucket. This allows a single user alice@domain.com

Notice that in this mode we are directly allowing the federated identity access to a GCS resource

To use Federated tokens, use remove the --useIAMToken flag

If you want to use Federated tokens only with the Automatic flow, delete service_account_impersonation_url declaration in sts-creds.json

Logging

If you used the STS token directly, the principal will appear in the GCS logs if you enabled audit logging

If you used IAM impersonation, you will see the principal performing the impersonation

and then the impersonated account accessing GCS

Notice the protoPayload.authenticationInfo structure between the two types of auth

Organization Policy Restrict

You can also define a GCP Organization Policy that restricts which providers can be enabled for federation

  • constraints/iam.workloadIdentityPoolProviders

For example, for the following test organization, we will define a policy that only allows you to create a workload identity using a the specified OIDC providers URL

Finally, if you’re just looking to write you own STS service for whatever reason, see Serverless Secure Token Exchange Server(STS) and gRPC STS credentials

--

--

--

A collection of technical articles and blogs published or curated by Google Cloud Developer Advocates. The views expressed are those of the authors and don't necessarily reflect those of Google.

Recommended from Medium

What’s wrong with Go’s error handling?

Haze Finance Frequency Mining Competition: $30000 USD Reward For One Winner

Spark SQL Hive Job Conversion

Back to overview

Easy Way to Create a Good Error-Handling in Flutter With Dartz

You’re Doing It Wrong — Recruiting a DevRel

Creating Project Environments in Python

5 Points you should know to start using Google Cloud Platform

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
salmaan rashid

salmaan rashid

More from Medium

Secure Deployments from Gitlab to Google Cloud Platform

How to Fully Automate the Deployment of Google Cloud Platform Projects with Terraform

Deploy Cloud Functions on GCP with Terraform

DIY Google Cloud Storage replication using Cloud Functions