This sample demonstrates the following scenarios:
- Users authenticated with different OAuth2/OpenID Connect providers can access my web app with their existing identities.
- With a configuration change, not code change, I can switch my web api to use a different OAuth2/OpenID Connect provider.
Note that in this scenario, the web api is protected by requiring a user to authenticate, but not requring the user to consent for the web app to access the api on their behalf. This is the case where the web app and the web api all belong to the same company, there's no need for the user to delegate to the web app to access the api.
- My web app can access Azure blob storage in multiple Azure AD tenants using Service Principals in those tenants.
My web app can generate a Shared Access Signature URL for client applications to access storage.
- My web app can access Azure blob stroage in multiple Azure AD tenants using a Service Principal in my own tenant with Azure Lighthouse.
My web app can generate a Shared Access Signature URL for client applications to access storage without creating service principals in customer's tenants.
Whether you use Azure Lighthouse or not, you can also assign a Managed Service Identity or MSI to the web service to access storage so that you don't need to manage secrets for service principals. While this approach is superior in security, it only works if your web service and storage account are in the same Azure AD tenant.
The majority of the code in this sample is based on IdentityServer4 Quickstart Tutorial. You can write an identity provider (IDP) to achieve the authentication scenarios yourself. In fact, ASP.NET core middleware has great support for OAuth2 and OpenID Connect. However, IdentityServer4 is an OpenSource framework that already implements these protocols.
There's an instance of this solution deployed in Azure, using Github actions in this repo, that you can try without having to configure anything yourself:
- sample web client app: https://polyauthfrontend.azurewebsites.net using IdentityServer to federate against multiple external providers.
- another instance of the client app: https://polyauthfrontendaad.azurewebsites.net using Azure AD as the identity provider.
- another instance of the client app: https://polyauthfrontendgoogle.azurewebsites.net using Google as the identity provider.
- sample web api: https://polyauthbackend.azurewebsites.net.
- web app that runs IdentityServer4: https://polyauthserver.azurewebsites.net.
- Go to https://polyauthfrontend.azurewebsites.net,
- log in as zoe/zoe, then log out
- log in using an Azure AD account, then log out
- log in using a Google account, then log out (note that you need to log out of the client app, but also go to google to logout)
- log in to the Demo IdentityServer as bob/bob, then log out
- Go to https://polyauthfrontendaad.azurewebsites.net, login button will lead you directly to Azure AD.
- similarly, https://polyauthfrontendgoogle.azurewebsites.net, login button will lead you directly to Google.
- These are different instances of the same frontend application, the only difference is in MvcClient/appsettings.json,
AuthProvideris configured to go tomyidsrvvsaadvsgoogle. - Click on
CallBackendSvcSPto make a backend call from https://polyauthfrontend.azurewebsites.net and https://polyauthfrontendaad.azurewebsites.net respectively. This works because BackendSvc is configured to trust the IdentityServer and Azure AD, as seen in BackendSvc/appsettings.jsonTrustedAuthProviders. Examine how the BackendSvc verifies the issuer and audience of the Authorization token without any code specific to each identity provider.
- Go to https://polyauthfrontend.azurewebsites.net,
- log in using an Azure AD account, then
CallBackendSvcSP, you'll see a list of blobs prefixed with curve_. The last item in the list is a SAS url to the last blob on the list that you can use to directly access in the browser. - log out and log in using IdentityServer or Google, you'll see a different list of blobs.
- examine how BackendSvc is configured to use different Service Principals to access different Storage accounts based on the identity provider user signed in with.
- log in using an Azure AD account, then
- Go to https://polyauthfrontend.azurewebsites.net,
- log in using an Azure AD account, then
CallBackendSvc, you'll see a list of blobs. The last item in the list is a SAS url to the last blob on the list that you can use to directly access in the browser. - also try
CallBackendSvcMSI, you'll see the same list of blobs. The superiority of MSI is that there's no service principal configurations needed for the application. - log out and log in using IdentityServer or Google, you'll see a different list of blobs. The storage account where the blobs are located is in another Azure AD tenant. If you try
CallBackendSvcMSInow, it will throw an error, because MSI only works in the tenant where the web service and the storage account are colated. - examine how BackendSvc is configured to use a single Service Principal to access different Storage accounts in different Azure AD tenants, based on the identity provider user signed in with.
- log in using an Azure AD account, then
There are 3 projects in this repo:
- a sample web client app in the MvcClient folder
- a sample web api in the BackendSvc folder
- a web app that runs IdentityServer4 in the IdentityServer folder
The code in the repo is fully functional. You can clone and run them in localhost, with ports configured in each project's launchSettings.json. However, there are a lot settings in each project's appsettings.json that you must configure for your environments, for example, tenant ids, subscription ids, service principals, and storage accounts. You must also register your applications in Azure AD and Google.
If you want to set up the solution from scratch, here are the main steps:
- Register an application in Azure AD. Only registered apps can sign in a user in Azure AD.
- make sure to set
accessTokenAcceptedVersionto 2 in the manifest if you want to use Azure AD v2 tokens. - for a Azure AD multi-tenant application, follow this guideline to validate issuer. In this sample, we don't restrict the tenants who can sign in.
- make sure to set
- Register an application in Google.
- Follow this IdentityServer4 tutorial to set up and customize your ASP.NET Core client application with IdentityServer4. Verify IdentityServer works by logging in and navigate to the /Diagnostics page to see the auth token information.
- Add Azure AD as an external provider to the web app
- Add Demo IdentityServer4 as an external provider to the web app
- Add custom or non-standard scopes that a client can request.
- Implement web api to access Azure Blob Storage.
- Follow this Azure Lighthouse documentation to deploy a storage account in another tenant.
- Implement web api to access Azure Blob Storage in the target tenant you deployed with Azure Lighthouse.
- Implement web api to access Azure Blob Storage using MSI.
When running in localhost, IdentityService may not work in Chrome. But other browsers work. Since this just stopped working recently, it might be related to the recent SameSite cookie change.
To use GitHub actions to deploy to Azure, add a secret that will be used by az cli to log in to Azure. With Lighthouse, you can deploy the services to the customer's subscription with the provider's credential when you configure the secrets as following:
{
"clientId": "providers-service-principal-app-id",
"clientSecret": "providers-service-principal-secret",
"subscriptionId": "customers-subscription-id",
"tenantId": "providers-tenant-id"
}