User authentication with Identity-Aware Proxy
1. Introduction
Authenticating users of your web app is often necessary, and usually requires special programming in your app. For Google Cloud Platform 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 Platform 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.
This codelab will walk you through creating your own application, restricting access to it, and getting user identity from IAP.
What you will build
What you'll learn
How to write and deploy a simple App Engine app using Python 3.7
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
What you'll need
A modern web browser such as Chrome.
Basic knowledge of the Python programming language
This codelab is focused on Google App Engine and IAP. Non-relevant concepts and code blocks are glossed over and are provided for you to simply copy and paste.
2. Getting set up
You will work in the Cloud Shell command line environment. Start by opening that environment and fetching the sample code to it.
Launch the Console and Cloud Shell
Download the code
Click the command line area in the Cloud Shell so you can type commands. Fetch the code from Github and then change to the code folder:
git clone https://github.com/googlecodelabs/user-authentication-with-iap.git
Your Cloud Platform project in this session is set to uconn-engr.
Use “gcloud config set project [PROJECT_ID]” to change to a different project.
john_iacovacci1@cloudshell:~ (uconn-engr)$ git clone https://github.com/googlecodelabs/user-authenticatio
n-with-iap.git
Cloning into 'user-authentication-with-iap'...
remote: Enumerating objects: 22, done.
remote: Total 22 (delta 0), reused 0 (delta 0), pack-reused 22
Unpacking objects: 100% (22/22), done.
john_iacovacci1@cloudshell:~ (uconn-engr)$
john_iacovacci1@cloudshell:~ (uconn-engr)$ cd user-authentication-with-iap/
john_iacovacci1@cloudshell:~/user-authentication-with-iap (uconn-engr)$
This folder contains one subfolder for each step of this codelab. You will change to the correct folder to perform each step.
3. Step 1 - Deploy application and protect it with IAP
This is an App Engine Standard application written in Python 3.7 that simply displays a "Hello, World" welcome page. We will deploy and test it, then restrict access to it using IAP.
Review the application code
Change from the main project folder to the 1-HelloWorld subfolder that contains code for this step.
cd 1-HelloWorld
john_iacovacci1@cloudshell:~/user-authentication-with-iap (uconn-engr)$ cd 1-HelloWorld/
john_iacovacci1@cloudshell:~/user-authentication-with-iap/1-HelloWorld (uconn-engr)$
The application code is in the main.py file. It uses the Flask web framework to respond to web requests with the contents of a template. That template file is in templates/index.html, and for this step contains only plain HTML. A second template file contains a skeletal example privacy policy in templates/privacy.html.
There are two other files: requirements.txt lists all the non-default Python libraries the application uses, and app.yaml tells Google Cloud Platform that this is a Python 3.7 App Engine application.
You can list each file in the shell using the cat command, as in:
cat main.py
Or you can open the Cloud Shell code editor by clicking the Pencil icon at the top right-hand side of the Cloud Shell window, and examine the code that way.
You do not need to change any files for this step.
Deploy to App Engine
Now deploy the app to the App Engine Standard environment for Python 3.7
gcloud app deploy
You may be asked to choose a region to deploy to. Select any one near to you that says it "supports standard". When you are asked if you want to continue, enter Y for yes.
In a few minutes the deploy should complete and you will see a message that you can view your application with gcloud app browse. Enter that command. If a new tab does not open in your browser, click the displayed link to open it in a new tab, or copy it to a manually opened new tab if necessary. Since this is the first time this app is run, it will take a few seconds to appear while a cloud instance is started, and you should see the following window.
john_iacovacci1@cloudshell:~/user-authentication-with-iap/1-HelloWorld (uconn-engr)$ gcloud app deploy
Services to deploy:
descriptor: [/home/john_iacovacci1/user-authentication-with-iap/1-HelloWorld/app.yaml]
source: [/home/john_iacovacci1/user-authentication-with-iap/1-HelloWorld]
target project: [uconn-engr]
target service: [default]
target version: [20210321t020740]
target url: [https://uconn-engr.uc.r.appspot.com]
Do you want to continue (Y/n)?
Beginning deployment of service [default]...
Created .gcloudignore file. See `gcloud topic gcloudignore` for details.
╔═════════════════════════════════════════════╗
╠═ Uploading 6 files to Google Cloud Storage ═╣
╚═════════════════════════════════════════════╝
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://uconn-engr.uc.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
john_iacovacci1@cloudshell:~/user-authentication-with-iap/1-HelloWorld (uconn-engr)$
You can open that same URL from any computer connected to the Internet to see that web page. Access is not yet restricted.
Restrict access with IAP
Fill in the required blanks with appropriate values:
Click Save. You will be prompted to create credentials. You do not need to create credentials for this codelab, so you can simply close this browser tab.
You have successfully protected your app with IAP, but you have not yet told IAP which accounts to allow through.
Click Save. The message "Policy Updated" will appear at the bottom of the window.
Navigate back to your app and reload the page. You should now see your web app, since you already logged in with a user you authorized. However, you may still see the "You don't have access" page since IAP may not recheck your authorization. In that case, do the following steps:
Open your web browser to the home page address with /_gcp_iap/clear_login_cookie added to the end of the URL, as in https://iap-example-999999.appspot.com/_gcp_iap/clear_login_cookie.
You will see a new Sign in with Google screen, with your account already showing. Do not click the account. Instead, click Use another account, and re-enter your credentials.
These steps cause IAP to recheck your access and you should now see your application's home screen.
https://uconn-engr.uc.r.appspot.com/_gcp_iap/clear_login_cookie
If you have access to another browser or can use Incognito Mode in your browser, and have another valid GMail or GSuite account, you can use that browser to navigate to your app page and log in with the other account. Since that account has not been authorized, it will see the "You Don't Have Access" screen inste
4. Step 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.
Change to the folder for this step:
john_iacovacci1@cloudshell:~ (uconn-engr)$ cd user-authentication-with-iap/
john_iacovacci1@cloudshell:~/user-authentication-with-iap (uconn-engr)$ ls
1-HelloWorld 2-HelloUser 3-HelloVerifiedUser CONTRIBUTING.md LICENSE README.md
john_iacovacci1@cloudshell:~/user-authentication-with-iap (uconn-engr)$ cd 2-HelloUser/
john_iacovacci1@cloudshell:~/user-authentication-with-iap/2-HelloUser (uconn-engr)$
Deploy to App Engine
Since deployment takes a few minutes, start by deploying the app to the App Engine Standard environment for Python 3.7:
gcloud app deploy
When you are asked if you want to continue, enter Y for yes. In a few minutes the deploy should complete. While you are waiting you can examine the application files as described below.
When the deployment is ready you will see a message that you can view your application with gcloud app browse. Enter that command. If a new tab does not open on your browser, copy the displayed link and open it in a new tab normally. You should see a page similar to the following:
You may need to wait a few minutes for the new version of your application to replace the prior version. Refresh the page if needed to see a page similar to the above.
Examine the application files
This folder contains the same set of files as seen in Step 1, 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 doubled 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.
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 the menu icon at the top left of the page, click on Security and then on Identity-Aware Proxy. Click the IAP toggle switch next to App Engine app to turn IAP off.
5. Step 3 - Use cryptographic verification
If there is a risk of IAP being turned off or bypassed, your app can check to make sure the identity information it receives is valid. This uses a third web request header added by IAP, called X-Goog-IAP-JWT-Assertion. The value of the header is a cryptographically signed object that also contains the user identity data. Your application can verify the digital signature and use the data provided in this object to be certain that it was provided by IAP without alteration.
Digital signature verification requires several extra steps, such as retrieving the latest set of Google public keys. You can decide whether your application needs these extra steps based on the risk that someone might be able to turn off or bypass IAP, and the sensitivity of the application.
john_iacovacci1@cloudshell:~ (uconn-engr)$ cd user-authentication-with-iap/
john_iacovacci1@cloudshell:~/user-authentication-with-iap (uconn-engr)$ cd 3-HelloVerifiedUser/
john_iacovacci1@cloudshell:~/user-authentication-with-iap/3-HelloVerifiedUser (uconn-engr)$
Deploy to App Engine
Deploy the app to the App Engine Standard environment for Python 3.7:
gcloud app deploy
When you are asked if you want to continue, enter Y for yes. In a few minutes the deploy should complete. While you are waiting you can examine the application files as described below.
When the deployment is ready you will see a message that you can view your application with gcloud app browse. Enter that command. If a new tab does not open on your browser, copy the displayed link and open it in a new tab normally.
Recall that you disabled IAP in Step 2, so no IAP data is being provided to the application. You should see a page similar to the following:
john_iacovacci1@cloudshell:~/user-authentication-with-iap/3-HelloVerifiedUser (uconn-engr)$ gcloud app de
ploy
Services to deploy:
descriptor: [/home/john_iacovacci1/user-authentication-with-iap/3-HelloVerifiedUser/app.yaml]
source: [/home/john_iacovacci1/user-authentication-with-iap/3-HelloVerifiedUser]
target project: [uconn-engr]
target service: [default]
target version: [20210321t210645]
target url: [https://uconn-engr.uc.r.appspot.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 ═╣
╚═════════════════════════════════════════════╝
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://uconn-engr.uc.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
john_iacovacci1@cloudshell:~/user-authentication-with-iap/3-HelloVerifiedUser (uconn-engr)$
As before, you may need to wait a few minutes for the newest version to be live to see the new version of the page.
Since IAP is disabled, no user information is available. Now turn IAP back on.
In the cloud console window, click the menu icon at the top left of the page, click on Security and then on Identity-Aware Proxy. Click the IAP toggle switch next to App Engine app to turn IAP on again.
Notice that the email address provided by the verified method does not have the accounts.google.com: prefix.
If IAP is turned off or bypassed, the verified data would either be missing, or invalid, since it cannot have a valid signature unless it was created by the holder of Google's private keys.
Examine the application files
This folder contains the same set of files as seen in Step 2, 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 step 2 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.
6. Summary
You deployed an App Engine web application. In Step 1, you restricted access to the application to only users you chose. In Step 2, you retrieved and displayed the identity of users that IAP allowed access to your application, and saw how that information might be spoofed if IAP were disabled or bypassed. In Step 3, you verified cryptographically signed assertions of the user's identity, which cannot be spoofed.
No comments:
Post a Comment