Skip to content

Commit a514f98

Browse files
authored
Follow-up security web API work (#35317)
1 parent 776622e commit a514f98

File tree

3 files changed

+115
-82
lines changed

3 files changed

+115
-82
lines changed

aspnetcore/blazor/call-web-api.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ The [`System.Net.Http.Json`](https://www.nuget.org/packages/System.Net.Http.Json
2020

2121
:::moniker range=">= aspnetcore-8.0"
2222

23+
## Use a token handler for web API calls
24+
25+
Blazor Web Apps with OIDC authentication can use a token handler approach to make outgoing requests to secure external web API calls. This approach is used by the `BlazorWebAppOidc` and `BlazorWebAppOidcServer` sample apps described in the next section.
26+
27+
For more information, see the following resources:
28+
29+
* <xref:blazor/security/additional-scenarios#use-a-token-handler-for-web-api-calls>
30+
* *Secure an ASP.NET Core Blazor Web App with OpenID Connect (OIDC)*
31+
* [Non-BFF pattern (Interactive Auto)](xref:blazor/security/blazor-web-app-oidc?view=aspnetcore-9.0&pivots=non-bff-pattern)
32+
* [Non-BFF pattern (Interactive Server)](xref:blazor/security/blazor-web-app-oidc?view=aspnetcore-9.0&pivots=non-bff-pattern-server)
33+
2334
## Sample apps
2435

2536
For working examples, see the following sample apps in the [Blazor samples GitHub repository (`dotnet/blazor-samples`)](https://github.com/dotnet/blazor-samples/) ([how to download](xref:blazor/fundamentals/index#sample-apps)).

aspnetcore/blazor/security/blazor-web-app-with-entra.md

Lines changed: 98 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ We recommend using separate registrations for apps and web APIs, even when the a
5151

5252
Register the web API (`MinimalApiJwt`) first so that you can then grant access to the web API when registering the app. The web API's tenant ID and client ID are used to configure the web API in its `Program` file. After registering the web API, expose the web API in **App registrations** > **Expose an API** with a scope name of `Weather.Get`. Record the App ID URI for use in the app's configuration.
5353

54-
Next, register the app (`BlazorWebAppEntra`). The app's tenant ID, tenant domain, and client ID, along with the web API's base address, App ID URI, and weather scope name, are used to configure the app in its `appsettings.json` file. Grant API permission to access the web API in **App registrations** > **API permissions**. If the app's security specification calls for it, you can grant admin consent for the organization to access the web API. Authorized users and groups are assigned to the app's registration in **App registrations** > **Enterprise applications**.
54+
Next, register the app (`BlazorWebAppEntra`) with a **Web** platform configuration and a **Redirect URI** of `https://localhost/signin-oidc` (a port isn't required). The app's tenant ID, tenant domain, and client ID, along with the web API's base address, App ID URI, and weather scope name, are used to configure the app in its `appsettings.json` file. Grant API permission to access the web API in **App registrations** > **API permissions**. If the app's security specification calls for it, you can grant admin consent for the organization to access the web API. Authorized users and groups are assigned to the app's registration in **App registrations** > **Enterprise applications**.
5555

56-
In the Entra or Azure portal's **Implicit grant and hybrid flows** app registration configuration, don't select either checkbox for the authorization endpoint to return **Access tokens** or **ID tokens**.
56+
In the Entra or Azure portal's **Implicit grant and hybrid flows** app registration configuration, don't select either checkbox for the authorization endpoint to return **Access tokens** or **ID tokens**. The OpenID Connect handler automatically requests the appropriate tokens using the code returned from the authorization endpoint.
5757

5858
Create a client secret in the app's registration in the Entra or Azure portal (**Manage** > **Certificates & secrets** > **New client secret**). Hold on to the client secret **Value** for use the next section.
5959

@@ -94,17 +94,59 @@ app.MapGet("/weather-forecast", () =>
9494

9595
The <xref:Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization%2A> extension method requires authorization for the route definition. For any controllers that you add to the project, add the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the controller or action.
9696

97-
## Configuration
97+
## Configure the backend web API project (`MinimalApiJwt`)
9898

99-
This section explains how to configure the sample app.
99+
Configure the project in the <xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions> of the <xref:Microsoft.Extensions.DependencyInjection.JwtBearerExtensions.AddJwtBearer%2A> call in the project's `Program` file.
100100

101-
<xref:Microsoft.Identity.Web.AppBuilderExtension.AddMicrosoftIdentityWebApp%2A> from [Microsoft Identity Web](/entra/msal/dotnet/microsoft-identity-web/) ([`Microsoft.Identity.Web` NuGet package](https://www.nuget.org/packages/Microsoft.Identity.Web), [API documentation](<xref:Microsoft.Identity.Web?displayProperty=fullName>)) is configured by the `AzureAd` section of the server project's `appsettings.json` file.
101+
For the web API app's registration, the `Weather.Get` scope is configured in the Entra or Azure portal in **Expose an API**.
102+
103+
<xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions.Authority%2A> sets the Authority for making OIDC calls.
104+
105+
```csharp
106+
jwtOptions.Authority = "{AUTHORITY}";
107+
```
108+
109+
The following examples use a Tenant ID of `aaaabbbb-0000-cccc-1111-dddd2222eeee`.
110+
111+
If the app is registered in an ME-ID tenant, the authority should match the issurer (`iss`) of the JWT returned by the identity provider:
112+
113+
```csharp
114+
jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
115+
```
116+
117+
If the app is registered in an AAD B2C tenant:
118+
119+
```csharp
120+
jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
121+
```
122+
123+
<xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions.Audience%2A> sets the Audience for any received JWT access token.
124+
125+
```csharp
126+
jwtOptions.Audience = "{AUDIENCE}";
127+
```
128+
129+
Match the value to just the path of the **Application ID URI** configured when adding the `Weather.Get` scope under **Expose an API** in the Entra or Azure portal. Don't include the scope name, "`Weather.Get`," in the value.
130+
131+
The following examples use an Application (Client) Id of `11112222-bbbb-3333-cccc-4444dddd5555`. The second example uses a tenant domain of `contoso.onmicrosoft.com`.
132+
133+
ME-ID tenant example:
134+
135+
```csharp
136+
jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";
137+
```
102138

103-
In the app's registration in the Entra or Azure portal, use a **Web** platform configuration with a **Redirect URI** of `https://localhost/signin-oidc` (a port isn't required). Confirm that **ID tokens** and access tokens under **Implicit grant and hybrid flows** are **not** selected. The OpenID Connect handler automatically requests the appropriate tokens using the code returned from the authorization endpoint.
139+
AAD B2C tenant example:
104140

105-
### Configure the app
141+
```csharp
142+
jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555";
143+
```
144+
145+
## Configure the server project (`BlazorWebAppEntra`)
146+
147+
<xref:Microsoft.Identity.Web.AppBuilderExtension.AddMicrosoftIdentityWebApp%2A> from [Microsoft Identity Web](/entra/msal/dotnet/microsoft-identity-web/) ([`Microsoft.Identity.Web` NuGet package](https://www.nuget.org/packages/Microsoft.Identity.Web), [API documentation](<xref:Microsoft.Identity.Web?displayProperty=fullName>)) is configured by the `AzureAd` section of the server project's `appsettings.json` file.
106148

107-
In the server project's app settings file (`appsettings.json`), provide the app's `AzureAd` section configuration. Obtain the application (client) ID, tenant (publisher) domain, and directory (tenant) ID from the app's registration in the Entra or Azure portal:
149+
Obtain the application (client) ID, tenant (publisher) domain, and directory (tenant) ID from the app's registration in the Entra or Azure portal. The App ID URI is obtained for the `Weather.Get` scope. Don't include the scope name, and there's no trailing slash.
108150

109151
```json
110152
"AzureAd": {
@@ -198,9 +240,9 @@ We recommend using separate registrations for apps and web APIs, even when the a
198240

199241
Register the web API (`MinimalApiJwt`) first so that you can then grant access to the web API when registering the app. The web API's tenant ID and client ID are used to configure the web API in its `Program` file. After registering the web API, expose the web API in **App registrations** > **Expose an API** with a scope name of `Weather.Get`. Record the App ID URI for use in the app's configuration.
200242

201-
Next, register the app (`BlazorWebAppEntra`). The app's tenant ID, tenant domain, and client ID, along with the web API's base address, App ID URI, and weather scope name, are used to configure the app in its `appsettings.json` file. Grant API permission to access the web API in **App registrations** > **API permissions**. If the app's security specification calls for it, you can grant admin consent for the organization to access the web API. Authorized users and groups are assigned to the app's registration in **App registrations** > **Enterprise applications**.
243+
Next, register the app (`BlazorWebAppEntra`) with a **Web** platform configuration and a **Redirect URI** of `https://localhost/signin-oidc` (a port isn't required). The app's tenant ID, tenant domain, and client ID, along with the web API's base address, App ID URI, and weather scope name, are used to configure the app in its `appsettings.json` file. Grant API permission to access the web API in **App registrations** > **API permissions**. If the app's security specification calls for it, you can grant admin consent for the organization to access the web API. Authorized users and groups are assigned to the app's registration in **App registrations** > **Enterprise applications**.
202244

203-
In the Entra or Azure portal's **Implicit grant and hybrid flows** app registration configuration, don't select either checkbox for the authorization endpoint to return **Access tokens** or **ID tokens**.
245+
In the Entra or Azure portal's **Implicit grant and hybrid flows** app registration configuration, don't select either checkbox for the authorization endpoint to return **Access tokens** or **ID tokens**. The OpenID Connect handler automatically requests the appropriate tokens using the code returned from the authorization endpoint.
204246

205247
Create a client secret in the app's registration in the Entra or Azure portal (**Manage** > **Certificates & secrets** > **New client secret**). Hold on to the client secret **Value** for use the next section.
206248

@@ -249,21 +291,59 @@ app.MapGet("/weather-forecast", () =>
249291

250292
The <xref:Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization%2A> extension method requires authorization for the route definition. For any controllers that you add to the project, add the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the controller or action.
251293

252-
## Configuration
294+
## Configure the backend web API project (`MinimalApiJwt`)
253295

254-
This section explains how to configure the sample app.
255-
256-
<xref:Microsoft.Identity.Web.AppBuilderExtension.AddMicrosoftIdentityWebApp%2A> from [Microsoft Identity Web](/entra/msal/dotnet/microsoft-identity-web/) ([`Microsoft.Identity.Web` NuGet package](https://www.nuget.org/packages/Microsoft.Identity.Web), [API documentation](<xref:Microsoft.Identity.Web?displayProperty=fullName>)) is configured by the `AzureAd` section of the server project's `appsettings.json` file.
296+
Configure the project in the <xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions> of the <xref:Microsoft.Extensions.DependencyInjection.JwtBearerExtensions.AddJwtBearer%2A> call in the project's `Program` file.
257297

258298
For the web API app's registration, the `Weather.Get` scope is configured in the Entra or Azure portal in **Expose an API**.
259299

260-
In the frontend app's registration in the Entra or Azure portal, use a **Web** platform configuration with a **Redirect URI** of `https://localhost/signin-oidc` (a port isn't required). Confirm that **ID tokens** and access tokens under **Implicit grant and hybrid flows** are **not** selected. The OpenID Connect handler automatically requests the appropriate tokens using the code returned from the authorization endpoint. Configure access to the web API using **API permissions**.
300+
<xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions.Authority%2A> sets the Authority for making OIDC calls.
301+
302+
```csharp
303+
jwtOptions.Authority = "{AUTHORITY}";
304+
```
305+
306+
The following examples use a Tenant ID of `aaaabbbb-0000-cccc-1111-dddd2222eeee`.
307+
308+
If the app is registered in an ME-ID tenant, the authority should match the issurer (`iss`) of the JWT returned by the identity provider:
309+
310+
```csharp
311+
jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
312+
```
313+
314+
If the app is registered in an AAD B2C tenant:
315+
316+
```csharp
317+
jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
318+
```
319+
320+
<xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions.Audience%2A> sets the Audience for any received JWT access token.
321+
322+
```csharp
323+
jwtOptions.Audience = "{AUDIENCE}";
324+
```
325+
326+
Match the value to just the path of the **Application ID URI** configured when adding the `Weather.Get` scope under **Expose an API** in the Entra or Azure portal. Don't include the scope name, "`Weather.Get`," in the value.
261327

262-
### Configure the server project
328+
The following examples use an Application (Client) Id of `11112222-bbbb-3333-cccc-4444dddd5555`. The second example uses a tenant domain of `contoso.onmicrosoft.com`.
263329

264-
In the server project's app settings file (`appsettings.json`), provide the app's `AzureAd` section configuration. Obtain the application (client) ID, tenant (publisher) domain, and directory (tenant) ID from the app's registration in the Entra or Azure portal.
330+
ME-ID tenant example:
331+
332+
```csharp
333+
jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";
334+
```
335+
336+
AAD B2C tenant example:
337+
338+
```csharp
339+
jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555";
340+
```
341+
342+
## Configure the server project (`BlazorWebAppEntra`)
343+
344+
<xref:Microsoft.Identity.Web.AppBuilderExtension.AddMicrosoftIdentityWebApp%2A> from [Microsoft Identity Web](/entra/msal/dotnet/microsoft-identity-web/) ([`Microsoft.Identity.Web` NuGet package](https://www.nuget.org/packages/Microsoft.Identity.Web), [API documentation](<xref:Microsoft.Identity.Web?displayProperty=fullName>)) is configured by the `AzureAd` section of the server project's `appsettings.json` file.
265345

266-
The App ID URI is obtained for the `Weather.Get` scope. Don't include the scope name, and there's no trailing slash.
346+
Obtain the application (client) ID, tenant (publisher) domain, and directory (tenant) ID from the app's registration in the Entra or Azure portal. The App ID URI is obtained for the `Weather.Get` scope. Don't include the scope name, and there's no trailing slash.
267347

268348
```json
269349
"AzureAd": {
@@ -331,64 +411,6 @@ If you don't add the signed-out callback path URI to the app's registration in E
331411
> Entra doesn't redirect a primary admin user (root account) or external user back to the Blazor application. Instead, Entra logs the user out of the app and recommends that they close all of their browser windows. For more information, see [postLogoutRedirectUri not working when authority url contains a tenant ID (`AzureAD/microsoft-authentication-library-for-js` #5783)](https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5783#issuecomment-1465217522).
332412
333413
[!INCLUDE[](~/blazor/security/includes/secure-authentication-flows.md)]
334-
335-
:::zone pivot="bff-pattern"
336-
337-
### Configure the backend web API project (`MinimalApiJwt`)
338-
339-
Configure the project in the <xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions> of the <xref:Microsoft.Extensions.DependencyInjection.JwtBearerExtensions.AddJwtBearer%2A> call in the project's `Program` file.
340-
341-
#### Authority
342-
343-
<xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions.Authority%2A> sets the Authority for making OIDC calls.
344-
345-
```csharp
346-
jwtOptions.Authority = "{AUTHORITY}";
347-
```
348-
349-
The following examples use a Tenant ID of `aaaabbbb-0000-cccc-1111-dddd2222eeee`.
350-
351-
If the app is registered in an ME-ID tenant, the authority should match the issurer (`iss`) of the JWT returned by the identity provider:
352-
353-
```csharp
354-
jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
355-
```
356-
357-
If the app is registered in an AAD B2C tenant:
358-
359-
```csharp
360-
jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
361-
```
362-
363-
#### Audience
364-
365-
<xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions.Audience%2A> sets the Audience for any received JWT access token.
366-
367-
```csharp
368-
jwtOptions.Audience = "{AUDIENCE}";
369-
```
370-
371-
Match the value to just the path of the **Application ID URI** configured when adding the `Weather.Get` scope under **Expose an API** in the Entra or Azure portal. Don't include the scope name, "`Weather.Get`," in the value.
372-
373-
The following examples use an Application (Client) Id (`{CLIENT ID}`) of `11112222-bbbb-3333-cccc-4444dddd5555`. The second example uses a directory name (`{DIRECTORY NAME}`) of `contoso`.
374-
375-
If the app is registered in an ME-ID tenant:
376-
377-
App ID URI (`{APP ID URI}`): `api://{CLIENT ID}`
378-
379-
```csharp
380-
jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";
381-
```
382-
383-
If the app is registered in an AAD B2C tenant:
384-
385-
App ID URI (`{APP ID URI}`): `https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}`
386-
387-
```csharp
388-
jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555";
389-
```
390-
391-
:::zone-end
392414

393415
### Establish the client secret
394416

0 commit comments

Comments
 (0)