15
15
"""Example use of a service account to authenticate to Identity-Aware Proxy."""
16
16
17
17
# [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
22
18
from google .auth .transport .requests import Request
23
- import google .oauth2 .credentials
24
- import google .oauth2 .service_account
19
+ from google .oauth2 import id_token
25
20
import 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'
31
21
32
22
33
23
def make_iap_request (url , client_id , method = 'GET' , ** kwargs ):
@@ -49,56 +39,9 @@ def make_iap_request(url, client_id, method='GET', **kwargs):
49
39
if 'timeout' not in kwargs :
50
40
kwargs ['timeout' ] = 90
51
41
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 )
102
45
103
46
# Fetch the Identity-Aware Proxy-protected URL, including an
104
47
# Authorization header containing "Bearer " followed by a
@@ -108,48 +51,13 @@ def make_iap_request(url, client_id, method='GET', **kwargs):
108
51
headers = {'Authorization' : 'Bearer {}' .format (
109
52
google_open_id_connect_token )}, ** kwargs )
110
53
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.' )
114
56
elif resp .status_code != 200 :
115
57
raise Exception (
116
58
'Bad response from application: {!r} / {!r} / {!r}' .format (
117
59
resp .status_code , resp .headers , resp .text ))
118
60
else :
119
61
return resp .text
120
62
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
-
155
63
# [END iap_make_request]
0 commit comments