Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aspnetcore/blazor/security/additional-scenarios.md
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ Alternatively, the setting can be made in the app settings (`appsettings.json`)
```json
{
"AzureAd": {
"Authority": "https://login.microsoftonline.com/common/oauth2/v2.0/",
"Authority": "https://login.microsoftonline.com/common/oauth2/v2.0",
...
}
}
Expand Down
218 changes: 197 additions & 21 deletions aspnetcore/blazor/security/blazor-web-app-with-entra.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,20 +109,33 @@ For the web API app's registration, the `Weather.Get` scope is configured in the
jwtOptions.Authority = "{AUTHORITY}";
```

The following examples use a Tenant ID of `aaaabbbb-0000-cccc-1111-dddd2222eeee`.
The following examples use a Tenant ID of `aaaabbbb-0000-cccc-1111-dddd2222eeee` and a directory name of `contoso`.

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:

```csharp
jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee";
```

If the app is registered in a Microsoft Entra External ID tenant:

```csharp
jwtOptions.Authority = "https://contoso.ciamlogin.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0";
```

If the app is registered in an AAD B2C tenant:

```csharp
jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0";
```

<!-- UPDATE 15.0 - Remove the following NOTE when .NET 15 releases in
2030, which is when B2C support will end for existing
customers prior to 5/1/25. -->

> [!NOTE]
> Azure Active Directory B2C is no longer available as a service to new customers as of May 1, 2025. AAD B2C tenants are supported for customers with accounts established prior to May 1, 2025 until 2030. For more information, see [Azure AD B2C: Frequently asked questions (FAQ)](/azure/active-directory-b2c/faq).

<xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions.Audience%2A> sets the Audience for any received JWT access token.

```csharp
Expand All @@ -131,14 +144,20 @@ jwtOptions.Audience = "{AUDIENCE}";

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.

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`.
The following examples use an Application (Client) Id of `11112222-bbbb-3333-cccc-4444dddd5555`. The third example uses a tenant domain of `contoso.onmicrosoft.com`.

ME-ID tenant example:

```csharp
jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";
```

Microsoft Entra External ID tenant:

```csharp
jwtOptions.Audience = "11112222-bbbb-3333-cccc-4444dddd5555";
```

AAD B2C tenant example:

```csharp
Expand All @@ -151,6 +170,15 @@ jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4

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 from the web API's registration. Don't include the scope name when taking the App ID URI from the portal.

The authentication configuration depends on the type of tenant:

* [ME-ID tenant configuration](#me-id-tenant-configuration)
* [Microsoft Entra External ID configuration](#microsoft-entra-external-id-configuration)

### ME-ID tenant configuration

*This section applies to an app registered in a Microsoft Entra ID or Azure AAD B2C tenant.*

In the `BlazorWebAppEntra` project's `Program` file, provide the values for the following placeholders in Microsoft Identity Web configuration:

```csharp
Expand All @@ -168,7 +196,7 @@ builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddDownstreamApi("DownstreamApi", configOptions =>
{
configOptions.BaseUrl = "{BASE ADDRESS}";
configOptions.Scopes = [ "{APP ID URI}/Weather.Get" ];
configOptions.Scopes = ["{APP ID URI}/Weather.Get"];
})
.AddDistributedTokenCaches();
```
Expand Down Expand Up @@ -200,7 +228,60 @@ builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddDownstreamApi("DownstreamApi", configOptions =>
{
configOptions.BaseUrl = "https://localhost:7277";
configOptions.Scopes = [ "api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get" ];
configOptions.Scopes = ["api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get"];
})
.AddDistributedTokenCaches();
```

### Microsoft Entra External ID configuration

*This section applies to an app registered in a Microsoft Entra External ID tenant.*

In the `BlazorWebAppEntra` project's `Program` file, provide the values for the following placeholders in Microsoft Identity Web configuration:

```csharp
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(msIdentityOptions =>
{
msIdentityOptions.CallbackPath = "/signin-oidc";
msIdentityOptions.Authority = "https://{DIRECTORY NAME}.ciamlogin.com/{TENANT ID}/v2.0";
msIdentityOptions.ClientId = "{CLIENT ID (BLAZOR APP)}";
msIdentityOptions.ResponseType = "code";
})
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApi("DownstreamApi", configOptions =>
{
configOptions.BaseUrl = "{BASE ADDRESS}";
configOptions.Scopes = ["{APP ID URI}/Weather.Get"];
})
.AddDistributedTokenCaches();
```

Placeholders in the preceding configuration:

* `{DIRECTORY NAME}`: The directory name of the tenant (publisher) domain.
* `{CLIENT ID (BLAZOR APP)}`: The application (client) ID.
* `{BASE ADDRESS}`: The web API's base address.
* `{APP ID URI}`: The App ID URI for web API scopes. Either of the following formats are used, where the `{CLIENT ID (WEB API)}` placeholder is the Client Id of the web API's Entra registration, and the `{DIRECTORY NAME}` placeholder is the directory name of the tenant (publisher) domain (example: `contoso`).
* ME-ID or Microsoft Entra External ID tenant format: `api://{CLIENT ID (WEB API)}`
* B2C tenant format: `https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID (WEB API)}`

Example:

```csharp
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(msIdentityOptions =>
{
msIdentityOptions.CallbackPath = "/signin-oidc";
msIdentityOptions.Authority = "https://contoso.ciamlogin.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0";
msIdentityOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
msIdentityOptions.ResponseType = "code";
})
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApi("DownstreamApi", configOptions =>
{
configOptions.BaseUrl = "https://localhost:7277";
configOptions.Scopes = ["api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get"];
})
.AddDistributedTokenCaches();
```
Expand Down Expand Up @@ -317,20 +398,33 @@ For the web API app's registration, the `Weather.Get` scope is configured in the
jwtOptions.Authority = "{AUTHORITY}";
```

The following examples use a Tenant ID of `aaaabbbb-0000-cccc-1111-dddd2222eeee`.
The following examples use a Tenant ID of `aaaabbbb-0000-cccc-1111-dddd2222eeee` and a directory name of `contoso`.

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:

```csharp
jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee";
```

If the app is registered in a Microsoft Entra External ID tenant:

```csharp
jwtOptions.Authority = "https://contoso.ciamlogin.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0";
```

If the app is registered in an AAD B2C tenant:

```csharp
jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0";
```

<!-- UPDATE 15.0 - Remove the following NOTE when .NET 15 releases in
2030, which is when B2C support will end for existing
customers prior to 5/1/25. -->

> [!NOTE]
> Azure Active Directory B2C is no longer available as a service to new customers as of May 1, 2025. AAD B2C tenants are supported for customers with accounts established prior to May 1, 2025 until 2030. For more information, see [Azure AD B2C: Frequently asked questions (FAQ)](/azure/active-directory-b2c/faq).

<xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions.Audience%2A> sets the Audience for any received JWT access token.

```csharp
Expand All @@ -339,14 +433,20 @@ jwtOptions.Audience = "{AUDIENCE}";

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.

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`.
The following examples use an Application (Client) Id of `11112222-bbbb-3333-cccc-4444dddd5555`. The third example uses a tenant domain of `contoso.onmicrosoft.com`.

ME-ID tenant example:

```csharp
jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";
```

Microsoft Entra External ID tenant:

```csharp
jwtOptions.Audience = "11112222-bbbb-3333-cccc-4444dddd5555";
```

AAD B2C tenant example:

```csharp
Expand All @@ -359,6 +459,15 @@ jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4

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 from the web API's registration. Don't include the scope name when taking the App ID URI from the portal.

The authentication configuration depends on the type of tenant:

* [ME-ID tenant configuration](#me-id-tenant-configuration)
* [Microsoft Entra External ID configuration](#microsoft-entra-external-id-configuration)

### ME-ID tenant configuration

*This section applies to an app registered in a Microsoft Entra ID or Azure AAD B2C tenant.*

In the `BlazorWebAppEntra` project's `Program` file, provide the values for the following placeholders in Microsoft Identity Web configuration:

```csharp
Expand All @@ -376,15 +485,15 @@ builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddDownstreamApi("DownstreamApi", configOptions =>
{
configOptions.BaseUrl = "{BASE ADDRESS}";
configOptions.Scopes = [ "{APP ID URI}/Weather.Get" ];
configOptions.Scopes = ["{APP ID URI}/Weather.Get"];
})
.AddDistributedTokenCaches();
```

Provide the same downstream API scope to the request transformer:

```csharp
List<string> scopes = [ "{APP ID URI}/Weather.Get" ];
List<string> scopes = ["{APP ID URI}/Weather.Get"];
```

Placeholders in the preceding configuration:
Expand Down Expand Up @@ -415,15 +524,80 @@ builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
{
configOptions.BaseUrl = "https://localhost:7277";
configOptions.Scopes =
[ "api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get" ];
["api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get"];
})
.AddDistributedTokenCaches();
```

Example:

```csharp
List<string> scopes = ["api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get"];
```

### Microsoft Entra External ID configuration

*This section applies to an app registered in a Microsoft Entra External ID tenant.*

In the `BlazorWebAppEntra` project's `Program` file, provide the values for the following placeholders in Microsoft Identity Web configuration:

```csharp
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(msIdentityOptions =>
{
msIdentityOptions.CallbackPath = "/signin-oidc";
msIdentityOptions.Authority = "https://{DIRECTORY NAME}.ciamlogin.com/{TENANT ID}/v2.0";
msIdentityOptions.ClientId = "{CLIENT ID (BLAZOR APP)}";
msIdentityOptions.ResponseType = "code";
})
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApi("DownstreamApi", configOptions =>
{
configOptions.BaseUrl = "{BASE ADDRESS}";
configOptions.Scopes = ["{APP ID URI}/Weather.Get"];
})
.AddDistributedTokenCaches();
```

Provide the same downstream API scope to the request transformer:

```csharp
List<string> scopes = ["{APP ID URI}/Weather.Get"];
```

Placeholders in the preceding configuration:

* `{DIRECTORY NAME}`: The directory name of the tenant (publisher) domain.
* `{CLIENT ID (BLAZOR APP)}`: The application (client) ID.
* `{BASE ADDRESS}`: The web API's base address.
* `{APP ID URI}`: The App ID URI for web API scopes. Either of the following formats are used, where the `{CLIENT ID (WEB API)}` placeholder is the Client Id of the web API's Entra registration, and the `{DIRECTORY NAME}` placeholder is the directory name of the tenant (publishers) domain (example: `contoso`).
* ME-ID or Microsoft Entra External ID tenant format: `api://{CLIENT ID (WEB API)}`
* B2C tenant format: `https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID (WEB API)}`

Example:

```csharp
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(msIdentityOptions =>
{
msIdentityOptions.CallbackPath = "/signin-oidc";
msIdentityOptions.Authority = "https://contoso.ciamlogin.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0";
msIdentityOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
msIdentityOptions.ResponseType = "code";
})
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApi("DownstreamApi", configOptions =>
{
configOptions.BaseUrl = "https://localhost:7277";
configOptions.Scopes = ["api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get"];
})
.AddDistributedTokenCaches();
```

Example:

```csharp
List<string> scopes = [ "api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get" ];
List<string> scopes = ["api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get"];
```

:::zone-end
Expand Down Expand Up @@ -591,7 +765,7 @@ In the app settings file (`appsettings.json`) of the `BlazorWebAppEntra` project
},
"DownstreamApi": {
"BaseUrl": "{BASE ADDRESS}",
"Scopes": [ "{APP ID URI}/Weather.Get" ]
"Scopes": ["{APP ID URI}/Weather.Get"]
}
}
```
Expand Down Expand Up @@ -619,7 +793,7 @@ Example:
},
"DownstreamApi": {
"BaseUrl": "https://localhost:7277",
"Scopes": [ "api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get" ]
"Scopes": ["api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get"]
}
```

Expand All @@ -645,7 +819,7 @@ builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
- .AddDownstreamApi("DownstreamApi", configOptions =>
- {
- configOptions.BaseUrl = "...";
- configOptions.Scopes = [ "..." ];
- configOptions.Scopes = ["..."];
- })
+ .AddDownstreamApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
.AddDistributedTokenCaches();
Expand All @@ -669,8 +843,8 @@ In the `MinimalApiJwt` project, add the following app settings configuration to
"Authentication": {
"Schemes": {
"Bearer": {
"Authority": "https://sts.windows.net/{TENANT ID (WEB API)}/",
"ValidAudiences": [ "{APP ID URI (WEB API)}" ]
"Authority": "https://sts.windows.net/{TENANT ID (WEB API)}",
"ValidAudiences": ["{APP ID URI (WEB API)}"]
}
}
},
Expand All @@ -683,12 +857,14 @@ Update the placeholders in the preceding configuration to match the values that

Authority formats adopt the following patterns:

* ME-ID tenant type: `https://sts.windows.net/{TENANT ID}/`
* B2C tenant type: `https://login.microsoftonline.com/{TENANT ID}/v2.0/`
* ME-ID tenant type: `https://sts.windows.net/{TENANT ID}`
* Microsoft Entra External ID: `https://{DIRECTORY NAME}.ciamlogin.com/{TENANT ID}/v2.0`
* B2C tenant type: `https://login.microsoftonline.com/{TENANT ID}/v2.0`

Audience formats adopt the following patterns (`{CLIENT ID}` is the Client Id of the web API; `{DIRECTORY NAME}` is the directory name, for example, `contoso`):

* ME-ID tenant type: `api://{CLIENT ID}`
* Microsoft Entra External ID: `{CLIENT ID}`
* B2C tenant type: `https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}`

The configuration is automatically picked up by the JWT bearer authentication builder.
Expand Down
Loading