Skip to content

Commit ff25b93

Browse files
committed
Updates
1 parent 2898966 commit ff25b93

File tree

1 file changed

+76
-63
lines changed

1 file changed

+76
-63
lines changed

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

Lines changed: 76 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,6 @@ This section explains how to configure the sample app.
5959

6060
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.
6161

62-
### Establish the client secret
63-
64-
Use the [Secret Manager tool](xref:security/app-secrets) to store the server app's client secret under the configuration key `AzureAd:ClientSecret`.
65-
66-
Create a client secret in the app's Entra ID registration in the Entra or Azure portal (**Manage** > **Certificates & secrets** > **New client secret**). Use the **Value** of the new secret in the following guidance.
67-
68-
Execute the following command in a command shell from the server project's directory, such as the Developer PowerShell command shell in Visual Studio. The `{SECRET}` placeholder is the client secret obtained from the app's registration:
69-
70-
```dotnetcli
71-
dotnet user-secrets set "AzureAd:ClientSecret" "{SECRET}"
72-
```
73-
74-
If using Visual Studio, you can confirm the secret is set by right-clicking the server project in **Solution Explorer** and selecting **Manage User Secrets**.
75-
7662
### Configure the app
7763

7864
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:
@@ -109,57 +95,41 @@ Example:
10995

11096
The callback path (`CallbackPath`) must match the redirect URI (login callback path) configured when registering the application in the Entra or Azure portal. Paths are configured in the **Authentication** blade of the app's registration. The default value of `CallbackPath` is `/signin-oidc` for a registered redirect URI of `https://localhost/signin-oidc` (a port isn't required).
11197

112-
<!-- UPDATE 9.0 Replace the following with the INCLUDE link after
113-
https://github.com/dotnet/AspNetCore.Docs/pull/33749
114-
merges.
98+
[!INCLUDE[](~/blazor/security/includes/secure-authentication-flows.md)]
11599

116-
[!INCLUDE[](~/blazor/security/includes/secure-authentication-flows.md)]
117-
-->
100+
### Establish the client secret
118101

119-
> [!WARNING]
120-
> Don't store app secrets, connection strings, credentials, passwords, personal identification numbers (PINs), private C#/.NET code, or private keys/tokens in client-side code, which is ***always insecure***. In test/staging and production environments, server-side Blazor code and web APIs should use secure authentication flows that avoid maintaining credentials within project code or configuration files. Outside of local development testing, we recommend avoiding the use of environment variables to store sensitive data, as environment variables aren't the most secure approach. For local development testing, the [Secret Manager tool](xref:security/app-secrets) is recommended for securing sensitive data.
102+
Create a client secret in the app's Entra ID registration in the Entra or Azure portal (**Manage** > **Certificates & secrets** > **New client secret**). Use the **Value** of the new secret in the following guidance.
121103

122-
## Redirect to the home page on sign out
104+
Use one of the following approaches to supply the client secret to the app:
123105

124-
When a user navigates around the app, the `LogInOrOut` component (`Layout/LogInOrOut.razor`) sets a hidden field for the return URL (`ReturnUrl`) to the value of the current URL (`currentURL`). When the user signs out of the app, the identity provider returns them to the page from which they signed out.
106+
* [Secret Manager tool](#secret-manager-tool)
107+
* [Azure Key Vault](#azure-key-vault)
125108

126-
If the user signs out from a secure page, they're returned back to the same secure page after signing out only to be sent back through the authentication process. This behavior is fine when users need to switch accounts frequently. However, a alternative app specification may call for the user to be returned to the app's home page or some other page after signing out. The following example shows how to set the app's home page as the return URL for sign-out operations.
109+
We strongly recommend that you avoid storing client secrets in project code or configuration files. Use secure authentication flows, such as either of the approaches in this section.
127110

128-
The important changes to the `LogInOrOut` component are demonstrated in the following example. There's no need to provide a hidden field for the `ReturnUrl` set to the home page at `/` because that's the default path. <xref:System.IDisposable> is no longer implemented. The <xref:Microsoft.AspNetCore.Components.NavigationManager> is no longer injected. The entire `@code` block is removed.
111+
### Secret Manager tool
129112

130-
`Layout/LogInOrOut.razor`:
113+
The [Secret Manager tool](xref:security/app-secrets) can store the server app's client secret under the configuration key `AzureAd:ClientSecret`.
131114

132-
```razor
133-
@using Microsoft.AspNetCore.Authorization
115+
Execute the following command in a command shell from the server project's directory, such as the Developer PowerShell command shell in Visual Studio. The `{SECRET}` placeholder is the client secret obtained from the app's registration:
134116

135-
<div class="nav-item px-3">
136-
<AuthorizeView>
137-
<Authorized>
138-
<form action="authentication/logout" method="post">
139-
<AntiforgeryToken />
140-
<button type="submit" class="nav-link">
141-
<span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true">
142-
</span> Logout @context.User.Identity?.Name
143-
</button>
144-
</form>
145-
</Authorized>
146-
<NotAuthorized>
147-
<a class="nav-link" href="authentication/login">
148-
<span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span>
149-
Login
150-
</a>
151-
</NotAuthorized>
152-
</AuthorizeView>
153-
</div>
117+
```dotnetcli
118+
dotnet user-secrets set "AzureAd:ClientSecret" "{SECRET}"
154119
```
155120

156-
## Obtain the client secret from Azure Key Vault
121+
If using Visual Studio, you can confirm the secret is set by right-clicking the server project in **Solution Explorer** and selecting **Manage User Secrets**.
122+
123+
### Azure Key Vault
157124

158125
[Azure Key Vault](https://azure.microsoft.com/products/key-vault/) provides a safe approach for providing the app's client secret to the app when hosting in [Microsoft Azure](https://azure.microsoft.com/).
159126

160-
To create a key vault and set a client secret, see [About Azure Key Vault secrets (Azure documentation)](/azure/key-vault/secrets/about-secrets), which cross-links resources to get started with Azure Key Vault. To implement the code in this section, record the key vault URI and the secret name from Azure when you create the key vault and secret.
127+
To create a key vault and set a client secret, see [About Azure Key Vault secrets (Azure documentation)](/azure/key-vault/secrets/about-secrets), which cross-links resources to get started with Azure Key Vault. To implement the code in this section, record the key vault URI and the secret name from Azure when you create the key vault and secret. When you set the access policy for the secret in the **Access policies** panel:
128+
129+
* Only the **Get** secret permission is required.
130+
* Select the application as the **Principal** for the secret.
161131

162-
The following `GetKeyVaultSecret` method retrieves a secret from a key vault. Add this method to the server project. Adjust the namespace (`BlazorSample.Helpers`) to match your project namespace scheme.
132+
The following `GetKeyVaultSecret` method retrieves a secret from a key vault. Add this method to the server project. Adjust the namespace (`BlazorSample.Helpers`) to match your project namespace scheme. If you aren't using Visual Studio, you can use the [Secret Manager tool](#secret-manager-tool) locally to store the secret and use the approach in this section in staging/production when the app is deployed.
163133

164134
`Helpers/AzureHelper`:
165135

@@ -177,6 +147,7 @@ public static class AzureHelper
177147
DefaultAzureCredentialOptions options = new()
178148
{
179149
// Specify the tenant ID to use the dev credentials when running the app locally
150+
// in Visual Studio.
180151
VisualStudioTenantId = tenantId,
181152
SharedTokenCacheTenantId = tenantId
182153
};
@@ -189,24 +160,32 @@ public static class AzureHelper
189160
}
190161
```
191162

192-
In the server project's `Program` file after Microsoft identity platform services are added (`AddMicrosoftIdentityWebApp`), obtain and apply the client secret using the following code:
163+
In the server project's `Program` file where services are registered, obtain and apply the client secret using the following code:
193164

194165
```csharp
195-
string tenantId = builder.Configuration.GetValue<string>("AzureAd:TenantId")!;
196-
string vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
197-
string secretName = builder.Configuration.GetValue<string>("AzureAd:SecretName")!;
166+
var tenantId = builder.Configuration.GetValue<string>("AzureAd:TenantId")!;
167+
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
168+
var secretName = builder.Configuration.GetValue<string>("AzureAd:SecretName")!;
198169

199170
builder.Services.Configure<MicrosoftIdentityOptions>(
200-
options =>
201-
{
202-
options.ClientSecret =
203-
AzureHelper.GetKeyVaultSecret(tenantId, vaultUri, secretName);
204-
});
171+
OpenIdConnectDefaults.AuthenticationScheme,
172+
options =>
173+
{
174+
options.ClientSecret =
175+
AzureHelper.GetKeyVaultSecret(tenantId, vaultUri, secretName);
176+
});
205177
```
206178

207-
Supply the vault URI and secret name from configuration.
179+
If you wish to control the environment where the preceding code operates, for example to avoid running the code locally because you've opted to use the [Secret Manager tool](#secret-manager-tool) for local development, you can wrap the preceding code in a conditional statement that checks the environment:
208180

209-
In the `AzureAd` section of `appsettings.json`, add configuration keys and values:
181+
```csharp
182+
if (!context.HostingEnvironment.IsDevelopment())
183+
{
184+
...
185+
}
186+
```
187+
188+
In the `AzureAd` section of `appsettings.json`, add the following configuration keys and values:
210189

211190
* The `{VAULT URI}` placeholder is the key vault URI. Include the trailing slash on the URI.
212191
* The `{SECRET NAME}` placeholder is the secret name.
@@ -220,10 +199,44 @@ Example:
220199

221200
```json
222201
"VaultUri": "https://contoso.vault.azure.net/",
223-
"SecretName": "BlazorSample_Entra"
202+
"SecretName": "BlazorWebAppEntra"
224203
```
225204

226-
Configuration is used to facilitate supplying values based on the app's environmental configuration files. For example, `appsettings.Development.json` for Development, `appsettings.Staging.json` for Staging, and `appsettings.Production.json` for Production can use dedicated key vaults and secret names for each environment.
205+
Configuration is used to facilitate supplying dedicated key vaults and secret values based on the app's environmental configuration files. For example, you can supply different values for `appsettings.Development.json` in Development, `appsettings.Staging.json` when Staging, and `appsettings.Production.json` for the production deployment. For more information, see <xref:blazor/fundamentals/configuration>.
206+
207+
## Redirect to the home page on sign out
208+
209+
When a user navigates around the app, the `LogInOrOut` component (`Layout/LogInOrOut.razor`) sets a hidden field for the return URL (`ReturnUrl`) to the value of the current URL (`currentURL`). When the user signs out of the app, the identity provider returns them to the page from which they signed out.
210+
211+
If the user signs out from a secure page, they're returned back to the same secure page after signing out only to be sent back through the authentication process. This behavior is fine when users need to switch accounts frequently. However, a alternative app specification may call for the user to be returned to the app's home page or some other page after signing out. The following example shows how to set the app's home page as the return URL for sign-out operations.
212+
213+
The important changes to the `LogInOrOut` component are demonstrated in the following example. There's no need to provide a hidden field for the `ReturnUrl` set to the home page at `/` because that's the default path. <xref:System.IDisposable> is no longer implemented. The <xref:Microsoft.AspNetCore.Components.NavigationManager> is no longer injected. The entire `@code` block is removed.
214+
215+
`Layout/LogInOrOut.razor`:
216+
217+
```razor
218+
@using Microsoft.AspNetCore.Authorization
219+
220+
<div class="nav-item px-3">
221+
<AuthorizeView>
222+
<Authorized>
223+
<form action="authentication/logout" method="post">
224+
<AntiforgeryToken />
225+
<button type="submit" class="nav-link">
226+
<span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true">
227+
</span> Logout @context.User.Identity?.Name
228+
</button>
229+
</form>
230+
</Authorized>
231+
<NotAuthorized>
232+
<a class="nav-link" href="authentication/login">
233+
<span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span>
234+
Login
235+
</a>
236+
</NotAuthorized>
237+
</AuthorizeView>
238+
</div>
239+
```
227240

228241
## Troubleshoot
229242

0 commit comments

Comments
 (0)