Accessing IAP protected endpoint using service account via Python leads to 401 (azp/sub mismatch)

357 views Asked by At

I am trying to access a (GKE) application sitting behind IAP using the official GCP Python libraries and service account credentials.

The code is (taken from https://cloud.google.com/iap/docs/samples/iap-make-request)

from google.oauth2 import id_token
from google.auth.transport.requests import Request

open_id_connect_token = id_token.fetch_id_token(Request(), client_id)

requests.request(
  "GET",
  "https://myapp.com",
   headers={"Authorization": "Bearer {}".format(open_id_connect_token)}
)

where client_id is the oauth client ID used by IAP (something like 8xxxxxxxxx.apps.googleusercontent.com) and the GOOGLE_APPLICATION_CREDENTIALS env var points to the JSON key file of a service account. The service account has the appropriate IAM permissions to access the IAP-protected application.

I've also added the following IAP settings using gcloud iap settings set SETTING_FILE --project=my-project where my-project is the project to which the IAP and the application belongs.

access_settings:
  oauth_settings:
    programmatic_clients: ["8xxxxxxxxx.apps.googleusercontent.com"]

I was mainly following this guide: https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_a_service_account

I would expect that the HTTP call made by request would yield a 2xx response, or at least reach the application. However, it returns 401 with the following error:

Invalid IAP credentials: Service Account doesn't match the authorized party for this application. 
('sub' claim (1yyyyyyyyyyyyy) doesn't match expected value ([email protected]))

Here, the value 1yyyyyyyyyyyyy is actually the client_id property in the service account JSON file. So it looks like the token produced by the Python library (which calls https://oauth2.googleapis.com/token behind the scenes), has an unexpected combination of sub and azp. This is the payload of the token:

{
  "aud": "8xxxxxxxxx.apps.googleusercontent.com",
  "azp": "[email protected]",
  "email": "[email protected]",
  "email_verified": true,
  "exp": 1696584237,
  "iat": 1696580637,
  "iss": "https://accounts.google.com",
  "sub": "1yyyyyyyyyyyyy"
}

I have been trying the following, with no change of the outcome:

  • use different service accounts (also from the same project as the IAP lives in)
  • tried to add the service account's client_id into the access_settings.oauth_settings.programmatic_clients configuration, but that is rejected by gcloud.
  • use different flags in the call of gcloud iap settings set SETTING_FILE, e.g. --resource-type, --organization, or --service
  • find a way to influence the token payload by looking at the Python source code

Does anyone know how to obtain a token for a service account that is accepted by IAP?

1

There are 1 answers

0
Tim On

In the end, I resorted to the following workaround:

Instead of using the Python SDK to generate the token, I use the gcloud CLI, i.e.

gcloud auth print-identity-token --audiences=<CLIENT_ID>