Skip to main content

Bitwarden Setup for dfe-fetcher

What a Bitwarden organization owner needs to configure so dfe-fetcher can read organization event logs from the Bitwarden Public API.

Overview​

dfe-fetcher pulls organization event logs from the Bitwarden Public API at /public/events - member, collection, group, vault, and policy actions across the organization. The endpoint is poll-based (Bitwarden offers no outbound webhooks). dfe-fetcher authenticates with the organization API key using the OAuth2 client_credentials flow: it exchanges client_id + client_secret (scope api.organization) at /identity/connect/token for a bearer token, then GETs /public/events with a start/end window and follows the continuationToken for pagination.

The source is the shipped bitwarden profile (crates/fetcher/profiles/bitwarden.yaml); the sources.bitwarden block below maps onto an instance of it at load. The token is cached per instance and refreshed shortly before it expires; a 429 or 5xx is retried with backoff (honouring Retry-After), a refused exchange or a 401 or 403 ends the tick, and a tick that fails does not advance the fetch window. A missing client id or secret is refused at load, naming sources.bitwarden.

Prerequisites​

  • A Bitwarden Teams or Enterprise organization (the Public API is available to Teams and Enterprise plans, not Free or Families).
  • The setup must be done by an organization Owner - only an owner can view or rotate the organization API key.
  • Works on Bitwarden Cloud (US or EU) and self-hosted servers.
  • No paid add-on beyond the Teams/Enterprise subscription.

Required Permissions​

ServiceObject/EndpointPermission or ScopeNotes
Identity/identity/connect/tokengrant_type client_credentials, scope api.organizationToken lives ~60 min; dfe-fetcher caches and refreshes it
Public API/public/eventsOrganization API keyRead-only event log; GET with start/end/continuationToken

The credential is the organization API key, whose client_id has the form organization.<uuid>. A personal API key (user.<uuid>) will not work against the Public API. Access is read-only.

Source-Side Setup​

  1. Retrieve the organization API key

    In the Bitwarden web vault, open the Admin Console for the organization. Go to Settings -> Organization info and scroll to the API key section, then select View API Key (you may be prompted to re-enter your master password). Copy the client_id (format organization.<uuid>) and client_secret. To rotate, use Rotate API Key on the same screen - this invalidates the old secret.

  2. Confirm event logging is on

    Event logs are an organization feature; ensure events are enabled for the organization (Admin Console -> Settings). If /public/events returns 403, verify the account permissions and that the Events feature is enabled.

  3. Identify your region / host

    DeploymentAPI base (api_url_override)Identity/token (identity_url_override)
    Cloud US (default)https://api.bitwarden.comhttps://identity.bitwarden.com/connect/token
    Cloud EUhttps://api.bitwarden.euhttps://identity.bitwarden.eu/connect/token
    Self-hostedhttps://your-host/apihttps://your-host/identity/connect/token

    US cloud uses the built-in defaults, so no overrides are needed there.

  4. Smoke-test the token exchange

    curl -s https://identity.bitwarden.com/connect/token \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d grant_type=client_credentials \
    -d "client_id=organization.<uuid>" \
    -d "client_secret=<secret>" \
    -d scope=api.organization

    A successful response contains access_token and expires_in (typically 3600). Use that bearer token against https://api.bitwarden.com/public/events to confirm event access.

dfe-fetcher Configuration​

Config File​

sources:
bitwarden:
enabled: true
client_id: "organization.<uuid>"
client_secret: "your-organization-api-client-secret"
# Cloud EU or self-hosted only:
# api_url_override: "https://api.bitwarden.eu"
# identity_url_override: "https://identity.bitwarden.eu/connect/token"
services:
- name: events
topic: "bitwarden"
# filter: 'type < 1500' # optional, hot-reloaded

Environment Variables​

DFE_FETCHER_SOURCES__BITWARDEN__ENABLED="true"
DFE_FETCHER_SOURCES__BITWARDEN__CLIENT_ID="organization.<uuid>"
DFE_FETCHER_SOURCES__BITWARDEN__CLIENT_SECRET="your-organization-api-client-secret"
# Cloud EU or self-hosted only:
DFE_FETCHER_SOURCES__BITWARDEN__API_URL_OVERRIDE="https://api.bitwarden.eu"
DFE_FETCHER_SOURCES__BITWARDEN__IDENTITY_URL_OVERRIDE="https://identity.bitwarden.eu/connect/token"

Secrets Manager​

Keep the client secret out of the config file:

sources:
bitwarden:
enabled: true
client_id: "organization.<uuid>"
credential_secret: "vault:kv/data/bitwarden:client_secret"
services:
- name: events
topic: "bitwarden"

credential_secret takes precedence over a literal client_secret and resolves to the secret string. client_id is always supplied in clear (it is not sensitive). The only service is events; it has no service-side config keys.

Verification​

The health check is the OAuth2 token exchange: a minted token proves the client, and no data endpoint is touched. A refused exchange is a health error carrying the response.

Smoke tests against a live Bitwarden organisation live in crates/fetcher/tests/e2e/smoke_remote.rs and are #[ignore]d by default. They read BITWARDEN_CLIENT_ID and BITWARDEN_CLIENT_SECRET (required), plus BITWARDEN_API_BASE and BITWARDEN_IDENTITY_URL (optional region/self-host overrides), from .env-cloud (or .env):

cargo test -p dfe-fetcher --test e2e bitwarden_ -- --ignored

Common failure modes:

  • 401 on token exchange / health check fails - wrong client_id or client_secret, a rotated secret, or a personal (user.<uuid>) key.
  • 403 on /public/events - account lacks permission or event logging is not enabled for the organization.
  • Wrong region - EU tenant left on the US default; set both api_url_override and identity_url_override.
  • HTTP 429 - rate limited; dfe-fetcher retries with backoff and Retry-After up to the profile's policy, then fails the tick without advancing; the page ceiling is 50 per fetch.

Cost​

dfe-fetcher only reads. The Public API and /public/events reads add no charge of their own, but access may require a paid Bitwarden plan (the Events/Public API is an organization feature). Fair-use server-side rate limits apply. Confirm any cost implications against your own Bitwarden plan.

References​