-
Notifications
You must be signed in to change notification settings - Fork 371
feat: Implement Dapr.SecretsManagement as a purpose-specific client #1794
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
b75f51b
8514d77
ff244d8
347e0c8
f0384da
e6ce283
579833f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # Dapr Secrets Management Sample | ||
|
|
||
| This sample demonstrates how to use the Dapr Secrets Management SDK to retrieve secrets from Dapr secret store components. | ||
|
|
||
| ## Features Demonstrated | ||
|
|
||
| 1. **Direct secret retrieval** — Using `DaprSecretsManagementClient` to fetch individual or bulk secrets via gRPC. | ||
| 2. **Typed secret stores** — Using the `[SecretStore]` and `[Secret]` attributes with the source generator to create strongly-typed secret accessors. | ||
| 3. **Dependency injection** — Registering the secrets client and typed stores via `IServiceCollection` extensions. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) | ||
| - [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) | ||
| - A configured Dapr secret store component (e.g., local file, Kubernetes secrets, Azure Key Vault) | ||
|
|
||
| ## Running the Sample | ||
|
|
||
| ```bash | ||
| dapr run --app-id secret-sample --app-port 5234 -- dotnet run | ||
| ``` | ||
|
|
||
| ## Endpoints | ||
|
|
||
| | Method | Path | Description | | ||
| |--------|------|-------------| | ||
| | GET | `/secrets/{storeName}/{key}` | Retrieve a single secret by key | | ||
| | GET | `/secrets/{storeName}` | Retrieve all secrets from a store | | ||
|
|
||
| ## NuGet Package Note | ||
|
|
||
| When consuming from NuGet, install the single **`Dapr.SecretsManagement`** package. The sub-projects (`Abstractions`, `Runtime`, `Generators`) are bundled into this one package and are not published individually. | ||
|
|
||
| ```xml | ||
| <PackageReference Include="Dapr.SecretsManagement" Version="<version>" /> | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| // ------------------------------------------------------------------------ | ||
| // Copyright 2026 The Dapr Authors | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| // ------------------------------------------------------------------------ | ||
|
|
||
| using Dapr.SecretsManagement.Abstractions; | ||
|
|
||
| namespace SecretManagementSample; | ||
|
|
||
| /// <summary> | ||
| /// Example of a typed secret store interface. Apply the <see cref="SecretStoreAttribute"/> to an interface | ||
| /// and the Dapr Secrets Management source generator will produce: | ||
| /// 1. A concrete implementation that caches secrets loaded at startup. | ||
| /// 2. A DI registration extension method (e.g., <c>AddMyVaultSecrets()</c>). | ||
| /// | ||
| /// Properties without <see cref="SecretAttribute"/> use the property name as the secret key. | ||
| /// Properties with <see cref="SecretAttribute"/> use the specified secret name. | ||
| /// </summary> | ||
| [SecretStore("my-vault")] | ||
| public partial interface IMyVaultSecrets | ||
| { | ||
| /// <summary> | ||
| /// The database connection string, retrieved from the "db-connection-string" secret key. | ||
| /// </summary> | ||
| [Secret("db-connection-string")] | ||
| string DatabaseConnection { get; } | ||
|
|
||
| /// <summary> | ||
| /// The API key. Uses the property name "ApiKey" as the secret key. | ||
| /// </summary> | ||
| string ApiKey { get; } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| // ------------------------------------------------------------------------ | ||
| // Copyright 2026 The Dapr Authors | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| // ------------------------------------------------------------------------ | ||
|
|
||
| using Dapr.SecretsManagement; | ||
| using Dapr.SecretsManagement.Extensions; | ||
|
|
||
| var builder = WebApplication.CreateBuilder(args); | ||
|
|
||
| // Register the Dapr Secrets Management client with dependency injection. | ||
| // This makes DaprSecretsManagementClient available throughout the application. | ||
| builder.Services.AddDaprSecretsManagementClient(); | ||
|
|
||
| // If you've defined a typed secret store interface with [SecretStore] and the source generator, | ||
| // you can register it here. For example: | ||
| // | ||
| // builder.Services.AddDaprSecretsManagementClient() | ||
| // .AddMyVaultSecrets(); // Generated extension method | ||
| // | ||
| // See IMyVaultSecrets.cs for the typed secret store interface definition. | ||
|
|
||
| var app = builder.Build(); | ||
|
|
||
| // --- Example 1: Direct secret retrieval --- | ||
| app.MapGet("/secrets/{storeName}/{key}", async ( | ||
| string storeName, | ||
| string key, | ||
| DaprSecretsManagementClient secretsClient, | ||
| CancellationToken cancellationToken) => | ||
| { | ||
| var secret = await secretsClient.GetSecretAsync(storeName, key, cancellationToken: cancellationToken); | ||
| return Results.Ok(secret); | ||
| }); | ||
|
|
||
| // --- Example 2: Bulk secret retrieval --- | ||
| app.MapGet("/secrets/{storeName}", async ( | ||
| string storeName, | ||
| DaprSecretsManagementClient secretsClient, | ||
| CancellationToken cancellationToken) => | ||
| { | ||
| var secrets = await secretsClient.GetBulkSecretAsync(storeName, cancellationToken: cancellationToken); | ||
| return Results.Ok(secrets); | ||
| }); | ||
|
|
||
| // --- Example 3: Using typed secret store (source-generated) --- | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is example 3 commented out?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uncommented and fully wired up — the example now registers |
||
| // If you have registered a typed secret store (see IMyVaultSecrets.cs), you can inject it directly: | ||
| // | ||
| // app.MapGet("/typed-secrets", (IMyVaultSecrets secrets) => | ||
| // { | ||
| // return Results.Ok(new | ||
| // { | ||
| // DatabaseConnection = secrets.DatabaseConnection, | ||
| // ApiKey = secrets.ApiKey | ||
| // }); | ||
| // }); | ||
|
|
||
| app.Run(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "profiles": { | ||
| "SecretManagementSample": { | ||
| "commandName": "Project", | ||
| "dotnetRunMessages": true, | ||
| "launchBrowser": false, | ||
| "applicationUrl": "http://localhost:5234", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you ensure that the port is consistent across other examples? I know they're all over the place today, but I'm ok standardizing on something easy like 6543 so all the READMEs can use a consistent port across all examples.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Standardized to port 6543 in both |
||
| "environmentVariables": { | ||
| "ASPNETCORE_ENVIRONMENT": "Development" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net10.0</TargetFramework> | ||
| <Nullable>enable</Nullable> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
|
|
||
| <!-- Added for demonstration purposes - emit generated source files to disk for inspection --> | ||
| <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> | ||
| <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)Generated</CompilerGeneratedFilesOutputPath> | ||
| </PropertyGroup> | ||
|
|
||
| <!-- | ||
| NOTE: When consuming from NuGet, use the single 'Dapr.SecretsManagement' package instead of | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perfect |
||
| these individual project references. The sub-projects (Abstractions, Runtime, Generators) are | ||
| NOT published to NuGet individually — they are bundled into the Dapr.SecretsManagement package. | ||
|
|
||
| Replace the ProjectReference items below with: | ||
| <PackageReference Include="Dapr.SecretsManagement" Version="<version>" /> | ||
| --> | ||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\..\src\Dapr.SecretsManagement.Abstractions\Dapr.SecretsManagement.Abstractions.csproj" /> | ||
| <ProjectReference Include="..\..\..\src\Dapr.SecretsManagement.Runtime\Dapr.SecretsManagement.Runtime.csproj" /> | ||
| <ProjectReference Include="..\..\..\src\Dapr.SecretsManagement.Generators\Dapr.SecretsManagement.Generators.csproj" | ||
| OutputItemType="Analyzer" | ||
| ReferenceOutputAssembly="false" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| </PropertyGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // ------------------------------------------------------------------------ | ||
| // Copyright 2026 The Dapr Authors | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| // ------------------------------------------------------------------------ | ||
|
|
||
| namespace Dapr.SecretsManagement.Abstractions; | ||
|
|
||
| /// <summary> | ||
| /// Specifies the secret key name used when retrieving the value of the annotated property from a Dapr | ||
| /// secret store. When this attribute is omitted, the property name is used as the secret key. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This attribute should be applied to properties on an interface that is also annotated with | ||
| /// <see cref="SecretStoreAttribute"/>. The source generator uses this metadata to map each property | ||
| /// to the correct secret key during bulk secret retrieval. | ||
| /// </remarks> | ||
| /// <example> | ||
| /// <code> | ||
| /// [SecretStore("my-vault")] | ||
| /// public partial interface IMySecrets | ||
| /// { | ||
| /// [Secret("database-connection-string")] | ||
| /// string DbConnection { get; } | ||
| /// } | ||
| /// </code> | ||
| /// </example> | ||
| [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] | ||
| public sealed class SecretAttribute : Attribute | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="SecretAttribute"/> class. | ||
| /// </summary> | ||
| /// <param name="secretName"> | ||
| /// The name of the secret key in the Dapr secret store. This value is used when calling the | ||
| /// Dapr Secrets API to retrieve the secret value. | ||
| /// </param> | ||
| /// <exception cref="ArgumentNullException">Thrown when <paramref name="secretName"/> is <see langword="null"/>.</exception> | ||
| public SecretAttribute(string secretName) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(secretName); | ||
| SecretName = secretName; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the name of the secret key in the Dapr secret store. | ||
| /// </summary> | ||
| public string SecretName { get; } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // ------------------------------------------------------------------------ | ||
| // Copyright 2026 The Dapr Authors | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| // ------------------------------------------------------------------------ | ||
|
|
||
| namespace Dapr.SecretsManagement.Abstractions; | ||
|
|
||
| /// <summary> | ||
| /// Marks an interface as a typed accessor for a Dapr secret store. When applied to a <c>partial interface</c>, | ||
| /// the Dapr Secrets Management source generator will produce a concrete implementation that retrieves secrets | ||
| /// from the specified Dapr secret store component and registers it in the dependency injection container. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// The interface should declare <see langword="string"/>-typed read-only properties. Each property maps to a | ||
| /// single secret key in the store. The key name defaults to the property name but can be overridden with | ||
| /// <see cref="SecretAttribute"/>. | ||
| /// </para> | ||
| /// <para> | ||
| /// Generated implementations load all mapped secrets in bulk at startup via an <c>IHostedService</c> and | ||
| /// expose them as synchronous properties. This makes secrets available immediately after host startup without | ||
| /// requiring callers to manage async flows. | ||
| /// </para> | ||
| /// <example> | ||
| /// <code> | ||
| /// [SecretStore("my-vault")] | ||
| /// public partial interface IMySecrets | ||
| /// { | ||
| /// /// <summary>The database connection string.</summary> | ||
| /// [Secret("db-connection-string")] | ||
| /// string DatabaseConnection { get; } | ||
| /// | ||
| /// /// <summary>The API key (uses property name as secret key).</summary> | ||
| /// string ApiKey { get; } | ||
| /// } | ||
| /// </code> | ||
| /// </example> | ||
| /// </remarks> | ||
| [AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] | ||
| public sealed class SecretStoreAttribute : Attribute | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="SecretStoreAttribute"/> class. | ||
| /// </summary> | ||
| /// <param name="storeName"> | ||
| /// The name of the Dapr secret store component to retrieve secrets from. This must match the | ||
| /// <c>metadata.name</c> of a configured Dapr secret store component. | ||
| /// </param> | ||
| /// <exception cref="ArgumentNullException">Thrown when <paramref name="storeName"/> is <see langword="null"/>.</exception> | ||
| public SecretStoreAttribute(string storeName) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(storeName); | ||
| StoreName = storeName; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the name of the Dapr secret store component that secrets will be retrieved from. | ||
| /// </summary> | ||
| public string StoreName { get; } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // ------------------------------------------------------------------------ | ||
| // Copyright 2026 The Dapr Authors | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| // ------------------------------------------------------------------------ | ||
|
|
||
| namespace Dapr.SecretsManagement.Abstractions; | ||
|
|
||
| /// <summary> | ||
| /// Well-known names and constants related to Dapr Secrets Management. | ||
| /// </summary> | ||
| public static class WellKnownSecrets | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should mark as many things internal and use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Made |
||
| { | ||
| /// <summary> | ||
| /// The fully qualified name of <see cref="SecretStoreAttribute"/>. | ||
| /// </summary> | ||
| public const string SecretStoreAttributeFullName = "Dapr.SecretsManagement.Abstractions.SecretStoreAttribute"; | ||
|
|
||
| /// <summary> | ||
| /// The fully qualified name of <see cref="SecretAttribute"/>. | ||
| /// </summary> | ||
| public const string SecretAttributeFullName = "Dapr.SecretsManagement.Abstractions.SecretAttribute"; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember to use the standardized port here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to 6543 in ff244d8.