Authenticating users with Cloud Identity-Aware Proxy for Python

Apps running on Google Cloud managed platforms such asApp Engine can avoid managinguser authentication and session management byusingIdentity-Aware Proxy (IAP) to control access to them. IAP can not only control access to theapp, but it also providesinformation about the authenticated users, including the email address and apersistent identifier to the app in the form ofnew HTTP headers.

Objectives

  • Require users of your App Engine app to authenticate themselves byusing IAP.

  • Access users' identities in the app to display the current user'sauthenticated email address.

Costs

In this document, you use the following billable components of Google Cloud:

To generate a cost estimate based on your projected usage, use thepricing calculator.

New Google Cloud users might be eligible for afree trial.

When you finish the tasks that are described in this document, you can avoid continued billing by deleting the resources that you created. For more information, seeClean up.

Before you begin

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.create permission.Learn how to grant roles.
    Note: If you don't plan to keep the resources that you create in this procedure, create a project instead of selecting an existing project. After you finish these steps, you can delete the project, removing all resources associated with the project.

    Go to project selector

  3. Install the Google Cloud CLI.

  4. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  5. Toinitialize the gcloud CLI, run the following command:

    gcloudinit
  6. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.create permission.Learn how to grant roles.
    Note: If you don't plan to keep the resources that you create in this procedure, create a project instead of selecting an existing project. After you finish these steps, you can delete the project, removing all resources associated with the project.

    Go to project selector

  7. Install the Google Cloud CLI.

  8. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  9. Toinitialize the gcloud CLI, run the following command:

    gcloudinit

Background

This tutorial uses IAP to authenticate users.This is only one of several possible approaches.To learn moreabout the various methods to authenticate users, see theAuthentication concepts section.

The Hellouser-email-address app

The app for this tutorial is a minimal Hello world App Engine app,with one non-typical feature: instead of "Hello world" it displays"Hellouser-email-address", whereuser-email-address is the authenticateduser's email address.

This functionality is possible by examining the authenticated informationthat IAP adds to each web request it passes through to your app.There arethree new request headers added to each web request that reaches your app.The first two headers are plain text strings that you can use toidentify the user. The third header is a cryptographically signed objectwith that same information.

  • X-Goog-Authenticated-User-Email: A user's email address identifies them.Don't store personal information if your app can avoid it. This app doesn'tstore any data; it just echoes it back to the user.

  • X-Goog-Authenticated-User-Id: This user ID assigned by Google doesn'tshow information about the user, but it does allow an app to knowthat a logged-in user is the same one that was previously seen before.

  • X-Goog-Iap-Jwt-Assertion: You can configure Google Cloud appsto accept web requests from other cloud apps, bypassingIAP, in addition to internet web requests. If an app is soconfigured, it's possible for such requests to have forged headers.Instead of using either of the plain text headers previously mentioned, youcan use and verify this cryptographically signed header to check that the informationwas provided by Google. Both the user's email address and a persistentuser ID are available as part of this signed header.

If you are certain that the app is configured so that only internetweb requests can reach it, and that no one can disable the IAPservice for the app, then retrieving a unique user ID takes only a single lineof code:

user_id=request.headers.get('X-Goog-Authenticated-User-ID')

However, a resilient app should expect things to go wrong, includingunexpected configuration or environmental issues, so we instead recommendcreating a function that uses and verifies the cryptographically signed header.That header's signature cannot be forged, and when verified, can be used toreturn the identification.

Create the source code

  1. Use a text editor to create a file namedmain.py, and paste thefollowing code in it:

    importsysfromflaskimportFlaskapp=Flask(__name__)CERTS=NoneAUDIENCE=Nonedefcerts():"""Returns a dictionary of current Google public key certificates for    validating Google-signed JWTs. Since these change rarely, the result    is cached on first request for faster subsequent responses.    """importrequestsglobalCERTSifCERTSisNone:response=requests.get('https://www.gstatic.com/iap/verify/public_key')CERTS=response.json()returnCERTSdefget_metadata(item_name):"""Returns a string with the project metadata value for the item_name.    See https://cloud.google.com/compute/docs/storing-retrieving-metadata for    possible item_name values.    """importrequestsendpoint='http://metadata.google.internal'path='/computeMetadata/v1/project/'path+=item_nameresponse=requests.get('{}{}'.format(endpoint,path),headers={'Metadata-Flavor':'Google'})metadata=response.textreturnmetadatadefaudience():"""Returns the audience value (the JWT 'aud' property) for the current    running instance. Since this involves a metadata lookup, the result is    cached when first requested for faster future responses.    """globalAUDIENCEifAUDIENCEisNone:project_number=get_metadata('numeric-project-id')project_id=get_metadata('project-id')AUDIENCE='/projects/{}/apps/{}'.format(project_number,project_id)returnAUDIENCEdefvalidate_assertion(assertion):"""Checks that the JWT assertion is valid (properly signed, for the    correct audience) and if so, returns strings for the requesting user's    email and a persistent user ID. If not valid, returns None for each field.    """fromjoseimportjwttry:info=jwt.decode(assertion,certs(),algorithms=['ES256'],audience=audience())returninfo['email'],info['sub']exceptExceptionase:print('Failed to validate assertion:{}'.format(e),file=sys.stderr)returnNone,None@app.route('/',methods=['GET'])defsay_hello():fromflaskimportrequestassertion=request.headers.get('X-Goog-IAP-JWT-Assertion')email,id=validate_assertion(assertion)page="<h1>Hello{}</h1>".format(email)returnpage

    Thismain.py file is explained in detail in theUnderstanding the codesection later in this tutorial.

  2. Create another file calledrequirements.txt, and paste the followinginto it:

    Flask==2.2.5cryptography==41.0.2python-jose[cryptography]==3.3.0requests==2.31.0

    Therequirements.txt file lists any non-standard Python libraries your app needsApp Engine to load for it:

    • Flask is the Python web framework used for the app.

    • cryptography is a module that provides strong cryptographic functions.

    • python-jose[cryptography] provides the JWT checking and decoding function.

    • requests retrieves data from web sites.

  3. Create a file namedapp.yaml and put the following text in it:

    runtime:python37

    Theapp.yaml file tells App Engine which language environment your coderequires.

Understanding the code

This section explains how the code inmain.py works. If you just want to runthe app, you can skip ahead to theDeploy the appsection.

The following code is in themain.py file. When a anHTTP GET request tothe home page isreceived by the app, the Flask framework invokes thesay_hello function:

@app.route('/',methods=['GET'])defsay_hello():fromflaskimportrequestassertion=request.headers.get('X-Goog-IAP-JWT-Assertion')email,id=validate_assertion(assertion)page="<h1>Hello{}</h1>".format(email)returnpage

Thesay_hello function gets the JWT assertion header value thatIAP added fromthe incoming request and calls a function to validate that cryptographicallysigned value. The first value returned (email) is then used in a minimal webpage that it creates and returns.

defvalidate_assertion(assertion):"""Checks that the JWT assertion is valid (properly signed, for the    correct audience) and if so, returns strings for the requesting user's    email and a persistent user ID. If not valid, returns None for each field.    """fromjoseimportjwttry:info=jwt.decode(assertion,certs(),algorithms=['ES256'],audience=audience())returninfo['email'],info['sub']exceptExceptionase:print('Failed to validate assertion:{}'.format(e),file=sys.stderr)returnNone,None

Thevalidate_assertion function uses thejwt.decode function from thethird-partyjose library to verify that the assertion is properly signed,and to extract the payload information from the assertion. That information isthe authenticated user's email address and a persistent unique ID for the user.If the assertion cannot be decoded, this function returnsNone for each ofthose values and prints a message to log the error.

Validating a JWT assertion requires knowing the public key certificates ofthe entity that signed the assertion (Google in this case), and the audiencethe assertion is intended for. For an App Engine app, the audience isa string with Google Cloud project identification information in it.This function getsthose certificates and the audience string from the functions preceding it.

defaudience():"""Returns the audience value (the JWT 'aud' property) for the current    running instance. Since this involves a metadata lookup, the result is    cached when first requested for faster future responses.    """globalAUDIENCEifAUDIENCEisNone:project_number=get_metadata('numeric-project-id')project_id=get_metadata('project-id')AUDIENCE='/projects/{}/apps/{}'.format(project_number,project_id)returnAUDIENCE

You can look up the Google Cloud project's numeric ID and name and put those in thesource code yourself, but theaudience function does that for you by queryingthe standard metadata service made available to every App Engine app.Because the metadata service is external to the app code, that result is savedin a global variable that is returned without having to look metadata upin subsequent calls.

defget_metadata(item_name):"""Returns a string with the project metadata value for the item_name.    See https://cloud.google.com/compute/docs/storing-retrieving-metadata for    possible item_name values.    """importrequestsendpoint='http://metadata.google.internal'path='/computeMetadata/v1/project/'path+=item_nameresponse=requests.get('{}{}'.format(endpoint,path),headers={'Metadata-Flavor':'Google'})metadata=response.textreturnmetadata

The App Engine metadata service (and similar metadata services for otherGoogle Cloud computing services) looks like a web site and is queried bystandard web queries. However, it isn't actually an external site, but aninternal feature that returns requested information about the runningapp, so it is safe to usehttp instead ofhttps requests.It's used to get the current Google Cloud identifiers needed to define theJWT assertion's intended audience.

defcerts():"""Returns a dictionary of current Google public key certificates for    validating Google-signed JWTs. Since these change rarely, the result    is cached on first request for faster subsequent responses.    """importrequestsglobalCERTSifCERTSisNone:response=requests.get('https://www.gstatic.com/iap/verify/public_key')CERTS=response.json()returnCERTS

Verification of a digital signature requires the public key certificate ofthe signer. Google provides a web site that returns all of the currently used publickey certificates. These results are cachedin case they're needed again in the same app instance.

Deploying the app

Now you can deploy the app and then enable IAP to require usersto authenticate before they can access the app.

  1. In your terminal window, go to the directory containing theapp.yaml file,and deploy the app to App Engine:

    gcloudappdeploy
  2. When prompted, select a nearby region.

  3. When asked if you want to continue with the deployment operation, enterY.

    Within a few minutes, your app is live on the internet.

  4. View the app:

    gcloudappbrowse

    In the output, copyweb-site-url, the web addressfor the app.

  5. In a browser window, pasteweb-site-url to open theapp.

    No email is displayed because you're not yet using IAP so nouser information is sent to the app.

Enable IAP

Now that an App Engine instance exists, you can protect it withIAP:

  1. In the Google Cloud console, go to theIdentity-Aware Proxy page.

    Go to Identity-Aware Proxy page

  2. Because this is the first time you've enabled an authentication option forthis project, you see a message that you must configure your OAuth consentscreen before you can use IAP.

    ClickConfigure Consent Screen.

  3. On theOAuth Consent Screen tab of theCredentials page, completethe following fields:

    • If your account is in a Google Workspace organization, selectExternaland clickCreate. To start, the app will only be available to users youexplicitly allow.

    • In theApplication name field, enterIAP Example.

    • In theSupport email field, enter your email address.

    • In theAuthorized domain field, enter the hostname portion of the app'sURL, for example,iap-example-999999.uc.r.appspot.com. Press theEnter keyafter entering the hostname in the field.

    • In theApplication homepage link field, enter the URL for your app, forexample,https://iap-example-999999.uc.r.appspot.com/.

    • In theApplication privacy policy line field, use the same URL as thehomepage link for testing purposes.

  4. ClickSave. When prompted to create credentials, you can close the window.

  5. In the Google Cloud console, go to theIdentity-Aware Proxy page.

    Go to Identity-Aware Proxy page

  6. To refresh the page, clickRefresh. Thepage displays a list of resources you can protect.

  7. In theIAP column, click to turn on IAP for the app.

  8. In your browser, go toweb-site-url again.

  9. Instead of the web page, there is a login screen to authenticate yourself.When you log in, you're denied access because IAP doesn'thave a list of users to allow through to the app.

Add authorized users to the app

  1. In the Google Cloud console, go to the Identity-Aware Proxy page.

    Go to Identity-Aware Proxy page

  2. Select the checkbox for the App Engine app, and then clickAdd Principal.

  3. EnterallAuthenticatedUsers, and then select theCloud IAP/IAP-Secured Web App User role.

  4. ClickSave.

Now any user that Google can authenticate can access the app. If you want,you can restrict access further by only adding one or more people orgroups as principals:

  • Any Gmail or Google Workspace email address

  • A Google Group email address

  • A Google Workspace domain name

Access the app

  1. In your browser, go toweb-site-url.

  2. To refresh the page, clickRefresh.

  3. On the login screen, log in with your Google credentials.

    The page displays a "Hellouser-email-address" page with youremail address.

    If you still see the same page as before, there might be an issue with thebrowser not fully updating new requests now that you enabledIAP. Close all browser windows, reopen them, and try again.

Authentication concepts

There are several ways an app can authenticate its users and restrict accessto only authorized users. Common authentication methods, in decreasing level of effortfor the app, are listed in the following sections.

OptionAdvantagesDisadvantages
App authentication
  • App can run on any platform, with or without an internet connection
  • Users don't need to use any other service to manage authentication
  • App must manage user credentials securely, guard against disclosure
  • App must maintain session data for logged-in users
  • App must provide user registration, password changes, password recovery
OAuth2
  • App can run on any internet-connected platform, including a developer workstation
  • App doesn't need user registration, password changes, or password recovery functions.
  • Risk of user information disclosure is delegated to other service
  • New login security measures handled outside the app
  • Users must register with the identity service
  • App must maintain session data for logged-in users
IAP
  • App doesn't need to have any code to manage users, authentication, or session state
  • App has no user credentials that might be breached
  • App can only run on platforms supported by the service. Specifically, certain Google Cloud services that support IAP, such as App Engine.

App-managed authentication

With this method, the app manages every aspect of user authenticationon its own. The app must maintain its own database of user credentials and manageuser sessions, and it needs to provide functions to manage user accounts andpasswords, check user credentials, as well as issue, check, and update user sessionswith each authenticated login. The following diagram illustrates the app-managedauthentication method.

Application managed flow

As shown in the diagram, after the user logs in, the app creates and maintainsinformation about the user's session. When the user makes a request to the app,the request must include session information that the app is responsible forverifying.

The main advantage of this approach is that it is self-contained and underthe control of the app. The app doesn't even need to beavailable on the internet. The main disadvantage is thatthe app is now responsible for providing all account managementfunctionality and protecting all sensitive credential data.

External authentication with OAuth2

A good alternative to handling everything within the app is to usean external identity service, such as Google, that handles all useraccount information and functionality and is responsible for safeguardingsensitive credentials. When a user tries to log in to the app therequest is redirected to the identity service, which authenticates theuser and then redirect the request back to the app with necessaryauthentication information provided. For more information, seeUsing OAuth 2.0 for Web Server Applications.

The following diagram illustrates the external authentication with the OAuth2method.

OAuth2 flow

The flow in the diagram begins when the user sends a request to access theapp. Instead of responding directly, the app redirects the user's browserto Google's identity platform, which displays a page to log in to Google. Aftersuccessfully logging in, the user's browser is directed back to the app.This request includes information that the app can use to look up informationabout the now authenticated user, and the app now responds to the user.

This method has many advantages for the app. It delegates allaccount management functionality and risks to the external service, whichcan improve login and account security without the app having tochange. However, as is shown in the preceding diagram, the app musthave access to the internet to use this method. The app is alsoresponsible for managing sessions after the user is authenticated.

Identity-Aware Proxy

The third approach, which this tutorial covers, is to use IAP tohandle all authentication and session management with any changes tothe app. IAP intercepts all web requests to your app, blocksany that haven't been authenticated, and passes others through with useridentity data added to each request.

The request handling is shown in the following diagram.

IAP flow

Requests from users are intercepted by IAP, which blocksunauthenticated requests. Authenticated requests are passed on to theapp, provided that the authenticated user is in the list of allowedusers. Requests passed through IAP have headers added to themidentifying the user who made the request.

The app no longer needs to handle any user account or sessioninformation. Any operation that needs to know a unique identifier for theuser can get that directly from each incoming web request. However, this canonly be used for computing services that support IAP, suchas App Engine andload balancers. You cannot use IAP on a local development machine.

Clean up

To avoid incurring charges to your Google Cloud account for the resources used in this tutorial, either delete the project that contains the resources, or keep the project and delete the individual resources.

    Caution: Deleting a project has the following effects:
    • Everything in the project is deleted. If you used an existing project for the tasks in this document, when you delete it, you also delete any other work you've done in the project.
    • Custom project IDs are lost. When you created this project, you might have created a custom project ID that you want to use in the future. To preserve the URLs that use the project ID, such as anappspot.com URL, delete selected resources inside the project instead of deleting the whole project.

    If you plan to explore multiple architectures, tutorials, or quickstarts, reusing projects can help you avoid exceeding project quota limits.

  1. In the Google Cloud console, go to theManage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then clickDelete.
  3. In the dialog, type the project ID, and then clickShut down to delete the project.

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2025-12-17 UTC.