Search code examples
pythongoogle-cloud-platformjwtgoogle-authentication

Google Cloud: sign a JWT while impersonating a service account


Goal:
I have a cloud function that impersonates a service account based on the service account email provided to it. I then want to use the impersonated service account to sign a jwt.

What I tried:
I have set up the impersonation of the service account like this:

credentials = google.auth.impersonated_credentials.Credentials(
        source_credentials=SOURCE_CREDENTIALS,
        target_principal=SA_EMAIL,
        target_scopes=TARGET_SCOPES,
        lifetime=300)

This brings me to a sub question: What target scopes do I need to specify?

After creating the impersonated credentials, I try to use signer (docs) to sign the payload of the JWT:

payload = {
        'iat': now,
        "exp": now + expiry_length,
        [...]
    }

signer = target_credentials.signer
jwt = google.auth.jwt.encode(signer, payload)

The error message I receive is:

AttributeError: 'Credentials' object has no attribute 'key_id'

Question:
I suspect I am not on the right track with my approach using signer.
How is it possible to achieve my goal?


Solution

  • In your example code, you have only create the payload section. A signed JWT also requires a header.

    The header looks like this:

    header = {
            'kid': pkey_id,    # Signing Private Key ID
            "alg": "RS256",    # Google uses SHA256withRSA
            "typ": "JWT"
    }
    

    The header and payload are base64 encoded and then concatenated with a dot (period symbol) in between. You sign the combined data.

    The problem that you will have is that you need to private key id (pkey_id in my example). That value is located in the service account JSON key file. However, Google does not publish the JSON key file that is used for signing. The key used is private to Google. You can get the private key by signing some data first as the pkey_id is returned as part of the response. Then sign your JWT.

    I recommend that you switch from using the Sign Blob API and use the SignJwt API instead. The SignJwt API does not require that you create a JWT header.

    IAM Service Account Credentials API

    Method: projects.serviceAccounts.signJwt