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 and three patterns of client credential flow are discussed. Here is a sample code for .NET 6 auth API.
TOC
OAuth 2.0 flows
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. 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
Authorization 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.
- Request an authorization code
- Request an access token
- Send a request to a Web API with the token
Authorization code flow example
Client credential flow
Client credential 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.
- Request an access token
- Send a request to a Web API with the token
Client credential flow example
Configuration patterns
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
-
Authorized client applications
in Expose an API section is not needed for the patterns below. - Neither ID tokens nor 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
- 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 fromCallbackPath
in a backend API .NET appsettings,44321
fromsslPort
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
- Client ID:
Authorization code flow 2
- 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 fromCallbackPath
in a backend API .NET appsettings,44321
fromsslPort
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
- Client ID:
Client credential flow 1
- 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 setapi://{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
- Client ID:
Client credential flow 2
- 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 setapi://{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
- Client ID:
Client credential flow 3
- 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 setapi://{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
- Client ID:
- In .NET application controller, you can set up an attribute
[Authorize(Roles = "Reader")]
. It works for[Authorize]
as well.
Token examples
Header
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
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 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 | ← |
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 | ← |
Top comments (0)