Azure AD app configuration for Web API
Published: 2021-12-26 03:11:23

# Summary
In this article, I am tyring to describe how to configure for your own APIs Azure Active Directory authentication and authorization with .NET application on Azure App Service. Two patterns of [authorization code flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow) and three patterns of [client credential flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow) are discussed. Here is [a sample code for .NET 6 auth API](https://github.com/koheikawata/authApiSample).

# TOC
- [OAuth 2.0 flows](#oauth2)
  - [Authorization code flow](#authorization-code-flow)
  - [Client credential flow](#client-credential-flow)
- [Configuration patterns](#configuration-patterns)
  - [Common configuration](#common-configuration)
  - [Authorization code flow 1](#authorization-code-flow1)
  - [Authorization code flow 2](#authorization-code-flow2)
  - [Client credential flow 1](#client-credential-flow1)
  - [Client credential flow 2](#client-credential-flow2)
  - [Client credential flow 3](#client-credential-flow3)
- [Token examples](#token-examples)
  - [Header](#header)
  - [Payload](#payload)
- [Configuration summary](#configuration-summary)

# OAuth 2.0 flows <a name="oauth2"></a>
For your own API authentication and authorization, you register your app in Azure AD and use [Microsoft Identity Platform that is compliant with OpenID Connect and OAuth 2.0 protocols](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols). There are some grant flows such as implicit, hybrid, or device code flow. Here I want to focus on **authorization code flow** and **client credential flow** for Web APIs.

## Authorization code flow <a name="authorization-code-flow"></a>
[Authorization code flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow) requires you to log in as a Azure AD user and then request a token for a Web API. There are three steps.

1. Request an authorization code
2. Request an access token
3. Send a request to a Web API with the token

**Authorization code flow example**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/24s23ym5ycz7e80j6zr5.png) 

## Client credential flow <a name="client-credential-flow"></a>
[Client credential flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow) does not require a Azure AD user but `roles` claim in the token. An alternative method without `roles` claim is ACL(Access Control List). In contrast with authorization code flow, client credential flow needs two steps.

1. Request an access token
2. Send a request to a Web API with the token

**Client credential flow example**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f1eu7667yokjylwiselv.png) 

# Configuration patterns <a name="configuration-patterns"></a>
I want to discuss some of configuration patterns of authorization code flow and client credential flow.
- Authorization code flow 1
- AUthorization code flow 2
- Client credential flow 1
- Client credential flow 2
- Client credential flow 3

For each pattern above, you have to configure four areas, Azure AD client, Azure AD backend, Client application, Backend API application.  

**Azure AD client/backend**
- Authentication
  - Platform
  - Redirect URIs
  - Front-channel logout URL
  - Access tokens
  - ID tokens
- Secrets
- API Permissions
  - Type (Delegated/Application)
  - Admin consent
- Expose an API
  - Additional ID URI
  - Scope
  - Who can consent
  - Authorized client applications
- App roles
  - Member types (User-Group/Application)
  - Value

**Client application for HTTP request**
- Type (GET/POST)
- Auth URL
- Token URL
- Client ID
- Client secret
- Scope

**Backend API (.NET application)**
- Controller
  - Attributes
- appsettings (AzureAd section)
  - Instance
  - Domain
  - ClientId
  - TenantId
  - CallbackPath
  - AllowWebApiToBeAuthorizedByACL
- launchSettings
  - iisSettings/sslPort

## Common configuration <a name="common-configuration"></a>
- `Authorized client applications` in Expose an API section is not needed for the patterns below.
- Neither [ID tokens](https://docs.microsoft.com/en-us/azure/active-directory/develop/id-tokens) nor [Access tokens](https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens) in Azure AD app Authentication section are required in the patterns below. The official explanation is below. It requires when using hybrid or implicit flow.

*Request a token directly from the authorization endpoint. If the application has a single-page architecture (SPA) and doesn't use the authorization code flow, or if it invokes a web API via JavaScript, select both access tokens and ID tokens. For ASP.NET Core web apps and other web apps that use hybrid authentication, select only ID tokens.*

## Authorization code flow 1 <a name="authorization-code-flow1"></a>
- Register only a backend app in Azure AD App Registration.
- For authorization code flow, you need to determine scope. In this example, it uses `access_as_user`.
- For authorization code flow, you need two endpoints
  - `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize`
  - `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token` 
- For authorization code flow, you need to set up Redirect URIs. `/signin-oidc` is from `CallbackPath` in a backend API .NET appsettings, `44321` from `sslPort` in launchSettings.
- HTTP request from the client application
  - Client ID: `{clientId of BACKEND}`
  - Client secret: `{client secret of BACKEND}`
  - Scope: `api://{clientId of BACKEND}/access_as_user`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f11mt8vl1yodppnw3pko.png) 

## Authorization code flow 2 <a name="authorization-code-flow2"></a>
- Register both client and backend apps in Azure AD App Registration.
- For authorization code flow, you need to determine scope. In this example, it uses `access_as_user`.
- For authorization code flow, you need two endpoints
  - `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize`
  - `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token` 
- For authorization code flow, you need to set up Redirect URIs. `/signin-oidc` is from `CallbackPath` in a backend API .NET appsettings, `44321` from `sslPort` in launchSettings.
- In Azure AD client, set up Delegated permission of `access_as_user` backend API. You can grant admin consent in Azure AD, otherwise you are asked when logging in.
- HTTP request from the client application
  - Client ID: `{clientId of CLIENT}`
  - Client secret: `{client secret of CLIENT}`
  - Scope: `api://{clientId of BACKEND}/access_as_user`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/epodgy2jitm8qlqky0kw.png)
 
## Client credential flow 1 <a name="client-credential-flow1"></a>
- Register only a backend app in Azure AD App Registration.
- For client credential flow, you only need `Additional ID URI` set, not need to determine scope. For HTTP request from client, you use have to set `api://{clientId}/.default`
- For client credential flow, you need only the token endpoint
 - `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token` 
- For client credential flow without App roles, you need to set up `AllowWebApiToBeAuthorizedByACL=true` otherwise you would get an error.
- HTTP request from the client application
  - Client ID: `{clientId of BACKEND}`
  - Client secret: `{client secret of BACKEND}`
  - Scope: `api://{clientId of BACKEND}/.default`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nnyon0lxc0324z1gz718.png) 

## Client credential flow 2 <a name="client-credential-flow2"></a>
- Register both client and backend apps in Azure AD App Registration.
- For client credential flow, you only need `Additional ID URI` set, not need to determine scope. For HTTP request from client, you use have to set `api://{clientId}/.default`
- For client credential flow, you need only the token endpoint
 - `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token` 
- For client credential flow without App roles, you need to set up `AllowWebApiToBeAuthorizedByACL=true` otherwise you would get an error.
- This pattern does not require `API Permissions` setting even though it has both client and backend Azure AD apps.
- HTTP request from the client application
  - Client ID: `{clientId of CLIENT}`
  - Client secret: `{client secret of CLIENT}`
  - Scope: `api://{clientId of BACKEND}/.default`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ehh2tx0gl1kg2v9kul5r.png)
 
## Client credential flow 3 <a name="client-credential-flow3"></a>
- Register both client and backend apps in Azure AD App Registration.
- For client credential flow, you only need `Additional ID URI` set, not need to determine scope. For HTTP request from client, you use have to set `api://{clientId}/.default`
- For client credential flow, you need only the token endpoint
 - `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token` 
- You create App roles for the backend Azure AD app. In this example, the app role is `Reader`
- `Application permission` has to be set in API Permissions of Azure AD client app. Admin consent should be set in Azure AD because this is a service login and does not have a browser to ask consent when logging in.
- HTTP request from the client application
  - Client ID: `{clientId of CLIENT}`
  - Client secret: `{client secret of CLIENT}`
  - Scope: `api://{clientId of BACKEND}/.default`
- In .NET application controller, you can set up an attribute `[Authorize(Roles = "Reader")]`. It works for `[Authorize]` as well.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4slejrcs6gqxmhwl3pz.png) 

# Token examples <a name="token-examples"></a>
## Header <a name="header"></a>

**JSON example**
```
{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "Mr5-AUibfBii...",
  "kid": "Mr5-AUibfBii..."
}
```

**Description**

|Claim|Description|
|:----|:----|
|typ|Type|
|alg|Algorithm|
|x5t|Thumbprint for the public key|
|kid|Same as x5t|

**Comparison of examples**

|Claim|Example (Authorization code flow)|Example (Client credential flow)|
|:----|:----|:----|
|typ|JWT|←|
|alg|RS256|←|
|x5t|Mr5-AUibfBi...|←|
|kid|Mr5-AUibfBi...|←|

## Payload <a name="payload"></a>

**JSON example**
```
{
  "aud": "api://xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "iss": "https://sts.windows.net/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/",
  "iat": 1640486334,
  "nbf": 1640486334,
  "exp": 1640490987,
  "acr": "1",
  "aio": "AUQAu/8TAAAAEWGo0AL...",
  "amr": [
    "wia"
  ],
  "appid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "appidacr": "1",
  "email": "koheikawata@xxxxx.com",
  "family_name": "Kawata",
  "given_name": "koheikawata@xxxxx.com",
  "idp": "https://sts.windows.net/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/",
  "in_corp": "true",
  "ipaddr": "xx.xx.xxx.xx",
  "name": "koheikawata@xxxxx.com Kawata",
  "oid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "rh": "0.AUoAT2_1IpDf1Ea7...",
  "scp": "access_as_user",
  "sub": "V2_FAxEN...",
  "tid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "unique_name": "koheikawata@xxxxx.com",
  "uti": "nk9QA...",
  "ver": "1.0"
}
```

**Description**

|Claim|Description|
|:----|:----|
|aud|Recipient of token, audience|
|iss|Issuer|
|iat|Issued At. Timestamp|
|nbf|Not before. Timestamp|
|exp|Expiration. Timestamp|
|acr|Authentication context class (Only access token)|
|aio|Internal claim by Azure AD|
|amr|How authenticated (wia: Windows Integrated Authentication)|
|appid|Application ID (Only access token)|
|appidacr|Public:0, Secret:1, Certificate:2 (Only access token)|
|email|Email address (Only ID token)|
|family_name|Last name (Only access token)|
|given_name|First name (Only access token)|
|idp|Identity provider|
|in_corp|Corporate network (Only access token)|
|ipaddr|IP address the user authenticated from (Only access token)|
|name|Name of human readable value|
|oid|Object ID of user or service principal|
|rh|Internal claim by Azure|
|scp|Scope (Only access token)|
|roles|The set of permissions exposed by your application|
|sub|Subject|
|tid|Tenant ID|
|unique_name|Name of human readable value|
|uti|Internal claim by Azure|
|ver|Version of the token|

**Comparison of examples**

|Claim|Example (Authorization code flow)|Example (Client credential flow)|
|:----|:----|:----|
|aud|api://{clientId}|←|
|iss|https://sts.windows.net/{tenantId}/|←|
|iat|1640271957|←|
|nbf|1640271957|←|
|exp|1640276399|←|
|acr|1| |
|aio|AUQAu/8TAAAA1Uw...|←|
|amr|wia| |
|appid|{clientId}|←|
|appidacr|1|←|
|email|koheikawata@xxxxx.com| |
|family_name|Kawata| |
|given_name|koheikawata@xxxxx.com| |
|idp|https://sts.windows.net/{tenantId}/|←|
|in_corp|TRUE| |
|ipaddr|xx.xx.xxx.xx| |
|name|koheikawata@xxxxx.com| |
|oid|{objectId}|←|
|rh|0.AUoAT2_1Ip...|←|
|scp|access_as_user| |
|roles| |Read|
|sub|V2_FAxENmMoue5VT...|{objectId}|
|tid|{tenantId}|←|
|unique_name|koheikawata@xxxxx.com| |
|uti|ZO37H...|←|
|ver|1|←|

# Configuration summary <a name="configuration-summary"></a>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6nkjnl8cl5svgqqfr9zb.png)