|
16 | 16 |
|
17 | 17 | This code should be used by applications in Google Compute Engine-based |
18 | 18 | environments (such as Google App Engine flexible environment, Google |
19 | | -Compute Engine, or Google Container Engine) to provide an extra layer |
20 | | -of assurance that a request was authorized by IAP. |
21 | | -
|
22 | | -For applications running in the App Engine standard environment, use |
23 | | -App Engine's Users API instead. |
| 19 | +Compute Engine, Google Container Engine, Google App Engine) to provide |
| 20 | +an extra layer of assurance that a request was authorized by IAP. |
24 | 21 | """ |
25 | 22 | # [START iap_validate_jwt] |
26 | | -from google.auth import jwt |
27 | | -import requests |
28 | | - |
29 | | - |
30 | | -def validate_iap_jwt_from_app_engine(iap_jwt, cloud_project_number, |
31 | | - cloud_project_id): |
32 | | - """Validate a JWT passed to your App Engine app by Identity-Aware Proxy. |
33 | | -
|
34 | | - Args: |
35 | | - iap_jwt: The contents of the X-Goog-IAP-JWT-Assertion header. |
36 | | - cloud_project_number: The project *number* for your Google Cloud project. |
37 | | - This is returned by 'gcloud projects describe $PROJECT_ID', or |
38 | | - in the Project Info card in Cloud Console. |
39 | | - cloud_project_id: The project *ID* for your Google Cloud project. |
40 | | -
|
41 | | - Returns: |
42 | | - (user_id, user_email, error_str). |
43 | | - """ |
44 | | - expected_audience = '/projects/{}/apps/{}'.format( |
45 | | - cloud_project_number, cloud_project_id) |
46 | | - return _validate_iap_jwt(iap_jwt, expected_audience) |
| 23 | +from google.auth.transport import requests |
| 24 | +from google.oauth2 import id_token |
47 | 25 |
|
48 | 26 |
|
49 | | -def validate_iap_jwt_from_compute_engine(iap_jwt, cloud_project_number, |
50 | | - backend_service_id): |
51 | | - """Validate an IAP JWT for your (Compute|Container) Engine service. |
| 27 | +def validate_iap_jwt(iap_jwt, expected_audience): |
| 28 | + """Validate an IAP JWT. |
52 | 29 |
|
53 | 30 | Args: |
54 | 31 | iap_jwt: The contents of the X-Goog-IAP-JWT-Assertion header. |
55 | | - cloud_project_number: The project *number* for your Google Cloud project. |
56 | | - This is returned by 'gcloud projects describe $PROJECT_ID', or |
57 | | - in the Project Info card in Cloud Console. |
58 | | - backend_service_id: The ID of the backend service used to access the |
59 | | - application. See |
| 32 | + expected_audience: The Signed Header JWT audience. See |
60 | 33 | https://cloud.google.com/iap/docs/signed-headers-howto |
61 | 34 | for details on how to get this value. |
62 | 35 |
|
63 | 36 | Returns: |
64 | 37 | (user_id, user_email, error_str). |
65 | 38 | """ |
66 | | - expected_audience = '/projects/{}/global/backendServices/{}'.format( |
67 | | - cloud_project_number, backend_service_id) |
68 | | - return _validate_iap_jwt(iap_jwt, expected_audience) |
69 | | - |
70 | 39 |
|
71 | | -def _validate_iap_jwt(iap_jwt, expected_audience): |
72 | 40 | try: |
73 | | - # Retrieve public key for token signature verification. |
74 | | - key_id = jwt.decode_header(iap_jwt).get('kid') |
75 | | - if not key_id: |
76 | | - return (None, None, '**ERROR: no key ID**') |
77 | | - key = get_iap_key(key_id) |
78 | | - |
79 | | - # Verify token signature, expiry and audience. |
80 | | - decoded_jwt = jwt.decode(iap_jwt, certs=key, audience=expected_audience) |
81 | | - |
82 | | - # Verify token issuer. |
83 | | - if decoded_jwt.get('iss') != 'https://cloud.google.com/iap': |
84 | | - return (None, None, '**ERROR: invalid issuer**') |
85 | | - |
| 41 | + decoded_jwt = id_token.verify_token( |
| 42 | + iap_jwt, requests.Request(), audience=expected_audience, |
| 43 | + certs_url='https://www.gstatic.com/iap/verify/public_key') |
86 | 44 | return (decoded_jwt['sub'], decoded_jwt['email'], '') |
87 | | - except (ValueError, requests.exceptions.RequestException) as e: |
| 45 | + except Exception as e: |
88 | 46 | return (None, None, '**ERROR: JWT validation error {}**'.format(e)) |
89 | 47 |
|
90 | | - |
91 | | -def get_iap_key(key_id): |
92 | | - """Retrieves a public key from the list published by Identity-Aware Proxy, |
93 | | - re-fetching the key file if necessary. |
94 | | - """ |
95 | | - key_cache = get_iap_key.key_cache |
96 | | - key = key_cache.get(key_id) |
97 | | - if not key: |
98 | | - # Re-fetch the key file. |
99 | | - resp = requests.get( |
100 | | - 'https://www.gstatic.com/iap/verify/public_key') |
101 | | - if resp.status_code != 200: |
102 | | - raise Exception( |
103 | | - 'Unable to fetch IAP keys: {} / {} / {}'.format( |
104 | | - resp.status_code, resp.headers, resp.text)) |
105 | | - key_cache = resp.json() |
106 | | - get_iap_key.key_cache = key_cache |
107 | | - key = key_cache.get(key_id) |
108 | | - if not key: |
109 | | - raise Exception('Key {!r} not found'.format(key_id)) |
110 | | - return key |
111 | | - |
112 | | - |
113 | | -# Used to cache the Identity-Aware Proxy public keys. This code only |
114 | | -# refetches the file when a JWT is signed with a key not present in |
115 | | -# this cache. |
116 | | -get_iap_key.key_cache = {} |
117 | 48 | # [END iap_validate_jwt] |
0 commit comments