(If you want to get right to coding, you can go here.)
I created the Identity Bridge to make it easier for IT admins to set up user access across cloud systems. My Microsoft Identity background led me to believe this would be simple (the value prop and product screenshots are). Yet, I was taken aback by the lack of clarity around enterprise authentication. It turns out that, as my kindred spirit on Reddit observes, “the relatively mundane task of creating a standalone SPA… which calls a secured ASP.Net API is incredibly hard to achieve and is almost entirely lacking in documentation.”
The complexity here is rooted in a blend of organizational dynamics, market influences, and well-intentioned product management decisions. I hope this post helps others who can’t just chat with the developers of these components.
Design Decisions
Confidential Client
The Microsoft Authentication Library (MSAL) has Single Page Public Clients (Angular, JavaScript, React). Here’s why you also want a Confidential Client:
- Application permissions are not available to Public Clients.
- Metered APIs are not available to Public Clients.
- Code tampering possible with a Public Client could allow an attacker to modify app actions that perform sensitive reads or critical writes.
- SPA Authorization Code: a recent update allows a Confidential Client to retrieve a SPA Authorization Code to improve performance and streamline token retrieval to avoid popup blockers.
Microsoft Identity Web
The ASP.NET team is trying to clarify auth. They offer ASP.NET Core Identity (not related to Azure AD) for developers to manage their own identities. They suggest developers use Duende Software Identity Server for token-based authentication. Developers are confused by the ASP.NET team’s recommendations around identity, the various technologies named “Identity”, and samples of varying degrees of obsolescence.
Here is what you need to know:
- Azure AD as the identity platform: For apps aimed at corporate users, you want to use Azure AD (or some other cloud-based enterprise IAM). You don’t want to manage usernames and passwords in a database (i.e. ASP.NET Core Identity). Multi-factor authentication, phishing-resistant auth methods, device compliance checks, and single-sign-on across resources and organizations are some of the wheels that you don’t want to re-invent.
- Counterpoint: This makes app adoption gated by the customer IT/tenant admin. Issuing passwords gives app developers the ability to bypass this friction and have apps up and running for their customers without waiting for the customer tenant admin to consent. LucidChart, for example, supports all auth methods (email, Google, Office 365, slack).
- Denouement: If your app, like LucidChart, allows sign up with non-Azure AD accounts, your app should, like LucidChart, provide nudges that the user really should be using Office 365 Single Sign On (Azure AD authentication) for the best and most secure end user experience. My focus is on enterprise admin tools that require Global Admin consent, so I don’t need to worry about this nuance.
- Microsoft Identity Web as the API: You want to use the highest level of abstraction (i.e. own the smallest amount of attack surface) when authenticating to Azure AD. You don’t want to implement at the protocol level. You don’t want to code directly to MSAL.NET. You want to use the (sometimes lightly documented) Microsoft Identity Web authentication library. As has been historically the case, reading Microsoft’s framework source code is essential; however, articles like this one and posts like this one from Bill Fiddes at Microsoft do help.
React, Vite, TypeScript
React is widely adopted and the KendoReact UI component library from Telerik is highly capable. I started with the ASP.NET Core React template and this article helps to understand the ASP.NET Core Single Page Application model. There are many places to read on the benefits of Vite over CRA, but this post about CRA from 2021 is conclusive for me, “it’s mostly in maintenance mode and does not strive to be the best tool for production React apps.” I prefer finding errors quickly and so prefer the linting and type checking capabilities of TypeScript.
Writing the Code
As the devil is in the details, I’ve created this detailed walkthrough if you want to get this working and deployed in Azure.
Hopefully this helps others going down this path.