OpenId Connect (OIDC)
oidc is an extension of OAuth. Where OAuth provides authorization only OIDC extends it to include authentication.
Javascript Web Token (JWT)
A JWT is a way to transmit OAuth data (not limited to authorization). It has three parts, the header, the payload, and the signature, all base64 encoded.
More detailed information can be found on jwt.io. There is much more to OAuth than will be included here.
Header
The header provides metadata information about the token payload and signature. This allows the payload to be decoded and the signature verified
Payload
The payload contains the user information for the token, also known as claims. There are some known claims and there can also be custom claims.
The information in the payload, while signed, is not private. To exemplify this fact take a token you received from Azure Active Directory and paste it into https://jwt.io/, a public website not linked in any way to the issuing instance of AAD using only the information in the token to decode it and verify the signature.
Signature
The signature is the result of combining the header and the payload with a secret through the algorithm specified in the header. The signature can be used to verify that the token has not been tampered with between the issuer and the receiver and that the claimed issuer is the actual issuer.
Verification
For JWTs signed by an issuer with more than one secret there will be attributes in the header (e.g. kid) that specify which secret was used to sign the token. For symmetrically signed tokens (HS256) the secret is preshared and can be readily used to verify the signature. For assymetrical signed tokens (RS256) the private key is used to sign the token and the public key (readily available from the issuer) is used to verify the signature.
OIDC extension
"OpenID Connect is a simple identity layer on top of the OAuth protocol"
OAuth provides bearer authorization by virtue of the "bearer" of the token having been authorized by a trusted issuer. In a nutshell, the core of OpenID Connect adds authentication (necessary to perform authorization) on top of the authorization already present in OAuth 2.0.
More detailed information can be found on OpenID.net. There is much more to OpenID Connect than will be included here.
Scope
A scope is included in a request of an oidc token and is used to control what the token may be used for.
In Azure Active Directory one application may have many scopes but a scope may only have one application. This is not necessarily true for all oidc providers. Having a scope limited to only belonging to one application is why we must setup our applications and their registrations to request a new token from their current token to call downstream APIs with user information from the original token. If the scope were not limited to only one application it would be possible to request a token at the entry point with a scope that all downstream applications will recognize and accept. This is both more maintenance intensive / less flexible as well as less secure.
Workflow
Much of computer security is triangles and math; two parties that don't implicitly trust each other agree they trust a third party and they form a triangle using fancy math to ensure that nobody is compromising the triangle they form. In our case our applications are the two parties and Azure Active Directory is the trusted third party. We'll talk about the triangle, not the math.
Web Application Workflow
- a user visits a web page for the first time (therefore they have no cookies)
- the server expects a authorization cookie to accompany the request, yet there is none
- none is present so the server returns 401: Unauthorized with a security challenge informing the client (the browser) what kind of security it's looking for
- the browser uses the challenge information to know to go over to Azure Active Directory to ask for a note (a JWT) to give to the web page's server with a page redirect
- Azure Active Directory presents the user with it's own web page asking some questions or presenting information before issuing a note. The questions might include
- user credentials, like username and password or SSO information that has already been obtained
- information about the requesting party, giving the user a chance to see if something doesn't look right (if they're being tricked)
- what information about the user the requestor has asked for e.g. full name, email address. This is more common in public web sites asking for user information from public issuers.
- if the user consents Azure Active Directory writes the note and signs it. It also tells the client where to take the note from the preconfigured redirect registration in order to give it back to the requestor
- the client follows the redirect and presents the note to the server who requested it in the first place
- the server takes the note and brings it back to Azure Active Directory in some way, points at the signature and says "did you write this?"
- azure Active Directory says "yes"
- the server takes that note, writes it into a cookie or set of cookies, and redirects the client back to the page they started on
- now, with a cookie or set of cookies in hand, the browser is back where it started but the server accepts the request and returns a web page... assuming that, after all of that, the user identified by the token is allowed to access the page
Notes
some of the redirect steps have to do with how cookies work. Cookies are sent back and forth with requests and responses to and from the issuing server, meaning that Azure Active Directory, as the issuer of the token, cannot write a cookie on behalf of the web application server. The web application server cannot go to Azure Active Directory to get the token directly because it requires user authentication with Azure Active Directory and user consent and the user is in control of the browser.
this is the workflow for the way our UI applications are set up, with Blazor Server. This process would look a little different and be configured a little differently if we were using a browser-based SPA (e.g. react, Blazor wasm) because preshared secrets should not be trusted from or allowed into the browser.
API Workflow
now that our web application has a token representing an authenticated user how do we call an API? Remember the API will not respect a token granting access to the web application because they have different scopes by design.
- Azure Active Directory is configured ahead of time such that the API application won't challenge a valid token asking for user consent before accepting a request.
- the web application takes the token that is has and goes to Azure Active Directory with the token and it's own preconfigured secret (we use preshared client secrets, the other option is another triangle with x509 certificates) and asks for a new note
- Azure accepts the token and secret and writes a new note for the API application with the information it is configured to get
- if the first step were not completed Azure Active Directory would return a response indicating that the web application should challenge the client a second time to ask for additional consent before issuing the new token
- the new token is included as an Authorization: Bearer {token} header
- the API takes the note and brings it back to Azure Active Directory in some way, points at the signature and says "did you write this?"
- azure Active Directory says "yes"
- the server takes the note and evaluates what's written on it to decide whether or not to allow the request to proceed
More Depth
This has only scratched the surface and has purposefully glossed over many details. Now that you have the basics in hand you should know enough to be able to search Google to get real answers to real problems.
The best way to learn more on this, in my opinion, is to dive in and set it up for yourself, from scratch, with something you can debug and a database you can get at freely. Duende IdentityServer is .net, open source, has good guides, and doesn't add any of the additional restrictions or guard rails Azure Active Directory does; everything is in your hands, for better or worse. It is free for individuals, testing, and small businesses.
There are many other related security practices and technologies that all work together to provide fully secure application use, e.g. SSL. OAuth + oidc is not, in itself, a comprehensive security solution.
One important detail to note that won't be explained is that there are more than one kind of token involved however we have acted as if there is only one. ID tokens contain user claims while access tokens are what should actually be used to perform authorization.
Azure
Setting all this up in Azure Active Directory isn't hard... as long as you already know what you're doing. Figuring that out is a little more difficult the first time.
Application to Secure API
Here are the steps to set up one application to call another, assuming they both already have App Registrations that include at least one scope in AAD
- from the upstream caller app registration find the Application (client) ID
- go to the downstream called API > Expose an API
- under "Authorized client applications" click the "+ Add a client application" button
- put the client id of the upstream application into the client ID field and select the scopes you want that upstream caller to be allowed to use (likely there will only be one)
- take the scope of the downstream API and go to the code of the upstream application
- use that scope where ITokenAcquisition is used to request a new token (very likely the scope should be read from application configuration and the configuration object be used to read the value at this place in the code)
Additional Configuration for Swagger UI
- in the App Registration for the application with swagger UI go to Authentication
- add Single-page application redirects for
- {app base path}/swagger/oauth2-redirect.html
- assuming that swagger UI is hosted in the default location
- {local development base url}/swagger/oauth2-redirect.html
- assuming this is the app registration that corresponds to local development
- {app base path}/swagger/oauth2-redirect.html
- follow the configuration example of an application (e.g. Document API) already configured with instructions for Swashbuckle and Swagger UI to show the Authorize button and use the OAuth configuration
- as new versions of Swashbuckle are released you might find yourself needing to consult Google results on how to translate the old configuration code to the new package version's API
- in Swagger UI you should now be able to click the Authorize button on the right, above the list of API routes. You should see a section in the modal the corresponds to the OAuth configuration and, when you click login, it should take you through the OAuth redirect flow. At the end of the redirect flow you should be told that you've been authenticated. You are now ready to call the API through swagger UI!
Additional Configuration for Postman
- go to Azure Active Directory > App registrations
- find the App Registration for Postman (the current configuration simply names it "postman")
- if this needs to be configured follow the normal app registration steps. The desktop application redirect path is https://oauth.pstmn.io/v1/callback and the web app redirect path is https://oauth.pstmn.io/v1/browser-callback, these should be registered as Web Redirect URIs
- find the Application (client) ID in the overview (49cbce7d-dcee-4db1-a93e-4b540ee0e578)
- in the App Registration for the application to be called by Postman go to Expose an API
- under "Authorized client applications" click the "+ Add a client application" button
- put the client id of postman into the client ID field and select the scopes you want Postman to be allowed to use (likely there will only be one)
- go back to the App Registration for Postman to get the values needed for the following steps
- in Postman go to the Authorization tab of a request you have set up
- set the type to OAuth 2.0
- set Add authorization data to Request Headers
- set Access Token to Available Tokens
- set Header Prefix to Bearer
- set Token Name to whatever you want
- set Grant Type to Authorization Code
- if using the desktop application check the Authorize using browser checkbox
- set Auth URL to https://login.microsoftonline.com/{Directory (tenant) ID}/oauth2/v2.0/authorize
- set Access Token URL to https://login.microsoftonline.com/{Directory (tenant) ID}/oauth2/v2.0/token
- set Client ID to the Application (client) ID of the Postman App Registration
- set Client Secret to a known client secret from the Postman App Registration
- if none is known having more than one secret active isn't a problem so long as they don't get compromised, feel free to create or request more
- set Scope to the scope you want to use of the App Registration for the application you want to call with Postman
- you can set it to the scope of the Postman App Registration however if you don't have permission on your account to give consent for Postman's scope to access the called API's scope you'll hit a roadblock here
- set Client Authentication to Send as Basic Auth header
- click the Get New Access Token button
- if everything worked correctly the browser will open, you'll be taken through the redirect flow, and then you'll be taken back to postman with a modal showing you the token you just received
- you might be asked if you want to allow chrome to open links of a type that open Postman. You should say yes, this is the postman website trying to send the token back to postman the application from the browser tab it opened to follow the required redirects
- you are now ready to call the API!

