Bitwarden Secrets Manager as a secret provider via an operator-installed bws client

This commit is contained in:
jarek committed 2026-08-17 09:50:34 +02:00
1 parent ff006965ce
commit 95fd26656b
6 files changed
+59 -10

No files matched your search

@@ -0,0 +1,7 @@
<script lang="ts">
let { class: className = 'w-4 h-4' }: { class?: string } = $props();
</script>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" class={className} fill="currentColor">
<path d="M39.589,4.608h-16.5H8.214c-0.55,0-0.997,0.444-1,0.994L7.089,24.108 c0,12.064,13.105,18.938,15.686,19.792h0c0,0,0.001,0,0.001,0c0.339,0.111,0.536,0.168,0.536,0.168 c0.09,0.026,0.184,0.039,0.277,0.039h0.5c0.131,0,0.26-0.025,0.38-0.075c0.658-0.271,16.12-6.781,16.12-19.925v-18.5 C40.589,5.056,40.142,4.608,39.589,4.608z M38.589,24.108c0,10.724-12.173,16.836-14.5,17.906V6.608h14.5V24.108z" />
</svg>
@@ -4,6 +4,8 @@ import OnePassword from './OnePassword.svelte';
import Infisical from './Infisical.svelte';
import HashiCorpVault from './HashiCorpVault.svelte';
import Doppler from './Doppler.svelte';
import Bitwarden from './Bitwarden.svelte';
import ProtonPass from './ProtonPass.svelte';
/** Brand icon for a secret-provider type. Falls back to a generic key icon. */
export function getProviderIcon(type: string): Component {
@@ -17,6 +19,10 @@ export function getProviderIcon(type: string): Component {
return HashiCorpVault;
case 'doppler':
return Doppler;
case 'bitwarden':
return Bitwarden;
case 'proton':
return ProtonPass;
default:
return KeyRound; // any future provider until it has a brand icon
}
+4 -5
View File
@@ -202,11 +202,10 @@ async function runBws(args: string[], limits: CommandLimits): Promise<Buffer> {
}
return await executeBws(stateDir, args, limits);
} finally {
try {
await rm(stateDir, { recursive: true, force: true });
} catch {
throw new BwsAdapterError('Bitwarden bws temporary state could not be removed');
}
// Swallow a cleanup failure: throwing here would mask the real error on the
// failure path, and turn a successful pull into a thrown error on the happy
// path. rm --force rarely fails; a stray temp dir is harmless residue.
await rm(stateDir, { recursive: true, force: true }).catch(() => undefined);
}
}
+3 -1
View File
@@ -19,6 +19,7 @@ import { infisicalProvider } from './infisical';
import { vaultProvider } from './vault';
import { dopplerProvider } from './doppler';
import { bitwardenProvider } from './bitwarden';
import { protonProvider } from './proton';
// Registered providers. Adding a new backend means dropping a file in this
// directory and registering it here; each implements the SecretProvider
@@ -29,7 +30,8 @@ const providers: Record<string, SecretProvider> = {
[infisicalProvider.type]: infisicalProvider as SecretProvider,
[vaultProvider.type]: vaultProvider as SecretProvider,
[dopplerProvider.type]: dopplerProvider as SecretProvider,
[bitwardenProvider.type]: bitwardenProvider as SecretProvider
[bitwardenProvider.type]: bitwardenProvider as SecretProvider,
[protonProvider.type]: protonProvider as SecretProvider
};
/** Returns the provider for a stored type, or undefined if unknown. */
+19 -3
View File
@@ -32,6 +32,7 @@ export type SecretProviderType =
| 'vault'
| 'doppler'
| 'bitwarden'
| 'proton'
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
| (string & {});
@@ -137,7 +138,12 @@ export interface ConnectConfig {
*/
export interface InfisicalConfig {
host: string;
token: string;
/** Static service/access token. Supply this OR clientId+clientSecret (Universal Auth). */
token?: string;
/** Universal Auth (Machine Identity) client ID. Paired with clientSecret. */
clientId?: string;
/** Universal Auth (Machine Identity) client secret. Paired with clientId. */
clientSecret?: string;
projectId: string;
environment?: string;
path?: string;
@@ -177,6 +183,15 @@ export interface BitwardenConfig {
serverUrl?: string;
}
/**
* Proton Pass: a Personal Access Token (`pst_...::...`) authenticates an
* operator-installed pass-cli client. Bulk pulls one vault (selected by name);
* inline `pass://SHARE_ID/ITEM_ID[/FIELD]` references resolve a single field.
*/
export interface ProtonConfig {
token: string;
}
/** Persisted (encrypted) config, discriminated by the provider `type`. */
export type SecretProviderConfig =
| ServiceAccountConfig
@@ -184,7 +199,8 @@ export type SecretProviderConfig =
| InfisicalConfig
| VaultConfig
| DopplerConfig
| BitwardenConfig;
| BitwardenConfig
| ProtonConfig;
/**
* Config keys that hold a SECRET across every provider type. Only these are stripped
@@ -193,7 +209,7 @@ export type SecretProviderConfig =
* a non-secret coordinate the user needs to see and edit. Keep in sync with the
* `type: 'password'` fields in ProviderModal.svelte's PROVIDER_FIELDS.
*/
export const SECRET_CONFIG_KEYS = new Set(['token']);
export const SECRET_CONFIG_KEYS = new Set(['token', 'clientSecret']);
/**
* Merges an incoming (edit-form) config OVER the stored one for a write/test: the incoming