* Update GHA and pre-commit
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update admin diagnostics
Added a check if the templates are overridden and return which specific folder, `admin`, `email` or `scss`.
This way we could more quickly point users to possible outdated templates which they are using.
Also updated the Support String to use some emojis so we should be able to quicker see if there is something wrong.
Just checking `true` or `false` could be difficult sometimes, and sometimes what we had as `false` wasn't bad either.
Also adjusted the eslint comments so it will work with the latest version of eslint.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Fix updating collections for a cipher
The newer clients expect a `cipherDetails` response on the `collections-admin` endpoints.
Without it, the client will cause an error and stops handling the update correctly.
This will fix this by returning the cipher json.
Fixes#7545Fixes#7546
Signed-off-by: BlackDex <black.dex@gmail.com>
* Cache CSS file in a different way
Currently we set a cache ttl of 24 hours, and users need to do a force refresh if there is anything changed to the CSS file.
In the past we have had several issue reported which were related to a still cached CSS file.
This commit will change the caching and also cache the generated CSS file in memory.
Instead of letting the browser cache it for 24 hours we generate an ETag, this is just a hash of the contents.
This ETag is returned by the browser during a request, and we can match this, and if so, just return a `304` `Not Modified`.
If the ETag is not known, we return the new content.
This should make simple refreshes by clients get updated settings or a new version of Vaultwarden which has other CSS entries get updated instantly.
If a user does a hard refresh, we will not receive the ETag and the content will be served.
The same goes if someone has the `reload_templates` feature enabled, since then we should not cache anyway.
If someone adjust settings via the `/admin` interface, the cache will be invalidated and a new CSS will be generated.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Fix showing events for a specific user
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update crates and adjust code.
- Updated opendal and adjusted code where needed.
- Updated yubico_ng and adjusted code where needed.
This version now supports using an own HttpClient and it pulls in no reqwest dependency anymore.
Now it will use our own client which uses custom hickory DNS and other features.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update web-vault to v2026.7.0
Signed-off-by: BlackDex <black.dex@gmail.com>
* Fix hadolint warnings
Signed-off-by: BlackDex <black.dex@gmail.com>
---------
Signed-off-by: BlackDex <black.dex@gmail.com>
A request addressed to an administrator is answered through the organization
and stays open for a week, so counting it as the pending request of its device
hid the short lived one the user was actually being shown and made approving
from another device fail for as long as it was open. It is now excluded, along
with requests past their window, as upstream does.
The stand-in page in the admin panel asked an administrator of an
organization for their vault master password inside the panel of the server
operator, which are two different roles, and it carried its own partial
login: no Argon2, no two-step login, and no fingerprint of the asking device
to compare against.
The page belongs where that already exists. The web vault has the whole
login stack and, in its AGPL part, the same unwrap the approval needs, since
account recovery does it too. Upstream keeps only the page itself in the
licensed part; the navigation entry and the string for it are already in
every build.
So the endpoints stay and the page goes, to be added to the web vault build
instead. The notification mail points at the route it lives under there. A
member who loses every trusted device is not stranded meanwhile: account
recovery gets them back in under the same conditions, at the price of a new
master password.
Findings from a review of the earlier commits.
A key rotation dropped the device key pairs along with the wrapped user
keys, so the devices listed in `/devices/update-trust` could never be
trusted again and silently lost their trust on every rotation. Those key
pairs are wrapped with the device key, which a rotation does not touch, so
they now stay and the re-wrap works. The invalidation also moved ahead of
the new user key: failing after it had been written left devices handing
out a key that no longer opens the vault.
The three key blobs are now checked against the shape of an `EncString`
instead of only for emptiness, as upstream does, which also keeps
multi-megabyte values out of those columns.
`/auth-requests/admin-request` is rate limited and reuses the open request
of a device instead of adding one per attempt, which mailed every
administrator again each time. Only confirmed memberships are asked, and
answered: an invitation that was never accepted is not a membership yet, a
revoked one is not one anymore, and neither should learn the address, the
IP and the device of the asker. `UserDecryptionOptions` reports the same
condition, so no way out is announced that would be refused when taken.
The bulk endpoints skip what they cannot answer rather than failing the
whole call after having answered everything before it, matching upstream,
and are bounded. The purge does its work per type in the database instead
of reading the table and deleting row by row, with indexes to go with it.
Also: approving an auth request is limited to the newest one of a device,
`GET /auth-requests/<id>` refuses an expired request like the anonymous
lookup already did, the approval notification addresses the device that
asked rather than the administrator's, and accepting an invitation while
enrolling is tied to the trusted device flow it was meant for.
Completes the trusted device flow for the case it was missing: a member who
unlocks with a trusted device, has no other device of their own left to ask,
and therefore has no way back into their vault. They can now turn to the
administrators of their organization, who hand them their own user key
encrypted for the key pair of the asking device.
New `atype` on auth_requests, mirroring bitwarden/server's AuthRequestType.
It decides who may answer a request and how long it stays open: 15 minutes
between the user's own devices, a week for an administrator, and half a day
for their answer once given. The purge job applies that per type instead of
dropping everything after 15 minutes, and both the answer and the anonymous
lookup now refuse an expired request, which they did not before.
POST /auth-requests/admin-request ask, one request per org
GET /organizations/<id>/auth-requests what is waiting for an answer
POST /organizations/<id>/auth-requests/<id> approve or deny one
POST /organizations/<id>/auth-requests/deny deny several
POST /organizations/<id>/auth-requests answer several
Asking requires authentication, so the anonymous `POST /auth-requests` now
refuses the type. Answering goes through the organization the request was
addressed to and needs admin rights there; the asking user cannot answer
their own request through `PUT /auth-requests/<id>`, which would make the
whole detour pointless. A denial is saved but not announced, so a request
that did not come from the member does not learn that it was seen. The
administrator's view leaves out the access code, which is the asking
device's proof and none of their business.
The administrators are mailed when a request arrives, the member when one of
their devices was let in, so an approval nobody asked for does not pass
unnoticed.
Two fixes without which none of this is reachable from a client:
- `UserDecryptionOptions.TrustedDeviceOption` reported `HasAdminApproval`
and `HasManageResetPasswordPermission` as a flat false. The clients
decide on `hasAdminApproval || hasMasterPassword` whether a login is a
returning user or a brand new one, so a member without a master password
was shown the screen for creating an account, on every device but the
one they first trusted. Both are now derived from the account recovery
enrollment and the role.
- `PUT /organizations/<id>/users/<id>/reset-password-enrollment` demanded a
master password whenever a key was supplied. An account that unlocks
with a trusted device has none, and the clients send nothing but the key
when they enroll during registration, so enrolling was impossible for
exactly the accounts that need it most. Upstream carves out the same
exception, keyed on the organization's SSO configuration rather than on
a server-wide setting as here. Enrolling now also accepts a pending
invitation, as upstream does, so a just-provisioned member does not stay
invited forever with nobody able to confirm them.
Lets a user unlock their vault after an SSO login with a key stored on a
trusted device instead of a master password, what Bitwarden calls trusted
device encryption. Off by default, enabled with
`SSO_TRUSTED_DEVICE_ENCRYPTION`, which requires `SSO_ENABLED`.
The client generates a key pair plus a device key that never leaves the
device and stores three blobs on the device row: the user key wrapped for
the device public key, that public key wrapped with the user key, and the
device private key wrapped with the device key. The server only keeps and
returns them, so a stored trust is worthless without the device itself.
A device counts as trusted only while all three are present.
Endpoints, matching bitwarden/server's DevicesController:
PUT|POST /devices/<identifier>/keys trust a device
POST /devices/<identifier>/retrieve-keys the public halves, for rotation
POST /devices/update-trust re-wrap after a key rotation
POST /devices/untrust drop the trust of some devices
POST /devices/lost-trust client reports a drifted trust
`GET /devices`, `GET /devices/identifier/<id>` and the login response now
report the real trust state instead of a hard-coded `isTrusted: false`.
An SSO login response carries `UserDecryptionOptions.TrustedDeviceOption`,
which is what makes the clients offer the flow at all. Upstream ties this to
the SSO configuration of an organization; SSO is configured for the whole
server here, so the setting decides it, and members of no organization can
use it too. A password login never gets these options. When the setting is
turned off again, devices that are still trusted keep receiving their keys
with `IsTdeOffboarding` set, so their owner can still unlock and set a
master password instead of being locked out.
`HasAdminApproval` and `HasManageResetPasswordPermission` are always false:
approval of a new device by an organization admin is not implemented, and
announcing it would leave the client waiting for a request nobody can
answer. A new device is unlocked by approving it from an already trusted
device or with the master password.
Two adjacent fixes this depends on:
- `POST /accounts/keys` refused to notice that an account already has a
key pair and would happily replace it, which makes every existing
cipher undecryptable. It now only accepts keys for an account that has
none, and tolerates a repeat of the same keys. The trusted device flow
is what makes this reachable in practice: a client that misjudges an
existing account as new posts a fresh key pair here.
- `POST /accounts/set-password` keyed "account already initialized" off
the key pair, so an account created without a master password could
never gain one. It now keys off the master password itself and refuses
to replace an existing key pair, which is what the check was guarding.
Rotating the account keys leaves every device holding a wrapped copy of the
previous user key. Those are dropped; the rotating device keeps its own
private key so its client can re-wrap the new user key right away via
`/devices/update-trust`. A client that skips that call ends up with no
trusted device rather than a broken unlock.
Covers dani-garcia/vaultwarden#7034.
- Updated API response to more closely match v2026.6.0+ server versions.
- Updated all the crates
- Updated Rust to v1.97.1
- Updated the web-vault to v2026.6.4
- Updated GitHub Actions
Signed-off-by: BlackDex <black.dex@gmail.com>
* Misc updates and fixes
- Updated Rust to v1.96.1
- Updated all the crates
- Updated GitHub Actions
- Updated the web-vault to v2026.6.2
- Updated Alpine to v3.24
- Fixed several clippy lints
- The `send` UUID wrappers didn't need the special namespace anymore since an updated crate, so removed this extra mod.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update MSRV to v1.94.1
Signed-off-by: BlackDex <black.dex@gmail.com>
---------
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update to Rust 2024 Edition
Updated to the Rust 2024 Edition and added and fixed several lint checks.
This is a large change which, because of the extra lints, added some possible fixes for issues.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Reorder and merge imports
Signed-off-by: BlackDex <black.dex@gmail.com>
* Remove "db_run!" macro calls where possible
Signed-off-by: BlackDex <black.dex@gmail.com>
---------
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update crates and gha
Updated all the crates
Updated GitHub Actions
Signed-off-by: BlackDex <black.dex@gmail.com>
* Fix restoring revoked user
A new endpoint is used to restore a revoked user.
This commit fixes that.
Fixes#7224
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update datatables
Signed-off-by: BlackDex <black.dex@gmail.com>
---------
Signed-off-by: BlackDex <black.dex@gmail.com>
* deps: upgrade the reqwest stack to 0.13
The reqwest 0.13 rustls feature selects the aws-lc provider. Use
rustls-no-provider instead, add rustls 0.23 with the ring provider, and
install that provider at process startup. This keeps Vaultwarden on the
existing ring crypto provider while giving reqwest, OpenDAL and lettre a
process-wide rustls provider.
Disable openidconnect default features and provide a small
AsyncHttpClient wrapper around Vaultwarden's shared reqwest client
builder. This preserves custom DNS, request blocking, timeouts and the
no-redirect OIDC behavior without openidconnect enabling its own reqwest
stack.
Upgrade yubico_ng to 0.15.0 and OpenDAL to 0.56.0. OpenDAL 0.56 also
moves S3 signing to reqsign 3, so switch the optional S3 dependencies
from reqsign/anyhow to reqsign-core and reqsign-aws-v4 and adapt the AWS
SDK credential bridge to the new ProvideCredential API.
Adjust the local OpenDAL call sites for the 0.56 API: use the FS_SCHEME
constant for filesystem checks and replace deprecated remove_all() with
delete_with(...).recursive(true) for Send file cleanup.
* storage: add OpenDAL S3 URI options
OpenDAL S3 storage accepts bucket and root path data today, but
serverless deployments also need URI query parameters to describe provider
behavior in one DATA_FOLDER value.
Update OpenDAL to 0.56.0 and build S3 operators with
S3Config::from_uri(). Keep Vaultwarden's AWS SDK credential chain by
installing a reqsign provider when the URI does not explicitly request
OpenDAL-native credential handling.
Move path handling and operator construction into storage.rs so S3-specific
parsing, credential setup, and URI path manipulation stay out of
configuration handling. Local filesystem behavior is unchanged, and S3
child paths are derived before query strings.
The collection update endpoints (post_collections_update and
post_collections_admin) call .unwrap() on cipher.organization_uuid
in four places. If a user-owned cipher without an organization
somehow reaches these code paths, the server would panic.
Extract the organization UUID early with a descriptive error message
instead of relying on .unwrap(), preventing potential panics and
providing a clear API error response.
Co-authored-by: Daniel García <dani-garcia@users.noreply.github.com>
* Add archiving
* Update Diesel macros and remove unnecessary SUPPORTED_FEATURE_FLAG
* Add IF EXISTS to down.sql migratinos
* Rename migration folders, separate logic based on PR threads
* Ensure SSO token is only usable on the same client
This commit adds an extra check via cookies to ensure the same browser/client is used to request and provide the SSO token.
Previously it would be able to provide a custom link which attackers could use to steal data.
While an attacker would still need the Master Password to be able to decrypt or execute specific actions, they were able to fetch encrypted data.
Solved with some help of Claude Code.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Check email-verified on SSO login/create
This commit prevents possible account takeover via SSO which doesn't check/validate or provide validated status of the email.
It was checked at other locations, but was skipped here.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Prevent data disclosure via SSO endpoints
This commit prevents some data disclosure and user enumeration by only returning the fake SSO identifier.
Since we do not check the identifier anywhere useful, returning the fake one is just fine.
During an invite to an org, that link contains the correct UUID and will be used for the master password requirements.
For anything else, server admins should set the `SSO_MASTER_PASSWORD_POLICY` env variable.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Adjust admin layout to fix issues when SSO is enabled
Signed-off-by: BlackDex <black.dex@gmail.com>
---------
Signed-off-by: BlackDex <black.dex@gmail.com>
Quote from the lint description:
"More flexibility, better memory optimization, and more idiomatic Rust code.
&Option<T> in a function signature breaks encapsulation because the caller must own T and move it into an Option to call with it. When returned, the owner must internally store it as Option<T> in order to return it. At a lower level, &Option<T> points to memory with the presence bit flag plus the T value, whereas Option<&T> is usually optimized to a single pointer, so it may be more optimal."
Quote from lint description:
"Using a smaller unit for a duration that is evenly divisible by a larger unit reduces readability. Readers have to mentally convert values, which can be error-prone and makes the code less clear."
- Updated web-vault to v2026.3.1
Added a new endpoint needed for the admin console to work
- Updated all crates including webpki CVE fixes - Closes#7115
- Updated GHA
Signed-off-by: BlackDex <black.dex@gmail.com>
managers without the access_all flag should not be able to create
collections. the manage all collections permission actually consists of
three separate custom permissions that have not been implemented yet for
more fine-grain access control.
In newer versions of Bitwarden Clients instead of using `null` the folder_id will be an empty string.
This commit adds a special deserialize_with function to keep the same way of working code-wise.
Fixes#6962
Signed-off-by: BlackDex <black.dex@gmail.com>
When a security-stamp gets reset/rotated we should also rotate all device refresh-tokens to invalidate them.
Else clients are still able to use old refresh tokens.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update Feature Flags
Added new feature flags which could be supported without issues.
Removed all deprecated feature flags and only match supported flags.
Do not error on invalid flags during load, but do on config save via admin interface.
During load it will print a `WARNING`, this is to prevent breaking setups when flags are removed, but are still configured.
There are no feature flags anymore currently needed to be set by default, so those are removed now.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Adjust code a bit and add Diagnostics check
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update .env template
Signed-off-by: BlackDex <black.dex@gmail.com>
---------
Signed-off-by: BlackDex <black.dex@gmail.com>
Newer versions of the Bitwarden client use Base64 with padding.
Since this is not a streaming string, but a defined length, we can just strip the `=` chars.
Fixes#6960
Signed-off-by: BlackDex <black.dex@gmail.com>
- Update Rust to v1.93.1
- Updated all the crates
Adjust changes needed for the newer `rand` crate
- Updated GitHub Actions
Signed-off-by: BlackDex <black.dex@gmail.com>
Fix an issue where it was possible for users who were not eligible to access all org ciphers to be able to download and extract the encrypted contents.
Only Managers with full access and Admins and Owners should be able to access this endpoint.
This change will block and prevent access for other users.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Fix posting cipher with readonly collections
This fix will check if a collection is writeable for the user, and if not error out early instead of creating the cipher first and leaving it.
It will also save some database transactions.
Fixes#6562
Signed-off-by: BlackDex <black.dex@gmail.com>
* Adjust code to delete on error
Signed-off-by: BlackDex <black.dex@gmail.com>
---------
Signed-off-by: BlackDex <black.dex@gmail.com>