Skip to content

Commit 44e4427

Browse files
authored
Add AKV approach (#34583)
1 parent 745ce48 commit 44e4427

File tree

3 files changed

+207
-4
lines changed

3 files changed

+207
-4
lines changed

aspnetcore/blazor/security/account-confirmation-and-password-recovery.md

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,18 @@ Register the `AuthMessageSenderOptions` configuration instance in the `Program`
4343
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);
4444
```
4545

46-
## Configure a user secret for the provider's security key
46+
## Configure a secret for the email provider's security key
47+
48+
Receive the email provider's security key from the provider and use it in the following guidance.
49+
50+
Use either or both of the following approaches to supply the secret to the app:
51+
52+
* [Secret Manager tool](#secret-manager-tool): The Secret Manager tool stores private data on the local machine and is only used during local development.
53+
* [Azure Key Vault](#azure-key-vault): You can store the secret in a key vault for use in any environment, including for the Development environment when working locally. Some developers prefer to use key vaults for staging and production deployments and use the [Secret Manager tool](#secret-manager-tool) for local development.
54+
55+
We strongly recommend that you avoid storing secrets in project code or configuration files. Use secure authentication flows, such as either or both of the approaches in this section.
56+
57+
### Secret Manager tool
4758

4859
If the project has already been initialized for the [Secret Manager tool](xref:security/app-secrets), it will already have an app secrets identifier (`<AppSecretsId>`) in its project file (`.csproj`). In Visual Studio, you can tell if the app secrets ID is present by looking at the **Properties** panel when the project is selected in **Solution Explorer**. If the app hasn't been initialized, execute the following command in a command shell opened to the project's directory. In Visual Studio, you can use the Developer PowerShell command prompt.
4960

@@ -63,6 +74,91 @@ For more information, see <xref:security/app-secrets>.
6374

6475
[!INCLUDE[](~/blazor/security/includes/secure-authentication-flows.md)]
6576

77+
### Azure Key Vault
78+
79+
[Azure Key Vault](https://azure.microsoft.com/products/key-vault/) provides a safe approach for providing the app's client secret to the app.
80+
81+
To create a key vault and set a 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:
82+
83+
* Only the **Get** secret permission is required.
84+
* Select the application as the **Principal** for the secret.
85+
86+
Confirm in the Azure or Entra portal that the app has been granted access to the secret that you created for the email provider key.
87+
88+
> [!IMPORTANT]
89+
> A key vault secret is created with an expiration date. Be sure to track when a key vault secret is going to expire and create a new secret for the app prior to that date passing.
90+
91+
Add the following `AzureHelper` class to the server project. The `GetKeyVaultSecret` method retrieves a secret from a key vault. Adjust the namespace (`BlazorSample.Helpers`) to match your project namespace scheme.
92+
93+
`Helpers/AzureHelper.cs`:
94+
95+
```csharp
96+
using Azure;
97+
using Azure.Identity;
98+
using Azure.Security.KeyVault.Secrets;
99+
100+
namespace BlazorSample.Helpers;
101+
102+
public static class AzureHelper
103+
{
104+
public static string GetKeyVaultSecret(string tenantId, string vaultUri, string secretName)
105+
{
106+
DefaultAzureCredentialOptions options = new()
107+
{
108+
// Specify the tenant ID to use the dev credentials when running the app locally
109+
// in Visual Studio.
110+
VisualStudioTenantId = tenantId,
111+
SharedTokenCacheTenantId = tenantId
112+
};
113+
114+
var client = new SecretClient(new Uri(vaultUri), new DefaultAzureCredential(options));
115+
var secret = client.GetSecretAsync(secretName).Result;
116+
117+
return secret.Value.Value;
118+
}
119+
}
120+
```
121+
122+
Where services are registered in the server project's `Program` file, obtain and bind the secret with [Options configuration](xref:fundamentals/configuration/options):
123+
124+
```csharp
125+
var tenantId = builder.Configuration.GetValue<string>("AzureAd:TenantId")!;
126+
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
127+
128+
var emailAuthKey = AzureHelper.GetKeyVaultSecret(
129+
tenantId, vaultUri, "EmailAuthKey");
130+
131+
var authMessageSenderOptions =
132+
new AuthMessageSenderOptions() { EmailAuthKey = emailAuthKey };
133+
builder.Configuration.GetSection(authMessageSenderOptions.EmailAuthKey)
134+
.Bind(authMessageSenderOptions);
135+
```
136+
137+
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:
138+
139+
```csharp
140+
if (!context.HostingEnvironment.IsDevelopment())
141+
{
142+
...
143+
}
144+
```
145+
146+
In the `AzureAd` section of `appsettings.json` in the server project, confirm the presence of the app's Entra ID `TenantId` and add the following `VaultUri` configuration key and value, if it isn't already present:
147+
148+
```json
149+
"VaultUri": "{VAULT URI}"
150+
```
151+
152+
In the preceding example, the `{VAULT URI}` placeholder is the key vault URI. Include the trailing slash on the URI.
153+
154+
Example:
155+
156+
```json
157+
"VaultUri": "https://contoso.vault.azure.net/"
158+
```
159+
160+
Configuration is used to facilitate supplying dedicated key vaults and secret names based on the app's environmental configuration files. For example, you can supply different configuration 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>.
161+
66162
## Implement `IEmailSender`
67163

68164
The following example is based on Mailchimp's Transactional API using [Mandrill.net](https://www.nuget.org/packages/Mandrill.net). For a different provider, refer to their documentation on how to implement sending an email message.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ To create a key vault and set a client secret, see [About Azure Key Vault secret
156156
> [!IMPORTANT]
157157
> A key vault secret is created with an expiration date. Be sure to track when a key vault secret is going to expire and create a new secret for the app prior to that date passing.
158158
159-
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.
159+
Add the following `AzureHelper` class to the server project. The `GetKeyVaultSecret` method retrieves a secret from a key vault. Adjust the namespace (`BlazorSample.Helpers`) to match your project namespace scheme.
160160

161161
`Helpers/AzureHelper.cs`:
162162

aspnetcore/blazor/security/webassembly/standalone-with-identity/account-confirmation-and-password-recovery.md

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,18 @@ Register the `AuthMessageSenderOptions` configuration instance in the server pro
5050
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);
5151
```
5252

53-
## Configure a user secret for the provider's security key
53+
## Configure a secret for the email provider's security key
54+
55+
Receive the email provider's security key from the provider and use it in the following guidance.
56+
57+
Use either or both of the following approaches to supply the secret to the app:
58+
59+
* [Secret Manager tool](#secret-manager-tool): The Secret Manager tool stores private data on the local machine and is only used during local development.
60+
* [Azure Key Vault](#azure-key-vault): You can store the secret in a key vault for use in any environment, including for the Development environment when working locally. Some developers prefer to use key vaults for staging and production deployments and use the [Secret Manager tool](#secret-manager-tool) for local development.
61+
62+
We strongly recommend that you avoid storing secrets in project code or configuration files. Use secure authentication flows, such as either or both of the approaches in this section.
63+
64+
## Secret Manager Tool
5465

5566
If the server project has already been initialized for the [Secret Manager tool](xref:security/app-secrets), it will already have a app secrets identifier (`<AppSecretsId>`) in its project file (`.csproj`). In Visual Studio, you can tell if the app secrets ID is present by looking at the **Properties** panel when the project is selected in **Solution Explorer**. If the app hasn't been initialized, execute the following command in a command shell opened to the server project's directory. In Visual Studio, you can use the Developer PowerShell command prompt (use the `cd` command to change the directory to the server project after you open the command shell).
5667

@@ -70,6 +81,102 @@ For more information, see <xref:security/app-secrets>.
7081

7182
[!INCLUDE[](~/blazor/security/includes/secure-authentication-flows.md)]
7283

84+
### Azure Key Vault
85+
86+
[Azure Key Vault](https://azure.microsoft.com/products/key-vault/) provides a safe approach for providing the app's client secret to the app.
87+
88+
To create a key vault and set a 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:
89+
90+
* Only the **Get** secret permission is required.
91+
* Select the application as the **Principal** for the secret.
92+
93+
Confirm in the Azure or Entra portal that the app has been granted access to the secret that you created for the email provider key.
94+
95+
> [!IMPORTANT]
96+
> A key vault secret is created with an expiration date. Be sure to track when a key vault secret is going to expire and create a new secret for the app prior to that date passing.
97+
98+
If Microsoft Identity packages aren't already part of the app's package registrations, add the following packages to the server project for Azure Identity and Azure Key Vault. These packages are transitively provided by Microsoft Identity Web packages, so you only need to add them if the app isn't referencing [`Microsoft.Identity.Web`](https://www.nuget.org/packages/Microsoft.Identity.Web):
99+
100+
* [`Azure.Identity`](https://www.nuget.org/packages/Azure.Identity)
101+
* [`Azure.Security.KeyVault.Secrets`](https://www.nuget.org/packages/Azure.Security.KeyVault.Secrets)
102+
103+
Add the following `AzureHelper` class to the server project. The `GetKeyVaultSecret` method retrieves a secret from a key vault. Adjust the namespace (`BlazorSample.Helpers`) to match your project namespace scheme.
104+
105+
`Helpers/AzureHelper.cs`:
106+
107+
```csharp
108+
using Azure.Identity;
109+
using Azure.Security.KeyVault.Secrets;
110+
111+
namespace BlazorSample.Helpers;
112+
113+
public static class AzureHelper
114+
{
115+
public static string GetKeyVaultSecret(string tenantId, string vaultUri, string secretName)
116+
{
117+
DefaultAzureCredentialOptions options = new()
118+
{
119+
// Specify the tenant ID to use the dev credentials when running the app locally
120+
// in Visual Studio.
121+
VisualStudioTenantId = tenantId,
122+
SharedTokenCacheTenantId = tenantId
123+
};
124+
125+
var client = new SecretClient(new Uri(vaultUri), new DefaultAzureCredential(options));
126+
var secret = client.GetSecretAsync(secretName).Result;
127+
128+
return secret.Value.Value;
129+
}
130+
}
131+
```
132+
133+
Where services are registered in the server project's `Program` file, obtain and bind the secret with [Options configuration](xref:fundamentals/configuration/options):
134+
135+
```csharp
136+
var tenantId = builder.Configuration.GetValue<string>("AzureAd:TenantId")!;
137+
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
138+
139+
var emailAuthKey = AzureHelper.GetKeyVaultSecret(
140+
tenantId, vaultUri, "EmailAuthKey");
141+
142+
var authMessageSenderOptions =
143+
new AuthMessageSenderOptions() { EmailAuthKey = emailAuthKey };
144+
builder.Configuration.GetSection(authMessageSenderOptions.EmailAuthKey)
145+
.Bind(authMessageSenderOptions);
146+
```
147+
148+
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:
149+
150+
```csharp
151+
if (!context.HostingEnvironment.IsDevelopment())
152+
{
153+
...
154+
}
155+
```
156+
157+
In the `AzureAd` section, which you may need to add if it isn't already present, of `appsettings.json` in the server project, add the following `TenantId` and `VaultUri` configuration keys and values, if they aren't already present:
158+
159+
```json
160+
"AzureAd": {
161+
"TenantId": "{TENANT ID}",
162+
"VaultUri": "{VAULT URI}"
163+
}
164+
```
165+
166+
In the preceding example:
167+
168+
* The `{TENANT ID}` placeholder is the app's tenant ID in Azure.
169+
* The `{VAULT URI}` placeholder is the key vault URI. Include the trailing slash on the URI.
170+
171+
Example:
172+
173+
```json
174+
"TenantId": "00001111-aaaa-2222-bbbb-3333cccc4444",
175+
"VaultUri": "https://contoso.vault.azure.net/"
176+
```
177+
178+
Configuration is used to facilitate supplying dedicated key vaults and secret names based on the app's environmental configuration files. For example, you can supply different configuration 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>.
179+
73180
## Implement `IEmailSender` in the server project
74181

75182
The following example is based on Mailchimp's Transactional API using [Mandrill.net](https://www.nuget.org/packages/Mandrill.net). For a different provider, refer to their documentation on how to implement sending an email message.
@@ -198,7 +305,7 @@ Enabling account confirmation on a site with users locks out all the existing us
198305
Password recovery requires the server app to adopt an email provider in order to send password reset codes to users. Therefore, follow the guidance earlier in this article to adopt an email provider:
199306

200307
* [Select and configure an email provider for the server project](#select-and-configure-an-email-provider-for-the-server-project)
201-
* [Configure a user secret for the provider's security key](#configure-a-user-secret-for-the-providers-security-key)
308+
* [Configure a secret for the email provider's security key](#configure-a-secret-for-the-email-providers-security-key)
202309
* [Implement `IEmailSender` in the server project](#implement-iemailsender-in-the-server-project)
203310

204311
Password recovery is a two-step process:

0 commit comments

Comments
 (0)