Part 3. Token exchange from GCP to Azure
Tags: googlecloud, azure, multicloud, security
Published: 2023-02-14 13:22:32

---
series: Multi-Cloud identity federation explained
---

In the previous article of this multi-cloud identity federation series, we saw how to securely exchange an Azure access token (on the form of a JWT token) with a GCP access token, to access private GCP resource from Azure. You are probably wondering how to do the reverse operation, you are in the right spot !

## The big picture

To request Azure APIs from GCP environment, we will need the same two objects as before : a GCP service account and an Azure App Registration. The process is straightforward, because there is no Workload Identity Federation-like product on Azure, everything happens in the App registration configuration : 

- Generate a GCP ID token for the source service account, either via the Metadata Server (recommended way for production applications), or via the CLI or IAM REST API (need to have impersonate permissions on the SA)
- Ask the Azure OAuth2 Authorization server to exchange the token for an Azure access token representing the target App registration.
- Enjoy your APIs requests 🙂


![Token exchange between Google Cloud and Azure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yl1ljldu2zbhlvbqgsuq.png)



## 1. Generate a GCP ID token

First you need to generate your ID token on behalf of the source service account. Why ID token and not access token ? Because access token on GCP are opaque and are not decodable ! We need a JWT token here : Azure need to be able to check the content of the token to map the App registration on the other side and check if the issuer is Google.

For this step you can either use the Metadata Server if your workload is running in GCP Compute context (recommended way for production applications), or use `gcloud` CLI (or any other method available). The result will be a valid identity token. Note that you need to match the audience with the audience you will configure in the next step. The recommended value (according to Azure) is `api://AzureADTokenExchange`.

1. **Using the magic Metadata Server URL :** 

```bash
curl -H "Metadata-Flavor: Google" \
  'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=api://AzureADTokenExchange'
```

**b. Using the gcloud CLI (for testing purpose only)**

```bash
gcloud auth print-identity-token \
	--impersonate-service-account=SOURCE_SERVICE_ACCOUNT_EMAIL \
	--audiences=api://AzureADTokenExchange
```

## 2. Create a federated credentials in your Azure App Registration

Secondly, configure your target Azure App Registration to allow impersonation from the source GCP service account. This operation happens in the *Federated Credential* section of your App registration in Azure Active Directory. You will need to specify the trusted issuer, the subject and the audience.

How to get these values ? By decoding the GCP identity token, of course ! Just as usual, you can go to [https://jwt.io/](https://jwt.io/) and inspect the content of your token’s payload : 

```bash
{
  "aud": "api://AzureADTokenExchange",
  "azp": "106697322240068434726",
  "exp": 1676301170,
  "iat": 1676297570,
  "iss": "https://accounts.google.com",
  "sub": "106697322240068434726"
}
```

- `iss` = [https://accounts.google.com](https://accounts.google.com) is the issuer of the token (Google)
- `sub` = subject is the source service account ID. You can also find this info in the GCP console, on the service account page (Unique ID)
- `aud` is the default audience value, defined in the first step.

From these informations you can create your federated-credentials settings :

```bash
az ad app federated-credential create --id APPLICATION_ID --parameters credential.json
("credential.json" contains the following content)
{
    "name": "GcpFederation",
    "issuer": "https://accounts.google.com",
    "subject": "106697322240068434726",
    "description": "Test GCP federation",
    "audiences": [
        "api://AzureADTokenExchange"
    ]
}
```

## 3. Exchange your GCP ID token for an Azure Access token

Last but not least, you need to exchange your GCP ID token for an Azure Access token to do whatever you want on Azure side : you need to make a request to the Azure Oauth2 Authorization Server by specifying the following parameters :

- `client_id` to your App registration ID,
- `scope`  to the desired scope depending on the future usage of your token,
- `client_assertion_type` fixed to `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` for this operation
- `grant_type`  to `client_credentials`
- And the most important: your GCP ID token under `client_assertion`

```bash
curl GET 'https://login.partner.microsoftonline.cn/TENANT_ID/oauth2/v2.0/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=APP_ID' \
--data-urlencode 'scope=https://storage.azure.com/.default' \ # or whatever other scope you might want
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode 'client_assertion=GCP_ID_TOKEN' \
--data-urlencode 'grant_type=client_credentials'

# Reponse

{
    "token_type": "Bearer",
    "expires_in": 3599,
    "ext_expires_in": 3599,
    "access_token": "eyJ0eXAiO********" # JWT token
}
```

After decoding the GCP token, if the audience, issuer and subject match, your are good to go for a brand new Azure access token ! You can now access the APIs that match the scope you specified (here the Azure Storage API) :

```bash
curl GET 'https://STORAGE_ACCOUNT_NAME.blob.core.windows.net/CONTAINER' \
--header 'x-ms-version: 2020-04-08' \
--header 'Authorization: Bearer AZURE_TOKEN'

# Response 200 OK
```

No need to store client_id and client_secret in GCP and risk a security breach ! Just use Azure Active Directory federated credentials !

In the next 2 articles we will see concret implementation in Python and Terraform for your production applications.