1515"""Example use of a service account to authenticate to Identity-Aware Proxy."""
1616
1717# [START iap_make_request]
18- import google .auth
19- import google .auth .app_engine
20- import google .auth .compute_engine .credentials
21- import google .auth .iam
2218from google .auth .transport .requests import Request
23- import google .oauth2 .credentials
24- import google .oauth2 .service_account
19+ from google .oauth2 import id_token
2520import requests
26- import requests_toolbelt .adapters .appengine
27-
28-
29- IAM_SCOPE = 'https://www.googleapis.com/auth/iam'
30- OAUTH_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token'
3121
3222
3323def make_iap_request (url , client_id , method = 'GET' , ** kwargs ):
@@ -49,56 +39,9 @@ def make_iap_request(url, client_id, method='GET', **kwargs):
4939 if 'timeout' not in kwargs :
5040 kwargs ['timeout' ] = 90
5141
52- # Figure out what environment we're running in and get some preliminary
53- # information about the service account.
54- bootstrap_credentials , _ = google .auth .default (
55- scopes = [IAM_SCOPE ])
56- if isinstance (bootstrap_credentials ,
57- google .oauth2 .credentials .Credentials ):
58- raise Exception ('make_iap_request is only supported for service '
59- 'accounts.' )
60- elif isinstance (bootstrap_credentials ,
61- google .auth .app_engine .Credentials ):
62- requests_toolbelt .adapters .appengine .monkeypatch ()
63-
64- # For service account's using the Compute Engine metadata service,
65- # service_account_email isn't available until refresh is called.
66- bootstrap_credentials .refresh (Request ())
67-
68- signer_email = bootstrap_credentials .service_account_email
69- if isinstance (bootstrap_credentials ,
70- google .auth .compute_engine .credentials .Credentials ):
71- # Since the Compute Engine metadata service doesn't expose the service
72- # account key, we use the IAM signBlob API to sign instead.
73- # In order for this to work:
74- #
75- # 1. Your VM needs the https://www.googleapis.com/auth/iam scope.
76- # You can specify this specific scope when creating a VM
77- # through the API or gcloud. When using Cloud Console,
78- # you'll need to specify the "full access to all Cloud APIs"
79- # scope. A VM's scopes can only be specified at creation time.
80- #
81- # 2. The VM's default service account needs the "Service Account Actor"
82- # role. This can be found under the "Project" category in Cloud
83- # Console, or roles/iam.serviceAccountActor in gcloud.
84- signer = google .auth .iam .Signer (
85- Request (), bootstrap_credentials , signer_email )
86- else :
87- # A Signer object can sign a JWT using the service account's key.
88- signer = bootstrap_credentials .signer
89-
90- # Construct OAuth 2.0 service account credentials using the signer
91- # and email acquired from the bootstrap credentials.
92- service_account_credentials = google .oauth2 .service_account .Credentials (
93- signer , signer_email , token_uri = OAUTH_TOKEN_URI , additional_claims = {
94- 'target_audience' : client_id
95- })
96-
97- # service_account_credentials gives us a JWT signed by the service
98- # account. Next, we use that to obtain an OpenID Connect token,
99- # which is a JWT signed by Google.
100- google_open_id_connect_token = get_google_open_id_connect_token (
101- service_account_credentials )
42+ # Obtain an OpenID Connect (OIDC) token from metadata server or using service
43+ # account.
44+ google_open_id_connect_token = id_token .fetch_id_token (Request (), client_id )
10245
10346 # Fetch the Identity-Aware Proxy-protected URL, including an
10447 # Authorization header containing "Bearer " followed by a
@@ -108,48 +51,13 @@ def make_iap_request(url, client_id, method='GET', **kwargs):
10851 headers = {'Authorization' : 'Bearer {}' .format (
10952 google_open_id_connect_token )}, ** kwargs )
11053 if resp .status_code == 403 :
111- raise Exception ('Service account {} does not have permission to '
112- 'access the IAP-protected application.' .format (
113- signer_email ))
54+ raise Exception ('Service account does not have permission to '
55+ 'access the IAP-protected application.' )
11456 elif resp .status_code != 200 :
11557 raise Exception (
11658 'Bad response from application: {!r} / {!r} / {!r}' .format (
11759 resp .status_code , resp .headers , resp .text ))
11860 else :
11961 return resp .text
12062
121-
122- def get_google_open_id_connect_token (service_account_credentials ):
123- """Get an OpenID Connect token issued by Google for the service account.
124-
125- This function:
126-
127- 1. Generates a JWT signed with the service account's private key
128- containing a special "target_audience" claim.
129-
130- 2. Sends it to the OAUTH_TOKEN_URI endpoint. Because the JWT in #1
131- has a target_audience claim, that endpoint will respond with
132- an OpenID Connect token for the service account -- in other words,
133- a JWT signed by *Google*. The aud claim in this JWT will be
134- set to the value from the target_audience claim in #1.
135-
136- For more information, see
137- https://developers.google.com/identity/protocols/OAuth2ServiceAccount .
138- The HTTP/REST example on that page describes the JWT structure and
139- demonstrates how to call the token endpoint. (The example on that page
140- shows how to get an OAuth2 access token; this code is using a
141- modified version of it to get an OpenID Connect token.)
142- """
143-
144- service_account_jwt = (
145- service_account_credentials ._make_authorization_grant_assertion ())
146- request = google .auth .transport .requests .Request ()
147- body = {
148- 'assertion' : service_account_jwt ,
149- 'grant_type' : google .oauth2 ._client ._JWT_GRANT_TYPE ,
150- }
151- token_response = google .oauth2 ._client ._token_endpoint_request (
152- request , OAUTH_TOKEN_URI , body )
153- return token_response ['id_token' ]
154-
15563# [END iap_make_request]
0 commit comments