Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0a3163c
Add Bitwarden Secrets Manager integration
sliekens May 19, 2026
9b0c46e
Add missing launch config to bitwarden example
sliekens May 19, 2026
4217d85
Convert bitwarden project name to required parameter
sliekens May 19, 2026
c31a2d3
Remove unused internal project name tracking
sliekens May 19, 2026
a18b6f5
Convert fake async to plain synchronous helper
sliekens May 19, 2026
7f137fb
Collapse the public overload matrix to one internal representation
sliekens May 19, 2026
396fb37
Add test for same-name AddSecret/GetSecret
sliekens May 19, 2026
26811a1
Expand sample with secret retrieval
sliekens May 19, 2026
bbdc191
Add ssl cert config for bitwarden sdk
sliekens May 19, 2026
68a3c39
Add first-class secret projection API
sliekens May 20, 2026
f9acaeb
Remove unused setter
sliekens May 20, 2026
a413b58
Add missing null/empty/whitespace guards
sliekens May 20, 2026
4f9e332
Make secret name matching case insensitive
sliekens May 20, 2026
6296bca
Skip Secrets.Update() for unchanged secrets
sliekens May 20, 2026
719eab7
Add missing logging
sliekens May 21, 2026
0c0922d
Make auth cache actually work and improve example
sliekens May 21, 2026
2d0d446
Replace manifest with pipeline steps
sliekens May 23, 2026
d736afe
Add compose env to example
sliekens May 23, 2026
4355662
Improve apphost state handling with IAspireStore
sliekens May 23, 2026
e23f171
Pin ssl cert dir in run mode
sliekens May 23, 2026
96bc17a
Ignore transient errors
sliekens May 23, 2026
e19d842
Fix argument passing
sliekens May 23, 2026
f1f0135
Configure auth state file as parameter
sliekens May 23, 2026
a35b6a3
Reorganize types into logical groups
sliekens May 24, 2026
8749577
Fix redundant parameter prompting
sliekens May 24, 2026
2394a6a
Reorganize auth and project cache files
sliekens May 24, 2026
71d0aad
Add env patch for resolved secrets
sliekens May 24, 2026
c9c03f0
Split deployment pipeline by concern
sliekens May 24, 2026
76faa88
Update bitwarden docs
sliekens May 25, 2026
6727c26
Disable deployed dashboard for example
sliekens May 25, 2026
dabdc0b
Make deployed example endpoints external
sliekens May 25, 2026
a4b8580
Merge branch 'main' into bitwarden-secret-manager-integration
aaronpowell May 25, 2026
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
8 changes: 8 additions & 0 deletions CommunityToolkit.Aspire.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
<Folder Name="/examples/azure-ext/">
<Project Path="examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost/CommunityToolkit.Aspire.Azure.Extensions.AppHost.csproj" />
</Folder>
<Folder Name="/examples/bitwarden-secret-manager/">
<Project Path="examples/bitwarden-secret-manager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.ApiService/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.ApiService.csproj" />
<Project Path="examples/bitwarden-secret-manager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.AppHost/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.AppHost.csproj" />
</Folder>
<Folder Name="/examples/bun/">
<Project Path="examples/bun/CommunityToolkit.Aspire.Hosting.Bun.AppHost/CommunityToolkit.Aspire.Hosting.Bun.AppHost.csproj" />
</Folder>
Expand Down Expand Up @@ -199,11 +203,13 @@
<Project Path="examples\zitadel\CommunityToolkit.Aspire.Hosting.Zitadel.AppHost\CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.csproj" />
</Folder>
<Folder Name="/src/">
<Project Path="src/CommunityToolkit.Aspire.Bitwarden.SecretManager/CommunityToolkit.Aspire.Bitwarden.SecretManager.csproj" />
<Project Path="src/CommunityToolkit.Aspire.GoFeatureFlag/CommunityToolkit.Aspire.GoFeatureFlag.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.ActiveMQ/CommunityToolkit.Aspire.Hosting.ActiveMQ.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Adminer/CommunityToolkit.Aspire.Hosting.Adminer.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/CommunityToolkit.Aspire.Hosting.Azure.Extensions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Bun/CommunityToolkit.Aspire.Hosting.Bun.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.DbGate/CommunityToolkit.Aspire.Hosting.DbGate.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Deno/CommunityToolkit.Aspire.Hosting.Deno.csproj" />
Expand Down Expand Up @@ -261,11 +267,13 @@
<Project Path="src/CommunityToolkit.Aspire.Hosting.Dapr/CommunityToolkit.Aspire.Hosting.Dapr.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/CommunityToolkit.Aspire.Bitwarden.SecretManager.Tests/CommunityToolkit.Aspire.Bitwarden.SecretManager.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.GoFeatureFlag.Tests/CommunityToolkit.Aspire.GoFeatureFlag.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.Tests/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Bun.Tests/CommunityToolkit.Aspire.Hosting.Bun.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.DbGate.Tests/CommunityToolkit.Aspire.Hosting.DbGate.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Deno.Tests/CommunityToolkit.Aspire.Hosting.Deno.Tests.csproj" />
Expand Down
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<ItemGroup Label="Aspire Packages">
<!-- Aspire packages -->
<PackageVersion Include="Aspire.Hosting" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Docker" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Azure.Storage" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Dapr" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Azure.AppContainers" Version="$(AspireVersion)" />
Expand Down Expand Up @@ -67,6 +68,7 @@
<ItemGroup Label="Integration Packages">
<!-- External packages -->
<PackageVersion Include="Azure.Provisioning.AppContainers" Version="1.2.0" />
<PackageVersion Include="Bitwarden.Secrets.Sdk" Version="1.0.0" />
<PackageVersion Include="JsonSchema.Net" Version="7.4.0" />
<PackageVersion Include="OllamaSharp" Version="5.4.12" />
<PackageVersion Include="OpenFeature.Providers.GOFeatureFlag" Version="1.0.0" />
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ This repository contains the source code for the Aspire Community Toolkit, a col
| - **Learn More**: [`Hosting.SqlDatabaseProjects`][sql-database-projects-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects][sql-database-projects-shields]][sql-database-projects-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects][sql-database-projects-shields-preview]][sql-database-projects-nuget-preview] | A hosting integration for the SQL Databases Projects. |
| - **Learn More**: [`Hosting.Rust`][rust-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Rust][rust-shields]][rust-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Rust][rust-shields-preview]][rust-nuget-preview] | A hosting integration for the Rust apps. |
| - **Learn More**: [`Hosting.Bun`][bun-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Bun][bun-shields]][bun-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Bun][bun-shields-preview]][bun-nuget-preview] | A hosting integration for the Bun apps. |
| - **Learn More**: [`Hosting.Bitwarden.SecretManager`][bitwarden-secret-manager-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager][bitwarden-secret-manager-hosting-shields]][bitwarden-secret-manager-hosting-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager][bitwarden-secret-manager-hosting-shields-preview]][bitwarden-secret-manager-hosting-nuget-preview] | A hosting integration for Bitwarden Secrets Manager projects and managed secrets. |
| - **Learn More**: [`Bitwarden.SecretManager`][bitwarden-secret-manager-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Bitwarden.SecretManager][bitwarden-secret-manager-client-shields]][bitwarden-secret-manager-client-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Bitwarden.SecretManager][bitwarden-secret-manager-client-shields-preview]][bitwarden-secret-manager-client-nuget-preview] | A client integration for authenticating and using the Bitwarden Secrets Manager SDK from Aspire applications. |
| - **Learn More**: [`Hosting.Perl`][perl-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Perl][perl-shields]][perl-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Perl][perl-shields-preview]][perl-nuget-preview] | A hosting integration for Perl scripts and APIs. |
| - **Learn More**: [`Hosting.Python.Extensions`][python-ext-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Python.Extensions][python-ext-shields]][python-ext-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Python.Extensions][python-ext-shields-preview]][python-ext-nuget-preview] | An integration that contains some additional extensions for running python applications |
| - **Learn More**: [`Hosting.KurrentDB`][kurrentdb-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.KurrentDB][kurrentdb-shields]][kurrentdb-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.KurrentDB][kurrentdb-shields-preview]][kurrentdb-nuget-preview] | An Aspire hosting integration leveraging the [KurrentDB](https://www.kurrent.io) container. |
Expand Down Expand Up @@ -154,6 +156,15 @@ This project is supported by the [.NET Foundation](https://dotnetfoundation.org)
[bun-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Bun/
[bun-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Bun?label=nuget%20(preview)
[bun-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Bun/absoluteLatest
[bitwarden-secret-manager-integration-docs]: https://learn.microsoft.com/dotnet/aspire/community-toolkit/hosting-bitwarden-secret-manager
[bitwarden-secret-manager-hosting-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager
[bitwarden-secret-manager-hosting-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/
[bitwarden-secret-manager-hosting-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager?label=nuget%20(preview)
[bitwarden-secret-manager-hosting-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager/absoluteLatest
[bitwarden-secret-manager-client-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Bitwarden.SecretManager
[bitwarden-secret-manager-client-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Bitwarden.SecretManager/
[bitwarden-secret-manager-client-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Bitwarden.SecretManager?label=nuget%20(preview)
[bitwarden-secret-manager-client-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Bitwarden.SecretManager/absoluteLatest
[perl-integration-docs]: https://learn.microsoft.com/dotnet/aspire/community-toolkit/hosting-perl
[perl-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.Perl
[perl-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Perl/
Expand Down
1 change: 1 addition & 0 deletions examples/bitwarden-secret-manager/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
aspire-output
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\CommunityToolkit.Aspire.Bitwarden.SecretManager\CommunityToolkit.Aspire.Bitwarden.SecretManager.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Bitwarden.Sdk;
using CommunityToolkit.Aspire.Bitwarden.SecretManager;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

// Register the Bitwarden secret manager client with Aspire configuration binding.
// Use the same connection name as the Bitwarden project in the AppHost
// Configuration is injected as Aspire:Bitwarden:SecretManager:{connection_name}:{setting}
// (e.g. Aspire:Bitwarden:SecretManager:secrets:AccessToken).
builder.AddBitwardenSecretManagerClient(connectionName: "secrets", settings =>
{
// You can optionally override Aspire injected values here or set additional client settings.
settings.IdentityUrl = "https://vault.bitwarden.com/identity";
settings.ApiUrl = "https://vault.bitwarden.com/api";
settings.DisableHealthChecks = true;
});

var app = builder.Build();

app.MapGet("/", ([FromQuery] string? apiKey, BitwardenClient client, BitwardenSecretManagerClientSettings settings, IConfiguration configuration) =>
{
Guid secretId = configuration.GetValue<Guid>("DEMO_API_KEY_SECRET_ID");
SecretResponse secret = client.Secrets.Get(secretId);
if (string.IsNullOrEmpty(apiKey))
{
return Results.Problem("Missing apiKey query parameter.", statusCode: StatusCodes.Status401Unauthorized);
}
else if (secret.Value != apiKey)
{
return Results.Problem("Invalid apiKey.", statusCode: StatusCodes.Status401Unauthorized);
}

return Results.Text("""
Access granted to protected resource!

But please don't use query parameters for API keys in real applications... this is just a demo!
Consider using an HTTP header or similar approach to keep secrets out of URLs and logs.
""");
});

app.MapGet("/health", () => Results.Ok(new { status = "ok" }));

app.Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5278",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7298;http://localhost:5278",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Aspire.AppHost.Sdk/13.3.0">

<PropertyGroup>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
<UserSecretsId>632cd204-f3fe-4c77-98e1-fa65b87b5fa9</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.Docker" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager\CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.csproj" IsAspireProjectResource="false" />
<ProjectReference Include="..\CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.ApiService\CommunityToolkit.Aspire.Hosting.Bitwarden.SecretManager.ApiService.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using Projects;

var builder = DistributedApplication.CreateBuilder(args);

builder.AddDockerComposeEnvironment("docker")
.WithDashboard(false);

var organizationId = builder.AddParameter("bitwarden-organization-id");
var projectName = builder.AddParameter("bitwarden-project-name");
var accessToken = builder.AddParameter("bitwarden-access-token", secret: true);
var demoApiKey = builder.AddParameter("demo-api-key", secret: true);

// Set up a secrets project within the specified organization using the provided management access token.
// The management token MUST have write permissions to the project if it already exists.
// If the project doesn't exist, it will be automatically created with write access for the provided token.
var bitwarden = builder.AddBitwardenSecretManager("secrets", projectName, organizationId, accessToken);

// Recommended: configure the Bitwarden client with a runtime access token that has fewer privileges than the management token.
bitwarden.WithRuntimeAccessToken(accessToken /* replace with least privilege token */);

// Optional: override the AppHost cache file location.
// This file stores the Bitwarden project ID and secret ID mappings between runs so the integration
// can reuse existing Bitwarden resources rather than creating duplicates.
// By default it is stored in the Aspire store (obj/.aspire/...). Override to share it across workspaces or CI pipelines.
bitwarden.WithCacheFile("demo.json");

// Optional: override the AppHost auth cache file location.
// By default it is stored in the Aspire store alongside the bookkeeping cache.
// Override to reuse a Bitwarden SDK auth session across CI runs or workspaces without re-authenticating.
bitwarden.WithAuthCacheFile(".apphost-auth-cache");

// Add a secret to the project with the value of the demo API key parameter.
// The secret is created or updated on each run. Use `GetSecret` if you only want to read an existing secret.
var demoApiKeySecret = bitwarden.AddSecret("demo-api-key", demoApiKey);

// Register an API service that references the Bitwarden secret manager
// There are two ways to reference secrets from the Bitwarden secret manager in Aspire.
var api = builder.AddProject<CommunityToolkit_Aspire_Hosting_Bitwarden_SecretManager_ApiService>("api")
.WithHttpHealthCheck("/health")
.WithExternalHttpEndpoints();

// 1. Using the secret manager client in code, which allows you to retrieve secrets at runtime and
// supports dynamic secret retrieval without redeploying the application when secrets change.
// (See ApiService/Program.cs for an example of retrieving secrets from the client in code.)
api.WithReference(bitwarden).WithBitwardenSecretId("DEMO_API_KEY_SECRET_ID", demoApiKeySecret.Resource);

// Optional: persist the app's Bitwarden SDK auth session across restarts so it does not re-authenticate on every startup.
// In run mode a fixed local path is fine; in deployed environments use a parameter so each
// environment can point to a durable storage location (e.g. a mounted volume).
// In deployed environments, set Parameters__bitwarden-auth-cache-location to a persistent path, e.g. /data/bitwarden/auth-cache.
if (builder.ExecutionContext.IsRunMode)
{
string apiProjectDir = Path.GetDirectoryName(api.Resource.GetProjectMetadata().ProjectPath)!;
string authCachePath = Path.Combine(apiProjectDir, "obj", ".app-auth-cache");
api.WithAuthCacheFile(bitwarden, authCachePath);
}
else if (builder.ExecutionContext.IsPublishMode)
{
api.WithAuthCacheFile(bitwarden, builder.AddParameter("app-auth-cache-location"));
}

// 2. Using direct secret references in the project configuration, which injects the secret value as an environment variable at runtime.
// This approach is simpler (no Bitwarden code in the application) but requires redeploying the application whenever the secret value changes.
api.WithBitwardenSecretValue("DEMO_API_KEY", demoApiKeySecret.Resource);

// Work around Linux trust-store discovery issues in Bitwarden.Secrets.Sdk 1.0.0.
if (builder.ExecutionContext.IsPublishMode || (builder.ExecutionContext.IsRunMode && OperatingSystem.IsLinux()))
{
api.WithEnvironment("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt")
.WithEnvironment("SSL_CERT_DIR", "/etc/ssl/certs");

if (builder.ExecutionContext.IsRunMode && OperatingSystem.IsLinux())
{
Environment.SetEnvironmentVariable("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt");
Environment.SetEnvironmentVariable("SSL_CERT_DIR", "/etc/ssl/certs");
}
}

builder.Build().Run();
Loading