Access user identity information provided by IAP
Use cryptographic verification to prevent spoofing of user identity information
What you'll learn
How to write and deploy a simple App Engine app using Python
How to enable and disable IAP to restrict access to your app
How to get user identity information from IAP into your app
How to cryptographically verify information from IAP to protect against spoofing
Introduction
Authenticating users of your web app is often necessary, and usually requires special programming in your app. For Google Cloud apps you can hand those responsibilities off to the Identity-Aware Proxy service. If you only need to restrict access to selected users there are no changes necessary to the application. Should the application need to know the user's identity (such as for keeping user preferences server-side) Identity-Aware Proxy can provide that with minimal application code.
What is Identity-Aware Proxy?
Identity-Aware Proxy (IAP) is a Google Cloud service that intercepts web requests sent to your application, authenticates the user making the request using the Google Identity Service, and only lets the requests through if they come from a user you authorize. In addition, it can modify the request headers to include information about the authenticated user.
Cloud Platform project in this session is set to qwiklabs-gcp-04-c077032b024e.
Use “gclou
student_04_e3f1df1e0d22@cloudshell:~ (qwiklabs-gcp-04-c077032b024e)$ gsutil cp gs://spls/gsp499/user-authentication-with-iap.zip .
Copying gs://spls/gsp499/user-authentication-with-iap.zip...
/ [1 files][ 46.6 KiB/ 46.6 KiB]
Operation completed over 1 objects/46.6 KiB.
student_04_e3f1df1e0d22@cloudshell:~ (qwiklabs-gcp-04-c077032b024e)$ unzip user-authentication-with-iap.zip
Archive: user-authentication-with-iap.zip
creating: user-authentication-with-iap/
creating: user-authentication-with-iap/2-HelloUser/
inflating: user-authentication-with-iap/2-HelloUser/app.yaml
extracting: user-authentication-with-iap/2-HelloUser/requirements.txt
entication-with-iap/.git/packed-refs
student_04_e3f1df1e0d22@cloudshell:~ (qwiklabs-gcp-04-c077032b024e)$ cd user-authentication-with-iap
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap (qwiklabs-gcp-04-c077032b024e)$ ls -l
total 32
drwxr-xr-x 3 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 4096 Oct 1 2021 1-HelloWorld
drwxr-xr-x 3 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 4096 Oct 1 2021 2-HelloUser
drwxr-xr-x 3 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 4096 Oct 1 2021 3-HelloVerifiedUser
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 1101 Oct 1 2021 CONTRIBUTING.md
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 11358 Oct 1 2021 LICENSE
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 249 Oct 1 2021 README.md
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap (qwiklabs-gcp-04-c077032b024e)$ cd 1-HelloWorld
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$ ls -l
total 16
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 631 Oct 1 2021 app.yaml
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 1643 Oct 1 2021 main.py
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 13 Oct 1 2021 requirements.txt
drwxr-xr-x 2 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 4096 Oct 1 2021 templates
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$ ls -l templates/
total 8
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 223 Oct 1 2021 index.html
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 395 Oct 1 2021 privacy.html
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$ cat main.py
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from flask import Flask, render_template
app = Flask(__name__)
# Disable browser caching so changes in each step are always shown
@app.after_request
def set_response_headers(response):
response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
response.headers['Pragma'] = 'no-cache'
response.headers['Expires'] = '0'
return response
@app.route('/', methods=['GET'])
def say_hello():
page = render_template('index.html')
return page
@app.route('/privacy', methods=['GET'])
def show_policy():
page = render_template('privacy.html')
return page
if __name__ == '__main__':
# This is used when running locally, only to verify it does not have
# significant errors. It cannot demonstrate restricting access using
# Identity-Aware Proxy when run locally, only when deployed.
#
# When deploying to Google App Engine, a webserver process such as
# Gunicorn will serve the app. This can be configured by adding an
# `entrypoint` to app.yaml.
app.run(host='127.0.0.1', port=8080, debug=True)
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$ gcloud app deploy
You are creating an app for project [qwiklabs-gcp-04-c077032b024e].
WARNING: Creating an App Engine application for a project is irreversible and the region
cannot be changed. More information about regions is at
<https://cloud.google.com/appengine/docs/locations>.
Please choose the region where you want your App Engine application located:
[1] asia-east1 (supports standard and flexible)
[2] asia-northeast1 (supports standard and flexible and search_api)
[3] asia-south1 (supports standard and flexible and search_api)
[4] asia-southeast1 (supports standard and flexible)
[5] australia-southeast1 (supports standard and flexible and search_api)
[6] europe-central2 (supports standard and flexible)
[7] europe-west (supports standard and flexible and search_api)
[8] europe-west2 (supports standard and flexible and search_api)
[9] europe-west3 (supports standard and flexible and search_api)
[10] us-central (supports standard and flexible and search_api)
[11] us-east1 (supports standard and flexible and search_api)
[12] us-east4 (supports standard and flexible and search_api)
[13] us-west1 (supports standard and flexible)
[14] us-west2 (supports standard and flexible and search_api)
[15] us-west3 (supports standard and flexible and search_api)
[16] us-west4 (supports standard and flexible and search_api)
[17] cancel
Please enter your numeric choice: 17
ERROR: (gcloud.app.deploy) Aborted by user.
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$ gcloud config list
[accessibility]
screen_reader = True
[component_manager]
disable_update_check = True
[compute]
gce_metadata_read_timeout_sec = 30
[core]
account = student-04-e3f1df1e0d22@qwiklabs.net
disable_usage_reporting = True
project = qwiklabs-gcp-04-c077032b024e
[metrics]
environment = devshell
Your active configuration is: [cloudshell-9846]
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$ gcloud config list
[accessibility]
screen_reader = True
[component_manager]
disable_update_check = True
[compute]
gce_metadata_read_timeout_sec = 30
[core]
account = student-04-e3f1df1e0d22@qwiklabs.net
disable_usage_reporting = True
project = qwiklabs-gcp-04-c077032b024e
[metrics]
environment = devshell
Your active configuration is: [cloudshell-9846]
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$ gcloud app deploy
You are creating an app for project [qwiklabs-gcp-04-c077032b024e].
WARNING: Creating an App Engine application for a project is irreversible and the region
cannot be changed. More information about regions is at
<https://cloud.google.com/appengine/docs/locations>.
Please choose the region where you want your App Engine application located:
[1] asia-east1 (supports standard and flexible)
[2] asia-northeast1 (supports standard and flexible and search_api)
[3] asia-south1 (supports standard and flexible and search_api)
[4] asia-southeast1 (supports standard and flexible)
[5] australia-southeast1 (supports standard and flexible and search_api)
[6] europe-central2 (supports standard and flexible)
[7] europe-west (supports standard and flexible and search_api)
[8] europe-west2 (supports standard and flexible and search_api)
[9] europe-west3 (supports standard and flexible and search_api)
[10] us-central (supports standard and flexible and search_api)
[11] us-east1 (supports standard and flexible and search_api)
[12] us-east4 (supports standard and flexible and search_api)
[13] us-west1 (supports standard and flexible)
[14] us-west2 (supports standard and flexible and search_api)
[15] us-west3 (supports standard and flexible and search_api)
[16] us-west4 (supports standard and flexible and search_api)
[17] cancel
Please enter your numeric choice: 13
Creating App Engine application in project [qwiklabs-gcp-04-c077032b024e] and region [us-west1]....done.
Services to deploy:
descriptor: [/home/student_04_e3f1df1e0d22/user-authentication-with-iap/1-HelloWorld/app.yaml]
source: [/home/student_04_e3f1df1e0d22/user-authentication-with-iap/1-HelloWorld]
target project: [qwiklabs-gcp-04-c077032b024e]
target service: [default]
target version: [20230805t183144]
target url: [https://qwiklabs-gcp-04-c077032b024e.uw.r.appspot.com]
target service account: [qwiklabs-gcp-04-c077032b024e@appspot.gserviceaccount.com]
Do you want to continue (Y/n)? Y
Beginning deployment of service [default]...
Created .gcloudignore file. See `gcloud topic gcloudignore` for details.
Uploading 6 files to Google Cloud Storage
17%
33%
50%
67%
83%
100%
100%
File upload done.
Updating service [default]...done.
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://qwiklabs-gcp-04-c077032b024e.uw.r.appspot.com]
You can stream logs from the command line by running:
$ gcloud app logs tail -s default
To view your application in the web browser run:
$ gcloud app browse
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$ gcloud app browse
Did not detect your browser. Go to this link to view your app:
https://qwiklabs-gcp-04-c077032b024e.uw.r.appspot.com
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$
Restrict access with IAP
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$ gcloud services disable appengineflex.googleapis.com
Operation "operations/acat.p17-319032836437-309f7da8-dc76-492b-8811-aa1b2d647a41" finished successfully.
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/1-HelloWorld (qwiklabs-gcp-04-c077032b024e)$
Task 2. Access user identity information
Once an app is protected with IAP, it can use the identity information that IAP provides in the web request headers it passes through. In this step, the application will get the logged-in user's email address and a persistent unique user ID assigned by the Google Identity Service to that user. That data will be displayed to the user in the welcome page.
In Cloud Shell, change to the folder for this step:
cd ~/user-authentication-with-iap/2-HelloUser
Deploy to App Engine
Since deployment takes a few minutes, start by deploying the app to the App Engine Standard environment for Python:
gcloud app deploy
automatic_scaling:
max_instances: 2student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$ cat requirements.txt
Flask>=1.0.0
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$ ls -l
total 16
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 630 Oct 1 2021 app.yaml
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 1837 Oct 1 2021 main.py
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 13 Oct 1 2021 requirements.txt
drwxr-xr-x 2 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 4096 Oct 1 2021 templates
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$ ls -l /tem
ls: cannot access '/tem': No such file or directory
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$ ls -l templates/
total 8
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 277 Oct 1 2021 index.html
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 395 Oct 1 2021 privacy.html
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$ cat templates/index.html
<!doctype html>
<html>
<head>
<title>IAP Hello User</title>
</head>
<body>
<h1>Hello User</h1>
<p>
Hello, {{ email }}! Your persistent ID is {{ id }}.
</p>
<p>
This is step 2 of the <em>User Authentication with IAP</em>
codelab.
</p>
</body>
</html>
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$ ls -l templates/
total 8
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 277 Oct 1 2021 index.html
-rw-r--r-- 1 student_04_e3f1df1e0d22 student_04_e3f1df1e0d22 395 Oct 1 2021 privacy.html
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$ gcloud app deploy
Services to deploy:
descriptor: [/home/student_04_e3f1df1e0d22/user-authentication-with-iap/2-HelloUser/app.yaml]
source: [/home/student_04_e3f1df1e0d22/user-authentication-with-iap/2-HelloUser]
target project: [qwiklabs-gcp-04-c077032b024e]
target service: [default]
target version: [20230805t190145]
target url: [https://qwiklabs-gcp-04-c077032b024e.uw.r.appspot.com]
target service account: [qwiklabs-gcp-04-c077032b024e@appspot.gserviceaccount.com]
Do you want to continue (Y/n)? Y
Beginning deployment of service [default]...
Created .gcloudignore file. See `gcloud topic gcloudignore` for details.
Uploading 3 files to Google Cloud Storage
33%
67%
100%
100%
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://qwiklabs-gcp-04-c077032b024e.uw.r.appspot.com]
You can stream logs from the command line by running:
$ gcloud app logs tail -s default
To view your application in the web browser run:
$ gcloud app browse
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$
ith-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$
xamine the application files
This folder contains the same set of files as seen in the previous app you deployed, 1-HelloWorld, but two of the files have been changed: main.py and templates/index.html. The program has been changed to retrieve the user information that IAP provides in request headers, and the template now displays that data.
There are two lines in main.py that get the IAP-provided identity data:
user_email = request.headers.get('X-Goog-Authenticated-User-Email')
user_id = request.headers.get('X-Goog-Authenticated-User-ID')
The X-Goog-Authenticated-User- headers are provided by IAP, and the names are case-insensitive, so they could be given in all lower or all upper case if preferred. The render_template statement now includes those values so they can be displayed:
page = render_template('index.html', email=user_email, id=user_id)
The index.html template can display those values by enclosing the names in double curly braces:
Hello, {{ email }}! Your persistent ID is {{ id }}.
As you can see, the provided data is prefixed with accounts.google.com, showing where the information came from. Your application can remove everything up to and including the colon to get the raw values if desired.
Test the updated IAP
Going back to the deployment, when it is ready, you will see a message that you can view your application with gcloud app browse.
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$ gcloud app browse
Did not detect your browser. Go to this link to view your app:
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$
Turn off IAP
What happens to this app if IAP is disabled, or somehow bypassed (such as by other applications running in your same cloud project)? Turn off IAP to see.
In the cloud console window, click Navigation menu > Security > Identity-Aware Proxy.
Click the IAP toggle switch next to App Engine app to turn IAP off. Click TURN OFF.
You will be warned that this will allow all users to access the app.
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$ curl -X GET <your-url-here> -H "X-Goog-Authenticated-User-Email: totally fake email"
-bash: your-url-here: No such file or directory
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/2-HelloUser (qwiklabs-gcp-04-c077032b024e)$ curl -X GET https://qwiklabs-gcp-04-c077032b024e.uw.r.appspot.com/ -H "X-Goog-Authenticated-User-Email: totally fake email"
<!doctype html>
<html>
<head>
<title>IAP Hello User</title>
</head>
<body>
<h1>Hello User</h1>
<p>
Hello, totally fake email! Your persistent ID is None.
</p>
<p>
This is step 2 of the <em>User Authentication with IAP</em>
codelab.
</p>
</body>
</html>
qwiklabs-gcstudent_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/3-HelloVerifiedUser (qwiklabs-gcp-04-c077032b024e)$ gcloud app deploy
Services to deploy:
descriptor: [/home/student_04_e3f1df1e0d22/user-authentication-with-iap/3-HelloVerifiedUser/app.yaml]
source: [/home/student_04_e3f1df1e0d22/user-authentication-with-iap/3-HelloVerifiedUser]
target project: [qwiklabs-gcp-04-c077032b024e]
target service: [default]
target version: [20230805t190846]
target url: [https://qwiklabs-gcp-04-c077032b024e.uw.r.appspot.com]
target service account: [qwiklabs-gcp-04-c077032b024e@appspot.gserviceaccount.com]
Do you want to continue (Y/n)? Y
Beginning deployment of service [default]...
Created .gcloudignore file. See `gcloud topic gcloudignore` for details.
Uploading 4 files to Google Cloud Storage
25%
50%
75%
100%
100%
File upload done.
Updating service [default]...working.
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://qwiklabs-gcp-04-c077032b024e.uw.r.appspot.com]
You can stream logs from the command line by running:
$ gcloud app logs tail -s default
To view your application in the web browser run:
$ gcloud app browse
student_04_e3f1df1e0d
Examine the application files
This folder contains the same set of files as seen in 2-HelloUser, with two files altered and one new file. The new file is auth.py, which provides a user() method to retrieve and verify the cryptographically signed identity information. The changed files are main.py and templates/index.html, which now use the results of that method. The unverified headers as found in the last deployment are also shown for comparison.
The new functionality is primarily in the user() function:
def user():
assertion = request.headers.get('X-Goog-IAP-JWT-Assertion')
if assertion is None:
return None, None
info = jwt.decode(
assertion,
keys(),
algorithms=['ES256'],
audience=audience()
)
return info['email'], info['sub']
The assertion is the cryptographically signed data provided in the specified request header. The code uses a library to validate and decode that data. Validation uses the public keys that Google provides for checking data it signs, and knowing the audience that the data was prepared for (essentially, the Google Cloud project that is being protected). Helper functions keys() and audience() gather and return those values.
The signed object has two pieces of data we need: the verified email address, and the unique ID value (provided in the sub, for subscriber, standard field).
This completes Step 3.
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/3-HelloVerifiedUser (qwiklabs-gcp-04-c077032b024e)$ gcloud app browse
Did not detect your browser. Go to this link to view your app:
student_04_e3f1df1e0d22@cloudshell:~/user-authentication-with-iap/3-HelloVerifiedUser (qwiklabs-gcp-04-c077032b024e)$
No comments:
Post a Comment