Skip to content

How to add University of Cambridge single-sign-on in to web applications

In this guide you will learn how to register credentials required to enable single-sign-on in your web application and make those credentials available to your services running in Google Cloud.

Our SSO service is powered by Microsoft Entra ID. Entra ID is an implementation of the OAuth 2.0 authentication standard.

The UIS provides generic documentation on integrating SSO with web applications. Configuration varies between web frameworks but in general you will need the following information to configure sign in:

  • One or more URLs providing OAuth 2.0 or OpenID Connect (OIDC) endpoints. Appropriate values for University of Cambridge sign-in are provided elsewhere in the guidebook.
  • A "redirect URL" which is a URL pointing to your web application. Entra ID redirects the user to this URL after sign in is completed. This URL is web framework dependant.
  • OAuth 2.0 "client credentials". For client-side single page web applications this is a non-secret "client id". For server-side web applications this also includes a "client secret" which must be kept confidential.

This guide covers how to register the OAuth 2.0 client credentials.

What you will need and what you will get

You will need:

  • To know what format you want your client credentials to take. This is called a "template". The default format is a JSON document.
  • To know which Google Cloud IAM principals (i.e. service accounts, users, groups, etc) should be able to read the client credentials. Usually these will be the service accounts associated with the Cloud Run services hosting your application.
  • A logo image for your application which is 215x215, in the PNG format and less than 100Kb in size. See the Microsoft documentation for details.
  • A human-friendly name for your application.
Why do I need a name and logo?

The name and logo for your application appears when users first sign in to your application when they authorise the application to access personally identifying information such as name and email address. The application name and logo also appear on the my applications page where users can revoke this authorisation at a later date. If users revoke their authorisation, they will be prompted to authorise access again when they next sign in to your application.

Informing users what personally identifying information you will use, asking consent and allowing that consent to be withdrawn is an important courtesy. Entra ID automates this process for you.

You will get:

  • A Google Secret Manager secret which holds the client credentials in the format you requested.
  • The secret will be readable by the IAM principals you specified.

Client credentials are automatically rotated as necessary and so it is best if your application loads them at start-up.

What if I don't notice credential rotation immediately?

The Entra ID Application Factory creates a rolling set of credentials. So, when your credential secret is rotated, the previous credential remains valid, by default, for 90 days. As long as your application re-loads the secret within those 90 days, it will move to the rotated credential before the original credential expires.

Case study

We will use the example of a web application called "Punt Booker" which expects to be configured using a YAML document with the following schema:

sign_in:
  client_id: "..."
  client_secret: "..."
  oidc_discovery_document_url: "..."

We'll also assume the following deployments:

  • The production web application will be hosted under https://punt-booker.apps.cam.ac.uk/ and the redirect URL for the web framework used is https://punt-booker.apps.cam.ac.uk/oauth2/redirect.
  • The production web application is running using the service account webapp@punt-booker-prod.iam.gserviceaccount.com and expects to mount a secret containing the sign-in configuration directly into the container at run time.
  • The staging web application will be hosted under https://test.punt-booker.apps.cam.ac.uk/ and the redirect URL for the web framework used is https://test.punt-booker.apps.cam.ac.uk/oauth2/redirect.
  • The staging web application runs with the service account identity webapp@punt-booker-test.iam.gserviceaccount.com.
  • When developing locally, the application will be hosted under https://localhost:8000/ and the redirect URL is http://localhost:8000/oauth2/redirect.
  • Developers can impersonate the service account local-dev@punt-booker-dev.iam.gserviceaccount.com and that service account is used to fetch a Google secret when they docker compose up their local development environment.

Open a Merge Request

Open a Merge Request (MR) in the Entra ID Application Factory project. The Merge Request should contain:

  • A logo for the application at logo-images/punt-booker.png.
  • A template file at credential-templates/punt-booker.tftpl specifying how credentials should be structured. We use the terraform template language and so we can make use of the yamlencode function:

    credential-templates/punt-booker.tftpl
    ${yamlencode({
      sign_in = {
        client_id                   = client_id
        client_secret               = client_secret
        oidc_discovery_document_url = "https://login.microsoftonline.com/${tenant_id}/.well-known/openid-configuration"
      }
    })
    
  • A file under applications/production/ called punt-booker-production.yaml with the following content:

    applications/production/punt-booker-production.yaml
    type: sign-in
    
    display_name: Punt Booker
    logo_image: punt-booker.png
    
    web_redirect_uris:
      - https://punt-booker.apps.cam.ac.uk/oauth2/redirect
    
    credential_secrets:
      sign_in:
        template: punt-booker-yaml
        iam_policy:
          "roles/secretmanager.secretAccessor":
            - serviceAccount:webapp@punt-booker-prod.iam.gserviceaccount.com
    
  • A file under applications/production/ called punt-booker-staging.yaml with the following content:

    applications/production/punt-booker-staging.yaml
    type: sign-in
    
    display_name: Punt Booker
    logo_image: punt-booker.png
    
    web_redirect_uris:
      - https://test.punt-booker.apps.cam.ac.uk/oauth2/redirect
    
    credential_secrets:
      sign_in:
        template: punt-booker-yaml
        iam_policy:
          "roles/secretmanager.secretAccessor":
            - serviceAccount:webapp@punt-booker-test.iam.gserviceaccount.com
    

    Note that this file is under applications/production/. In this case, "production" means "the production deployment of Entra ID application factory". * A file under applications/production called punt-booker-local-dev.yaml with the following content:

    applications/production/punt-booker-local-dev.yaml
    type: sign-in
    
    display_name: Punt Booker
    logo_image: punt-booker.png
    
    web_redirect_uris:
      - http://localhost:8000/oauth2/redirect
    
    credential_secrets:
      sign_in:
        template: punt-booker-yaml
        iam_policy:
          "roles/secretmanager.secretAccessor":
            - serviceAccount:local-dev@punt-booker-dev.iam.gserviceaccount.com
    

When you have opened the Merge Request, tag it teamCloud and alert the Cloud Team in their Teams channel. You or a reviewer can trigger a manual plan for the MR from the CI pipelines page of the MR. This can help verify that the format of your files is correct and that the things you expect to be created will be created.

Once merged and deployed, your applications will be created.

Wire the credentials secret into your application

I'm not yet ready to consume secrets automatically, how do I read them manually?

Generally we assume that applications will read the credential secret at startup. Some applications may not be able to do that. See the section below on manually retrieving secrets.

If you are a member of the uis-devops-division Lookup group and are signed into Google with your @cam.ac.uk address, you can see the list of registered applications. This document tells you the name of the secret containing your client credentials. In our example:

https://storage.cloud.google.com/entra-applications-11f0f206/applications.json
{
  "applications": {
    "punt-booker-production": {
      "application_id": "...",
      "credential_secrets": {
        "sign_in": {
          "secret_name": "projects/{...}/secrets/punt-booker-production-signin"
        }
      }
    },
    "punt-booker-staging": {
      "application_id": "...",
      "credential_secrets": {
        "sign_in": {
          "secret_name": "projects/{...}/secrets/punt-booker-staging-signin"
        }
      }
    },
    "punt-booker-local-dev": {
      "application_id": "...",
      "credential_secrets": {
        "sign_in": {
          "secret_name": "projects/{...}/secrets/punt-booker-local-dev-signin"
        }
      }
    },
    // ... other applications
  },
  // ... other properties
}

Add configuration to your Cloud Run service to mount the secret named in the config into your application.

Manually retrieving secrets

Warning

It is preferable, if possible, to automatically fetch credentials at run-time.

There are occasions where secrets cannot be fetched at run time from Google secrets and must be manually copied into application configuration.

Make a note of the end date

Credentials have an "end date" associated with them. A new credential will be written into the secret, without invalidating the first credential, half way through the validity period. CREATE A CALENDAR REMINDER to re-fetch the credential secret before it expires.

You can fetch the secret value manually using the gcloud command line. Since you authenticate as an ordinary user you need to add yourself to the credential_secrets.[...].iam_policy configuration.

We have a team data file which drives memberships of groups automatically. This file is documented on a separate page of the guidebook. If you are using manual fetching of secrets, it is best if you grant access to a group of users. Try to limit the set of users to only those people who need to access the secret in order to deploy the application.

For example, let's assume the team data file has the following entries:

{
  // ...
  "puntbooker": {
    "display_name": "Punt Booker App",
    "slug": "puntbooker",
    // ... "admin" and "view" groups ...
    "deploy": [
      "spqr2@gcloudadmin.g.apps.cam.ac.uk"
    ]
  },
  // ...
}

You can grant the puntbooker-deploy@gcloudadmin.g.apps.cam.ac.uk group rights to access the secret:

applications/production/punt-booker-production.yaml
# ... rest of file ...

credential_secrets:
  sign_in:
    template: punt-booker-yaml
    iam_policy:
      "roles/secretmanager.secretAccessor":
        - group:puntbooker-deploy@gcloudadmin.g.apps.cam.ac.uk

Granting an individual user access

If you don't have a useful group defined in the team data file, you can grant access to individuals. We prefer using gcloudadmin accounts when possible.

To grant a user access to the secret:

applications/production/punt-booker-production.yaml
# ... rest of file ...

credential_secrets:
  sign_in:
    template: punt-booker-yaml
    iam_policy:
      "roles/secretmanager.secretAccessor":
        - user:spqr2@gcloudadmin.g.apps.cam.ac.uk

Accessing the secret

Once you have configured access permissions, you can access the secret itself:

  1. If you have not yet done so, install the gcloud SDK.
  2. If you have not yet done so, authenticate as a user with access to the secret via gcloud auth login.
  3. Visit the list of registered applications and copy the appropriate secret_name for your application. It will be of the form projects/.../secrets/....
  4. Use the gcloud command line tool to access the secret:

    $ SECRET_NAME="{paste name of secret here}"
    $ gcloud secrets versions access "${SECRET_NAME}/versions/latest"
    sign_in:
      client_id: "..."
      client_secret: "..."
      oidc_discovery_document_url: "..."
    

REMEMBER TO SET A CALENDAR REMINDER to re-fetch the secret before the expiry date of the credential.

Summary

In this how to guide, you learned what OAuth 2.0 client credentials are, how to register them and how to store them in a Google Cloud secret using a custom format for your application.

Next steps