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 ishttps://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 ishttps://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 ishttp://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 theydocker 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/
calledpunt-booker-production.yaml
with the following content:applications/production/punt-booker-production.yamltype: 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/
calledpunt-booker-staging.yaml
with the following content:applications/production/punt-booker-staging.yamltype: 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 underapplications/production
calledpunt-booker-local-dev.yaml
with the following content:applications/production/punt-booker-local-dev.yamltype: 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:
{
"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.
Granting a team group access (recommended)¶
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:
# ... 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:
# ... 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:
- If you have not yet done so, install the gcloud SDK.
- If you have not yet done so, authenticate as a user with access to the secret via
gcloud auth login
. - Visit the list of registered
applications and
copy the appropriate
secret_name
for your application. It will be of the formprojects/.../secrets/...
. -
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.