-
Notifications
You must be signed in to change notification settings - Fork 218
Web APIs
The Microsoft Identity Web library also enables web APIs to work with the Microsoft identity platform, enabling them to process access tokens for both work and school and Microsoft personal accounts, as well as B2C.
Assuming you have a similar configuration in appsettings.json
:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "msidentitysamplestesting.onmicrosoft.com",
"TenantId": "7f58f645-c190-4ce5-9de4-e2b7acd2a6ab",
"ClientId": "a4c2469b-cf84-4145-8f5f-cb7bacf814bc"
},
...
}
To enable the web API to accept tokens emitted by the Microsoft identity platform, replace this code in your web API's Startup.cs file:
using Microsoft.Identity.Web;
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));
...
}
...
}
...with this code, using the AuthenticationBuilder
:
using Microsoft.Identity.Web;
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration);
...
}
...
}
or with this code, using the services directly:
using Microsoft.Identity.Web;
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
...
}
...
}
This method enables your web API to be protected using the Microsoft identity platform. This includes validating the token in all scenarios (single- and multi-tenant applications) in the Azure public and national clouds.
See also:
- ASP.NET Core Web API incremental tutorial chapter 1.1, Protect the web API
- Protected web API scenario overview in the Microsoft identity platform documentation, and related articles
The configuration file above assumes that the App ID URI for your application (the base segment of scopes exposed by your Web API) is api://{ClientID}. This is the default when your register your application with the application registration portal. However, you can override it. In that case, you'll want to explicitly set the Audience
in your configuration to match the App ID URI for your Web API
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "msidentitysamplestesting.onmicrosoft.com",
"TenantId": "7f58f645-c190-4ce5-9de4-e2b7acd2a6ab",
"ClientId": "a4c2469b-cf84-4145-8f5f-cb7bacf814bc",
"Audience": "api://myappreg.azurewebsites.net"
},
...
}
Assuming you have a similar configuration in appsettings.json
:
{
"AzureAdB2C": {
"Instance": "https://fabrikamb2c.b2clogin.com",
"ClientId": "90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6",
"Domain": "fabrikamb2c.onmicrosoft.com",
"SignedOutCallbackPath": "/signout/B2C_1_susi",
"SignUpSignInPolicyId": "b2c_1_susi",
"ResetPasswordPolicyId": "b2c_1_reset",
"EditProfilePolicyId": "b2c_1_edit_profile" // Optional profile editing policy
},
// more here
}
To enable the web API to accept tokens emitted by Azure AD B2C, have the following code in your Web API:
using Microsoft.Identity.Web;
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMicrosoftIdentityWebApiAuthentication(Configuration, "AzureAdB2C");
...
}
...
}
In the controller methods, protected web APIs needs to verify that the the token used to called them has the right:
- scopes in the case of APIs called on behalf of a user
- app roles, in the case of APIs called by daemon applications
A Web API that is called on behalf of users needs to verify the scopes in the controller actions using the VerifyUserHasAnyAcceptedScope
extension method on the HttpContext.
[Authorize]
public class HomeController : Controller
{
static string[] scopeRequiredByAPI = new string[] { "access_as_user" };
...
public async Task<IActionResult> Action()
{
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByAPI);
{
}
A Web API that accepts daemon applications need to verify the application roles in the controller actions
[Authorize]
public class HomeController : Controller
{
public async Task<IActionResult> Action()
{
HttpContext.ValidateAppRole("acceptedRole1")
// Do the work
}
Alternatively, it can use the [Authorize("role")]
attributes on the controller or an action (or a razor page)
[Authorize("role")]
MyController
But for this, you'll need to map the Role claim to "roles" in the startup.cs
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
// The claim in the Jwt token where App roles are available.
options.TokenValidationParameters.RoleClaimType = "roles";
});
this is not the best solution if you also need to do authorization based on groups.
For details see the Web app incremental tutorial on authorization by roles and groups.
If you want your web API to, moreover, call downstream web APIs, add the .EnableTokenAcquisitionToCallDownstreamApi()
line, and then choose a token cache implementation, for example .AddInMemoryTokenCaches()
:
using Microsoft.Identity.Web;
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMicrosoftIdentityWebApiAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
...
}
...
}
As with web apps, you can choose various token cache implementations.
If you're certain that your web API will need some specific scopes, you can optionally pass them as arguments to EnableTokenAcquisitionToCallDownstreamApi
.
For Azure AD B2C, the code will be:
using Microsoft.Identity.Web;
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMicrosoftIdentityWebApiAuthentication(Configuration, "AzureAdB2C")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
...
}
...
}
To enable your web API to call downstream APIs:
-
Add (as in web apps) a parameter of type
ITokenAcquisition
to the constructor of your controller. TheITokenAcquisition
service will be injected by dependency injection by ASP.NET Core. -
In your controller actions, verify that the token contains the scopes expected by the action. To do so, call the
VerifyUserHasAnyAcceptedScope
extension method on theHttpContext
. -
Alternatively if your web API is called by a daemon app, use
ValidateAppRoles()
-
In your controller actions, call
ITokenAcquisition.GetAccessTokenForUserAsync
, passing the scopes for which to request a token. -
Alternatively if you controller calls a downstream API on behalf of itself (instead of on behalf of the user), call
ITokenAcquisition.GetAccessTokenForApplicationAsync
, passing the scopes for which to request a token.
The following code snippet shows how to combine these steps:
[Authorize]
public class HomeController : Controller
{
readonly ITokenAcquisition tokenAcquisition;
static string[] scopeRequiredByAPI = new string[] { "access_as_user" };
...
public async Task<IActionResult> Action()
{
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByAPI);
string[] scopes = new []{"user.read"};
try
{
string accessToken = await _tokenAcquisition.GetAccessTokenOnBehalfOfUser(scopes);
// call the downstream API with the bearer token in the Authorize header
}
catch (MsalUiRequiredException ex)
{
await _tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeaderAsync(HttpContext, scopes, ex);
}
...
}
When your web API tries to get a token for the downstream API, the token acquisition service may throw a MsalUiRequiredException
. The MsalUiRequiredException
indicates that the user on the client calling the web API needs to perform additional actions, for example, multi-factor authentication.
Given that the web API isn't capable of performing such interaction itself, the exception needs to be passed to the client. To propagate the exception back to the client, catch the exception and call the ITokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeaderAsync
method.
A Web API that accepts daemon applications need to verify the application roles in the controller actions
HttpContext.ValidateAppRole("acceptedRole1")
If your Web API wants to call a downstream web API on behalf of itself (not of behalf of a user), you can use ITokenAcquisition.GetAccessTokenForAppAsync in the controller. The code in the startup.cs file is the same as when you call an API on behalf of a user, and the constructor of your controller or Razor page injects an
ITokenAcquisition` service.
[Authorize]
public class HomeController : Controller
{
readonly ITokenAcquisition tokenAcquisition;
...
public async Task<IActionResult> Action()
{
string[] scopes = new []{"users.read.all"};
HttpContext.ValidateAppRole("acceptedRole1")
string token = await tokenAcquisition.GetAccessTokenForAppAsync(scopes);
...
// call the downstream API with the bearer token in the Authorize header
}
- Home
- Why use Microsoft Identity Web?
- Web apps
- Web APIs
- Using certificates
- Minimal support for .NET FW Classic
- Logging
- Azure AD B2C limitations
- Samples
- Web apps
- Web app samples
- Web app template
- Call an API from a web app
- Managing incremental consent and conditional access
- Web app troubleshooting
- Deploy to App Services Linux containers or with proxies
- SameSite cookies
- Hybrid SPA
- Web APIs
- Web API samples
- Web API template
- Call an API from a web API
- Token Decryption
- Web API troubleshooting
- web API protected by ACLs instead of app roles
- gRPC apps
- Azure Functions
- Long running processes in web APIs
- Authorization policies
- Generic API
- Customization
- Logging
- Calling graph with specific scopes/tenant
- Multiple Authentication Schemes
- Utility classes
- Setting FIC+MSI
- Mixing web app and web API
- Deploying to Azure App Services
- Azure AD B2C issuer claim support
- Performance
- specify Microsoft Graph scopes and app-permissions
- Integrate with Azure App Services authentication
- Ajax calls and incremental consent and conditional access
- Back channel proxys
- Client capabilities