One sign-in across several apps
Azure AD B2C single sign-on across several applications, an Umbraco 7 to 13 upgrade with centralised SSO, and an identity service for service-to-service access.
Context
The platforms I work on are made of several applications. Without shared sign-in, each one asks people to log in separately, with its own account and its own password reset.
Problem
Separate sign-ins mean separate user stores and a worse experience for anyone who uses more than one app. An older Umbraco 7 site added its own maintenance work on top.
What I did
I set up Azure AD B2C as the identity provider for several applications. Each app sends people to B2C with OpenID Connect. Once someone has signed in to one app, B2C recognises their session, and the next app signs them in without asking again.
I led the upgrade of the Umbraco site from version 7 to 13, and moved it onto the same centralised sign-in.
Services need identity too. I built a centralised identity service on ASP.NET Core and IdentityServer 4 that secures service-to-service access with the OAuth 2.0 client credentials flow, covering its backend, frontend and test automation with Selenium.
For a digital marketplace, the same B2C sign-in leads into an onboarding flow with KYC and payment steps.
Result
One sign-in covers several applications, service-to-service calls go through one identity service, and the Umbraco upgrade reduced maintenance work.
Stack
- Azure AD B2C
- OpenID Connect
- OAuth 2.0
- IdentityServer 4
- .NET
- Umbraco
Step through it
Two recreations. First, single sign-on for people across two apps with Azure AD B2C and OpenID Connect. Below it, how one service calls another with the client credentials flow. Move through each at your own pace.
- Open App A. Someone opens App A. It has no session for them yet.
- Redirect to B2C. App A redirects the browser to Azure AD B2C with an OpenID Connect sign-in request.
- Sign in once. They sign in on the B2C page. B2C starts its own session for them.
- Code back to App A. B2C redirects back to App A with an authorization code.
- Exchange for tokens. App A exchanges the code with B2C for an ID token and an access token, and signs them in.
- Open App B. Later they open App B, which has no session for them either.
- Redirect to B2C. App B redirects to B2C the same way. B2C sees the session from step 3.
- Signed in silently. B2C sends a code straight back without asking for a password. App B signs them in. That is single sign-on.
Static preview. The interactive version needs JavaScript.
Service to service: the client credentials flow
No person is involved here: one backend service calls another. It proves who it is to the identity service, gets a short-lived token, and presents that token to the API. A recreation with made-up names and values.
- Ask for a token. The billing service sends its client ID and secret to the identity service, and asks only for the scope it needs.
- Receive a short-lived token. The identity service checks the client and its allowed scopes, then issues a signed access token that expires within the hour.
- Call the API with the token. The billing service sends the token as a Bearer header. It reuses the same token until it expires.
- Check the signature. The API validates the token against the identity service’s public signing keys, fetched once and cached, then checks issuer, audience, expiry and scope. No round trip per request.
- Return the data. The token is valid and carries the orders.read scope, so the API answers. A token without that scope would get 403 Forbidden.
Static preview. The interactive version needs JavaScript.