Hosting a sign-in page with Cloud Run

To use external identities with Identity-Aware Proxy (IAP), your app needs a sign-in page. IAP will redirect users to this page to authenticate before they can access secure resources.

This document shows you how to deploy and customize a prebuilt sign-in page using Cloud Run. This is the fastest way to get started with external identities, and doesn't require you to write any code.

You can also build a sign-in page yourself. Building your own page is more difficult, but increases your control over the authentication flow and user experience. See Creating a sign-in page with FirebaseUI and Creating a custom sign-in page to learn more.

Sign-in page limitations

You cannot use the prebuilt sign-in page if your project has email enumeration protection enabled.

If your project has email enumeration protection enabled, disable email-enumeration-protection before continuing with the procedures in this document.

Before you begin

  • Enable the Compute Engine API.

    Enable Compute Engine API

  • Enable external identities, and select the Create a sign-in page for me option during setup. This lets Cloud Run and FirebaseUI create a sign-in page for you.

  • Ensure that the service account used by Cloud Run, PROJECT_NUMBER-compute@developer.gserviceaccount.com, has the following predefined roles:

    • roles/identitytoolkit.viewer
    • roles/iap.settingsAdmin
    • roles/compute.networkViewer

Setting the hosted page's redirect URL

Hosted login pages use their own domain as the Firebase auth domain to ensure that sign-in via redirect works correctly in all environments. You must add the hosted page's URL as an authorized redirect URL in your provider configuration.

To add the hosted login page's URL as an authorized redirect URL, do the following:

  1. Copy the Login URL after selecting your application.

  2. In the Google Cloud console, go to the Credentials page.

    Go to Credentials

  3. Add LOGIN_URL/__/auth/handler as one of the authorized redirect URIs for your app's OAuth 2.0 client. Select the same OAuth client ID and secret key you used when configuring your provider.

  4. If your app uses other SAML and OIDC providers, add LOGIN_URL/__/auth/handler as the authorized redirect URI or ACS URL.

Alternatively, you can use PROJECT_ID.firebaseapp.com instead of LOGIN_URL/__/auth/handler as your auth domain by customizing your sign-in page with the pop-up sign-in flow.

To implement the pop-up sign-in flow for your page, do the following:

  1. Click Customize Page.

  2. In the JSON-formatted configuration file, set authDomain to PROJECT_ID.firebaseapp.com and signInFlow to popup.

  3. To save the configuration, click Save.

Replace the following:

  • LOGIN_URL: the domain of your hosted login page
  • PROJECT_ID: the firebase project ID

The following is an example of the pop-up sign-in flow configuration:

{
  "AIzaSyC5DtmRUR...": {
    "authDomain": "example.firebaseapp.com",
    "displayMode": "optionFirst",
    ...
    ...
    "tenants": {
      "tenant-a-id": {
        "fullLabel": "Company A Portal",
        "displayName": "Company A",
        ...
        ...
        "signInFlow": "popup",
        ...
        ...
      }
    }
  }
}

For more information on redirect best practices and storage partitioning, see Best practices for using signInWithRedirect on browsers that block third-party storage access.

Testing the sign-in page

The initial sign-in page created by IAP is fully functional. To test it:

  1. Navigate to a resource protected by IAP. You should be automatically redirected to the sign-in page.

  2. Select a tenant and provider to sign-in with. If you don't see any tenants or providers listed, make sure you've configured one using Identity Platform.

  3. Sign in with your credentials.

You should be redirected to the protected resource.

Customizing the sign-in page

You can customize the sign-in page using a JSON configuration file. Some options include:

  • Adding a header and logo to the sign-in page.
  • Specifying the available tenants and providers.
  • Customizing the icons and styling of each tenant and provider button.
  • Adding links to your app's privacy policy and terms of service.

The following sections explain how to access and update the JSON configuration file.

Obtaining an access token

In order to administer the sign-in page, you need a Google access token. The easiest way to obtain one is by enabling Google as a provider for Identity Platform. If your app already uses Google as an identity provider, you can skip this section.

  1. Go to the Identity Platform Providers page in the Google Cloud console.

    Go to the Identity Platform Providers page

  2. Click Add a Provider.

  3. Select Google from the list of providers.

  4. Configure the Web Client ID and Web Client Secret:

    1. In a separate tab, open the IAP page.

      Go to the IAP page

    2. Expand the View more menu for your resource, then click Edit OAuth Client.

    3. Copy the Client ID and Client secret fields into the Google provider configuration for Identity Platform.

    4. Add the Identity Platform redirect URL to the list of Authorized Redirect URIs for the OAuth client. The URL is formatted like https://PROJECT_ID.firebaseapp.com/__/auth/handler.

  5. Click Save on both pages.

Signing in to the admin panel

The JSON configuration for the sign-in page hosted by Cloud Run. The following steps show how to access the panel. Note that you'll need the Storage Admin (roles/storage.admin) role.

  1. Go to the IAP page in the Google Cloud console.

    Go to the IAP page

  2. Select your resource from the list.

  3. Launch the URL listed under Customize page in the info panel. It should look something like https://servicename-xyz-uc.a.run.app/admin.

  4. Sign in with the same Google account you used to configure IAP. A text editor containing the JSON configuration file appears.

Modifying the configuration

The configuration schema for the sign-in page is based on FirebaseUI, and inherits many of its properties. The following code shows an example configuration with three tenants:

{
  "AIzaSyC5DtmRUR...": {
    "authDomain": "awesomeco.firebaseapp.com",
    "displayMode": "optionFirst",
    "selectTenantUiTitle": "Awesome Company Portal",
    "selectTenantUiLogo": "https://awesome.com/abcd/logo.png",
    "styleUrl": "https://awesome.com/abcd/overrides/stylesheet.css",
    "tosUrl": "https://awesome.com/abcd/tos.html",
    "privacyPolicyUrl": "https://awesome.com/abcd/privacypolicy.html",
    "tenants": {
      "tenant-a-id": {
        "fullLabel": "Company A Portal",
        "displayName": "Company A",
        "iconUrl": "https://companya.com/img/icon.png",
        "logoUrl": "https://companya.com/img/logo.png",
        "buttonColor": "#007bff",
        "signInOptions": [
          {
            "provider": "password",
            "requireDisplayName": false,
            "disableSignUp": {
              "status": true,
              "adminEmail": "admin@example.com",
              "helpLink": "https://www.example.com/trouble_signing_in"
            }
          },
          "facebook.com",
          "google.com",
          "microsoft.com",
          {
            "provider": "saml.okta-cicp-app",
            "providerName": "Corp Account",
            "fullLabel": "Employee Corporate Login",
            "buttonColor": "#ff0000",
            "iconUrl": "https://companya.com/abcd/icon-1.png"
          },
          {
            "provider": "oidc.okta-oidc",
            "providerName": "Contractor Account",
            "fullLabel": "Contractor Account Portal",
            "buttonColor": "#00ff00",
            "iconUrl": "https://companya.com/abcd/icon-2.png"
          }
        ],
        "tosUrl": "https://companya.com/abcd/tos.html",
        "privacyPolicyUrl": "https://companya.com/abcd/privacypolicy.html"
      },
      "tenant-b-id": {
        "fullLabel": "Company B Portal",
        "displayName": "Company B",
        "iconUrl": "https://companyb.com/img/icon.png",
        "logoUrl": "https://companyb.com/img/logo.png",
        "buttonColor": "#007bff",
        "immediateFederatedRedirect": true,
        "signInOptions": [
          {
            "provider": "saml.okta-bla-app",
            "providerName": "Corp Account",
            "buttonColor": "#0000ff",
            "iconUrl": "https://companyb.com/abcd/icon.png"
          }
        ],
        "tosUrl": "https://companyb.com/abcd/tos.html",
        "privacyPolicyUrl": "https://companyb.com/abcd/privacypolicy.html"
      },
      "tenant-c-id": {
        "fullLabel": "Company C Portal",
        "displayName": "Company C",
        "iconUrl": "https://companyc.com/img/icon.png",
        "logoUrl": "https://companyc.com/img/logo.png",
        "buttonColor": "#007bff",
        "immediateFederatedRedirect": true,
        "signInOptions": [
          {
            "provider": "password",
            "requireDisplayName": false
          },
          {
            "provider": "google.com",
            "scopes": ["scope1", "scope2", "https://example.com/scope3"],
            "loginHintKey": "login_hint",
            "customParameters": {
              "prompt": "consent",
            },
          }
        ],
        "tosUrl": "https://companyc.com/abcd/tos.html",
        "privacyPolicyUrl": "https://companyc.com/abcd/privacypolicy.html",
        "adminRestrictedOperation": {
          "status": true,
          "adminEmail": "admin@example.com",
          "helpLink": "https://www.example.com/trouble_signing_in"
        }
      },
    }
  }
}

For a complete list of available properties, see the reference documentation.

Overriding CSS

You can use the styleUrl property to specify a custom CSS file. Styles in this file will override the default CSS. The file must be publicly accessible using HTTPS (for example, hosted in a Cloud Storage bucket).

The following example demonstrates overriding the default CSS:

/** Change header title style. */
.heading-center {
  color: #7181a5;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 20px;
  font-weight: bold;
}

/** Use round edged borders for container. */
.main-container {
  border-radius: 5px;
}

/** Change page background color. */
body {
  background-color: #f8f9fa;
}

Redeploying the Cloud Run instance

In some cases, you might want to redeploy the Cloud Run instance that hosts the sign-in page. Example scenarios include:

  • Adding, modifying, or removing identity providers
  • Modifying tenant configurations
  • Setting environment variables
  • Updating the container image to the latest version

Regularly updating and redeploying the container image ensures you have the latest bug fixes and security patches. You can view the list of changes between versions on GitHub.

You can get the current version of the deployed container using the /versionz endpoint. For example:

curl 'https://servicename-xyz-uc.a.run.app/versionz'

To redeploy the Cloud Run instance:

  1. Go to the Cloud Run page in the Google Cloud console.

    Go to the Cloud Run page

  2. Select the instance hosting the sign-in page.

  3. Click Edit & Deploy New Revision.

  4. Optionally, specify advanced settings for the revision, or add an environment variable by clicking the Variables & Secrets tab.

  5. Click Deploy.

Advanced options

Customizing the sign-in page programmatically

In addition to using the /admin console, you can update the JSON configuration programmatically.

To get the current configuration, use the /get_admin_config endpoint. For example:

curl -H 'Authorization: Bearer [TOKEN]'
  'https://servicename-xyz-uc.a.run.app/get_admin_config'

To update the configuration, use /set_admin_config. For example:

curl -XPOST -H 'Authorization: Bearer [TOKEN]' -H "Content-type: application/json"
  -d '[UPDATED-CONFIG]' 'https://servicename-xyz-uc.a.run.app/set_admin_config'

Both REST calls require the https://www.googleapis.com/auth/devstorage.read_write scope, and a valid OAuth token must be attached to the Authorization header.

Setting environment variables

You can set environment variables on the Cloud Run instance to customize advance settings. The following table lists the available variables:

Variable Description
DEBUG_CONSOLE A boolean value (0 or 1) indicating whether to log all network request errors and details. Sensitive data will not be logged. Disabled (0) by default.
UI_CONFIG A string containing the JSON configuration for the sign-in page. Using this variable instead of the /admin panel avoids reading and writing to a Cloud Storage when accessing the configuration. Invalid configurations are ignored. Using the /admin panel to validate your JSON before setting this variable can help minimize syntax errors.
GCS_BUCKET_NAME A string overriding the default Cloud Storage bucket used to store the JSON configuration. The file is named config.json, and the default location is gcip-iap-bucket-[CLOUD-RUN-SERVICE-NAME]-[PROJECT-NUMBER].
ALLOW_ADMIN A boolean value (0 or 1) indicating whether to allow access to the /admin configuration panel. Enabled (1) by default.

You'll need to deploy a new revision of the Cloud Run instance after updating variables before the changes take effect. To learn more about environment variables, see the Cloud Run documentation.

Customizing the domain

By default, users will see the URL of the Cloud Run instance when signing in. To specify a custom domain instead:

  1. Follow the steps in Mapping custom domains to set a custom domain for the Cloud Run instance.

  2. Configure IAP to use the new authentication URL:

    1. Go to the IAP page in the Google Cloud console.

      Go to the IAP page

    2. Select the resource protected by IAP.

    3. In the side panel, select the Edit icon next to the Login URL field.

    4. Select Use an existing hosted sign-in page, and choose your domain from the drop-down menu.

    5. Click Save.

Using one sign-in page for multiple IAP resources

You can protect multiple IAP resources using the same sign-in page. This reduces the work associated with managing multiple configurations.

To re-use a sign-in page:

  1. Follow the steps in this article to deploy the authentication page for the first resource protected by IAP.

  2. Enable IAP for the second resource. When prompted to specify a sign-in page, select I'll provide my own, and enter the URL of the Cloud Run service as the URL.

  3. Redeploy the Cloud Run service.

Troubleshooting

Login page doesn't work on browsers that disable third-party cookies or implement storage partitioning.

To resolve this issue, do the following:

  1. Redeploy your login page. The latest version of the login page uses the login page's domain as the authDomain.

  2. Customize your login page's configuration to ensure that authDomain is set as the URL of the login page for your IAP-protected application.

    {
      "AIzaSyC5DtmRUR...": {
        "authDomain": "LOGIN_URL",
        ...
      }
    }
    

What's next