diff --git a/.env.template b/.env.template index 05b640e4..c8c1dede 100644 --- a/.env.template +++ b/.env.template @@ -233,7 +233,8 @@ # SIGNUPS_ALLOWED=true ## Controls if new users need to verify their email address upon registration -## Note that setting this option to true prevents logins until the email address has been verified! +## On new client versions, this will require the user to verify their email at signup time. +## On older clients, it will require the user to verify their email before they can log in. ## The welcome email will include a verification link, and login attempts will periodically ## trigger another verification email to be sent. # SIGNUPS_VERIFY=false @@ -357,6 +358,9 @@ ## - "inline-menu-positioning-improvements": Enable the use of inline menu password generator and identity suggestions in the browser extension. ## - "ssh-key-vault-item": Enable the creation and use of SSH key vault items. (Needs clients >=2024.12.0) ## - "ssh-agent": Enable SSH agent support on Desktop. (Needs desktop >=2024.12.0) +## - "anon-addy-self-host-alias": Enable configuring self-hosted Anon Addy alias generator. (Needs Android >=2025.2.0) +## - "simple-login-self-host-alias": Enable configuring self-hosted Simple Login alias generator. (Needs Android >=2025.2.0) +## - "mutual-tls": Enable the use of mutual TLS on Android (Client >= 2025.2.0) # EXPERIMENTAL_CLIENT_FEATURE_FLAGS=fido2-vault-credentials ## Require new device emails. When a user logs in an email is required to be sent. @@ -526,7 +530,7 @@ ## Maximum attempts before an email token is reset and a new email will need to be sent. # EMAIL_ATTEMPTS_LIMIT=3 ## -## Setup email 2FA regardless of any organization policy +## Setup email 2FA on registration regardless of any organization policy # EMAIL_2FA_ENFORCE_ON_VERIFIED_INVITE=false ## Automatically setup email 2FA as fallback provider when needed # EMAIL_2FA_AUTO_FALLBACK=false diff --git a/Cargo.lock b/Cargo.lock index cd9bcd3f..4ee864f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,9 +111,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310c9bcae737a48ef5cdee3174184e6d548b292739ede61a1f955ef76a738861" +checksum = "c0cf008e5e1a9e9e22a7d3c9a4992e21a350290069e36d8fb72304ed17e8f2d2" dependencies = [ "brotli", "flate2", @@ -220,9 +220,9 @@ dependencies = [ [[package]] name = "async-std" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" +checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" dependencies = [ "async-channel 1.9.0", "async-global-executor", @@ -275,9 +275,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.87" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", @@ -352,9 +352,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "bigdecimal" @@ -1139,9 +1139,9 @@ dependencies = [ [[package]] name = "email-encoding" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea3d894bbbab314476b265f9b2d46bf24b123a36dd0e96b06a1b49545b9d9dcc" +checksum = "20b9cde6a71f9f758440470f3de16db6c09a02c443ce66850d87f5410548fb8e" dependencies = [ "base64 0.22.1", "memchr", @@ -1255,9 +1255,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "rand_core 0.6.4", "subtle", @@ -1536,7 +1536,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d9e3df7f0222ce5184154973d247c591d9aadc28ce7a73c6cd31100c9facff6" dependencies = [ "codemap", - "indexmap 2.7.1", + "indexmap 2.8.0", "lasso", "once_cell", "phf", @@ -1564,8 +1564,8 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.2.0", - "indexmap 2.7.1", + "http 1.3.1", + "indexmap 2.8.0", "slab", "tokio", "tokio-util", @@ -1580,9 +1580,9 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "handlebars" -version = "6.3.1" +version = "6.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d752747ddabc4c1a70dd28e72f2e3c218a816773e0d7faf67433f1acfa6cba7c" +checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098" dependencies = [ "derive_builder", "log", @@ -1710,17 +1710,6 @@ dependencies = [ "digest", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - [[package]] name = "hostname" version = "0.4.0" @@ -1754,9 +1743,9 @@ dependencies = [ [[package]] name = "http" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -1781,18 +1770,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.2.0", + "http 1.3.1", ] [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.2.0", + "futures-core", + "http 1.3.1", "http-body 1.0.1", "pin-project-lite", ] @@ -1842,7 +1831,7 @@ dependencies = [ "futures-channel", "futures-util", "h2", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "httparse", "itoa", @@ -1859,10 +1848,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.2.0", + "http 1.3.1", "hyper 1.6.0", "hyper-util", - "rustls 0.23.23", + "rustls 0.23.25", "rustls-pki-types", "tokio", "tokio-rustls 0.26.2", @@ -1895,7 +1884,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "hyper 1.6.0", "pin-project-lite", @@ -2086,9 +2075,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -2216,9 +2205,9 @@ dependencies = [ [[package]] name = "lettre" -version = "0.11.14" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d476fe7a4a798f392ce34947aa7d53d981127e37523c5251da3c927f7fa901f" +checksum = "759bc2b8eabb6a30b235d6f716f7f36479f4b38cbe65b8747aefee51f89e8437" dependencies = [ "async-std", "async-trait", @@ -2229,7 +2218,7 @@ dependencies = [ "fastrand", "futures-io", "futures-util", - "hostname 0.4.0", + "hostname", "httpdate", "idna", "mime", @@ -2247,9 +2236,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.170" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libm" @@ -2292,9 +2281,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "litemap" @@ -2359,12 +2348,6 @@ dependencies = [ "syn", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.1.0" @@ -2466,7 +2449,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.2.0", + "http 1.3.1", "httparse", "memchr", "mime", @@ -2661,7 +2644,7 @@ dependencies = [ "base64 0.22.1", "chrono", "getrandom 0.2.15", - "http 1.2.0", + "http 1.3.1", "rand 0.8.5", "reqwest", "serde", @@ -2683,9 +2666,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" [[package]] name = "openidconnect" @@ -2698,7 +2681,7 @@ dependencies = [ "dyn-clone", "ed25519-dalek", "hmac", - "http 1.2.0", + "http 1.3.1", "itertools", "log", "oauth2", @@ -3096,11 +3079,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.7.35", + "zerocopy 0.8.23", ] [[package]] @@ -3195,12 +3178,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quinn" version = "0.11.6" @@ -3212,7 +3189,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.23", + "rustls 0.23.25", "socket2", "thiserror 2.0.12", "tokio", @@ -3230,7 +3207,7 @@ dependencies = [ "rand 0.8.5", "ring", "rustc-hash", - "rustls 0.23.23", + "rustls 0.23.25", "rustls-pki-types", "slab", "thiserror 2.0.12", @@ -3255,9 +3232,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -3434,9 +3411,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.12" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +checksum = "989e327e510263980e231de548a33e63d34962d29ae61b467389a1a09627a254" dependencies = [ "async-compression", "base64 0.22.1", @@ -3448,7 +3425,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", @@ -3464,7 +3441,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.23", + "rustls 0.23.25", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", @@ -3490,12 +3467,11 @@ dependencies = [ [[package]] name = "resolv-conf" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +checksum = "48375394603e3dd4b2d64371f7148fd8c7baa2680e28741f2cb8d23b59e3d4c4" dependencies = [ - "hostname 0.3.1", - "quick-error", + "hostname", ] [[package]] @@ -3510,9 +3486,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", @@ -3557,7 +3533,7 @@ dependencies = [ "either", "figment", "futures", - "indexmap 2.7.1", + "indexmap 2.8.0", "log", "memchr", "multer", @@ -3589,7 +3565,7 @@ checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" dependencies = [ "devise", "glob", - "indexmap 2.7.1", + "indexmap 2.8.0", "proc-macro2", "quote", "rocket_http", @@ -3609,7 +3585,7 @@ dependencies = [ "futures", "http 0.2.12", "hyper 0.14.32", - "indexmap 2.7.1", + "indexmap 2.8.0", "log", "memchr", "pear", @@ -3651,9 +3627,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" dependencies = [ "const-oid", "digest", @@ -3715,14 +3691,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8dcd64f141950290e45c99f7710ede1b600297c91818bb30b3667c0f45dc0" +checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.9.2", + "linux-raw-sys 0.9.3", "windows-sys 0.59.0", ] @@ -3740,14 +3716,14 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.23" +version = "0.23.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" +checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.8", + "rustls-webpki 0.103.0", "subtle", "zeroize", ] @@ -3791,9 +3767,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.8" +version = "0.103.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +checksum = "0aa4eeac2588ffff23e9d7a7e9b3f971c5fb5b7ebc9452745e0c232c64f83b2f" dependencies = [ "ring", "rustls-pki-types", @@ -3909,9 +3885,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -3938,9 +3914,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -4009,7 +3985,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.7.1", + "indexmap 2.8.0", "serde", "serde_derive", "serde_json", @@ -4229,9 +4205,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.99" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -4264,7 +4240,7 @@ version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "019f1500a13379b7d051455df397c75770de6311a7a188a699499502704d9f10" dependencies = [ - "hostname 0.4.0", + "hostname", "libc", "log", "time", @@ -4299,15 +4275,14 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] name = "tempfile" -version = "3.18.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567" +checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600" dependencies = [ - "cfg-if", "fastrand", "getrandom 0.3.1", "once_cell", - "rustix 1.0.0", + "rustix 1.0.2", "windows-sys 0.59.0", ] @@ -4430,9 +4405,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.43.0" +version = "1.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" dependencies = [ "backtrace", "bytes", @@ -4483,7 +4458,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.23", + "rustls 0.23.25", "tokio", ] @@ -4524,9 +4499,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" dependencies = [ "bytes", "futures-core", @@ -4562,7 +4537,7 @@ version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.8.0", "serde", "serde_spanned", "toml_datetime", @@ -4691,7 +4666,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.2.0", + "http 1.3.1", "httparse", "log", "rand 0.8.5", @@ -4788,9 +4763,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.15.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ "getrandom 0.3.1", ] @@ -4863,6 +4838,7 @@ dependencies = [ "semver", "serde", "serde_json", + "subtle", "syslog", "time", "tokio", @@ -5067,9 +5043,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" [[package]] name = "winapi" @@ -5138,32 +5114,31 @@ checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" [[package]] name = "windows-registry" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", "windows-strings", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189" dependencies = [ - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-result", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -5217,13 +5192,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -5236,6 +5227,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -5248,6 +5245,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -5260,12 +5263,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -5278,6 +5293,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -5290,6 +5311,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -5302,6 +5329,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -5314,11 +5347,17 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] @@ -5414,7 +5453,6 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", "zerocopy-derive 0.7.35", ] diff --git a/Cargo.toml b/Cargo.toml index 6830951f..acf4672d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,7 +94,8 @@ libsqlite3-sys = { version = "0.31.0", features = ["bundled"], optional = true } # Crypto-related libraries rand = "0.9.0" -ring = "0.17.11" +ring = "0.17.13" +subtle = "2.6.1" # UUID generation uuid = { version = "1.14.0", features = ["v4"] } diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 2ce150ed..d68965e8 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -71,18 +71,31 @@ pub fn routes() -> Vec { #[serde(rename_all = "camelCase")] pub struct RegisterData { email: String, + kdf: Option, kdf_iterations: Option, kdf_memory: Option, kdf_parallelism: Option, + + #[serde(alias = "userSymmetricKey")] key: String, + #[serde(alias = "userAsymmetricKeys")] keys: Option, + master_password_hash: String, master_password_hint: Option, + name: Option, - token: Option, + #[allow(dead_code)] organization_user_id: Option, + + // Used only from the register/finish endpoint + email_verification_token: Option, + accept_emergency_access_id: Option, + accept_emergency_access_invite_token: Option, + #[serde(alias = "token")] + org_invite_token: Option, } #[derive(Debug, Deserialize)] @@ -139,13 +152,78 @@ async fn is_email_2fa_required(member_id: Option, conn: &mut DbCon #[post("/accounts/register", data = "")] async fn register(data: Json, conn: DbConn) -> JsonResult { - _register(data, conn).await + _register(data, false, conn).await } -pub async fn _register(data: Json, mut conn: DbConn) -> JsonResult { - let data: RegisterData = data.into_inner(); +pub async fn _register(data: Json, email_verification: bool, mut conn: DbConn) -> JsonResult { + let mut data: RegisterData = data.into_inner(); let email = data.email.to_lowercase(); + let mut email_verified = false; + + let mut pending_emergency_access = None; + + // First, validate the provided verification tokens + if email_verification { + match ( + &data.email_verification_token, + &data.accept_emergency_access_id, + &data.accept_emergency_access_invite_token, + &data.organization_user_id, + &data.org_invite_token, + ) { + // Normal user registration, when email verification is required + (Some(email_verification_token), None, None, None, None) => { + let claims = crate::auth::decode_register_verify(email_verification_token)?; + if claims.sub != data.email { + err!("Email verification token does not match email"); + } + + // During this call we don't get the name, so extract it from the claims + if claims.name.is_some() { + data.name = claims.name; + } + email_verified = claims.verified; + } + // Emergency access registration + (None, Some(accept_emergency_access_id), Some(accept_emergency_access_invite_token), None, None) => { + if !CONFIG.emergency_access_allowed() { + err!("Emergency access is not enabled.") + } + + let claims = crate::auth::decode_emergency_access_invite(accept_emergency_access_invite_token)?; + + if claims.email != data.email { + err!("Claim email does not match email") + } + if &claims.emer_id != accept_emergency_access_id { + err!("Claim emer_id does not match accept_emergency_access_id") + } + + pending_emergency_access = Some((accept_emergency_access_id, claims)); + email_verified = true; + } + // Org invite + (None, None, None, Some(organization_user_id), Some(org_invite_token)) => { + let claims = decode_invite(org_invite_token)?; + + if claims.email != data.email { + err!("Claim email does not match email") + } + + if &claims.member_id != organization_user_id { + err!("Claim org_user_id does not match organization_user_id") + } + + email_verified = true; + } + + _ => { + err!("Registration is missing required parameters") + } + } + } + // Check if the length of the username exceeds 50 characters (Same is Upstream Bitwarden) // This also prevents issues with very long usernames causing to large JWT's. See #2419 if let Some(ref name) = data.name { @@ -159,20 +237,17 @@ pub async fn _register(data: Json, mut conn: DbConn) -> JsonResult let password_hint = clean_password_hint(&data.master_password_hint); enforce_password_hint_setting(&password_hint)?; - let mut verified_by_invite = false; - let mut user = match User::find_by_mail(&email, &mut conn).await { - Some(mut user) => { + Some(user) => { if !user.password_hash.is_empty() { err!("Registration not allowed or user already exists") } - if let Some(token) = data.token { + if let Some(token) = data.org_invite_token { let claims = decode_invite(&token)?; if claims.email == email { // Verify the email address when signing up via a valid invite token - verified_by_invite = true; - user.verified_at = Some(Utc::now().naive_utc()); + email_verified = true; user } else { err!("Registration email does not match invite email") @@ -193,7 +268,10 @@ pub async fn _register(data: Json, mut conn: DbConn) -> JsonResult // Order is important here; the invitation check must come first // because the vaultwarden admin can invite anyone, regardless // of other signup restrictions. - if Invitation::take(&email, &mut conn).await || CONFIG.is_signup_allowed(&email) { + if Invitation::take(&email, &mut conn).await + || CONFIG.is_signup_allowed(&email) + || pending_emergency_access.is_some() + { User::new(email.clone(), None) } else { err!("Registration not allowed or user already exists") @@ -228,8 +306,12 @@ pub async fn _register(data: Json, mut conn: DbConn) -> JsonResult user.public_key = Some(keys.public_key); } + if email_verified { + user.verified_at = Some(Utc::now().naive_utc()); + } + if CONFIG.mail_enabled() { - if CONFIG.signups_verify() && !verified_by_invite { + if CONFIG.signups_verify() && !email_verified { if let Err(e) = mail::send_welcome_must_verify(&user.email, &user.uuid).await { error!("Error sending welcome email: {:#?}", e); } @@ -238,7 +320,7 @@ pub async fn _register(data: Json, mut conn: DbConn) -> JsonResult error!("Error sending welcome email: {:#?}", e); } - if verified_by_invite && is_email_2fa_required(data.organization_user_id, &mut conn).await { + if email_verified && is_email_2fa_required(data.organization_user_id, &mut conn).await { email::activate_email_2fa(&user, &mut conn).await.ok(); } } diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 896297fa..92c66df2 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -206,6 +206,9 @@ fn config() -> Json { feature_states.insert("key-rotation-improvements".to_string(), true); feature_states.insert("flexible-collections-v-1".to_string(), false); + feature_states.insert("email-verification".to_string(), true); + feature_states.insert("unauth-ui-refresh".to_string(), true); + Json(json!({ // Note: The clients use this version to handle backwards compatibility concerns // This means they expect a version that closely matches the Bitwarden server version diff --git a/src/api/identity.rs b/src/api/identity.rs index 5ca185a9..6862ca28 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -30,7 +30,18 @@ use crate::{ }; pub fn routes() -> Vec { - routes![login, prelogin, identity_register, _prevalidate, prevalidate, authorize, oidcsignin, oidcsignin_error] + routes![ + login, + prelogin, + identity_register, + register_verification_email, + register_finish, + _prevalidate, + prevalidate, + authorize, + oidcsignin, + oidcsignin_error + ] } #[post("/connect/token", data = "")] @@ -841,7 +852,67 @@ async fn prelogin(data: Json, conn: DbConn) -> Json { #[post("/accounts/register", data = "")] async fn identity_register(data: Json, conn: DbConn) -> JsonResult { - _register(data, conn).await + _register(data, false, conn).await +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct RegisterVerificationData { + email: String, + name: Option, + // receiveMarketingEmails: bool, +} + +#[derive(rocket::Responder)] +enum RegisterVerificationResponse { + NoContent(()), + Token(Json), +} + +#[post("/accounts/register/send-verification-email", data = "")] +async fn register_verification_email( + data: Json, + mut conn: DbConn, +) -> ApiResult { + let data = data.into_inner(); + + if !CONFIG.is_signup_allowed(&data.email) { + err!("Registration not allowed or user already exists") + } + + let should_send_mail = CONFIG.mail_enabled() && CONFIG.signups_verify(); + + if User::find_by_mail(&data.email, &mut conn).await.is_some() { + if should_send_mail { + // There is still a timing side channel here in that the code + // paths that send mail take noticeably longer than ones that + // don't. Add a randomized sleep to mitigate this somewhat. + use rand::{rngs::SmallRng, Rng, SeedableRng}; + let mut rng = SmallRng::from_os_rng(); + let delta: i32 = 100; + let sleep_ms = (1_000 + rng.random_range(-delta..=delta)) as u64; + tokio::time::sleep(tokio::time::Duration::from_millis(sleep_ms)).await; + } + return Ok(RegisterVerificationResponse::NoContent(())); + } + + let token_claims = auth::generate_register_verify_claims(data.email.clone(), data.name.clone(), should_send_mail); + let token = auth::encode_jwt(&token_claims); + + if should_send_mail { + mail::send_register_verify_email(&data.email, &token).await?; + + Ok(RegisterVerificationResponse::NoContent(())) + } else { + // If email verification is not required, return the token directly + // the clients will use this token to finish the registration + Ok(RegisterVerificationResponse::Token(Json(token))) + } +} + +#[post("/accounts/register/finish", data = "")] +async fn register_finish(data: Json, conn: DbConn) -> JsonResult { + _register(data, true, conn).await } // https://github.com/bitwarden/jslib/blob/master/common/src/models/request/tokenRequest.ts diff --git a/src/auth.rs b/src/auth.rs index d7a5af35..8dff3603 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -42,6 +42,7 @@ static JWT_ADMIN_ISSUER: Lazy = Lazy::new(|| format!("{}|admin", CONFIG. static JWT_SEND_ISSUER: Lazy = Lazy::new(|| format!("{}|send", CONFIG.domain_origin())); static JWT_ORG_API_KEY_ISSUER: Lazy = Lazy::new(|| format!("{}|api.organization", CONFIG.domain_origin())); static JWT_FILE_DOWNLOAD_ISSUER: Lazy = Lazy::new(|| format!("{}|file_download", CONFIG.domain_origin())); +static JWT_REGISTER_VERIFY_ISSUER: Lazy = Lazy::new(|| format!("{}|register_verify", CONFIG.domain_origin())); static PRIVATE_RSA_KEY: OnceCell = OnceCell::new(); static PUBLIC_RSA_KEY: OnceCell = OnceCell::new(); @@ -156,6 +157,10 @@ pub fn decode_file_download(token: &str) -> Result { decode_jwt(token, JWT_FILE_DOWNLOAD_ISSUER.to_string()) } +pub fn decode_register_verify(token: &str) -> Result { + decode_jwt(token, JWT_REGISTER_VERIFY_ISSUER.to_string()) +} + #[derive(Debug, Serialize, Deserialize)] pub struct LoginJwtClaims { // Not before @@ -393,6 +398,33 @@ pub fn generate_file_download_claims(cipher_id: CipherId, file_id: AttachmentId) } } +#[derive(Debug, Serialize, Deserialize)] +pub struct RegisterVerifyClaims { + // Not before + pub nbf: i64, + // Expiration time + pub exp: i64, + // Issuer + pub iss: String, + // Subject + pub sub: String, + + pub name: Option, + pub verified: bool, +} + +pub fn generate_register_verify_claims(email: String, name: Option, verified: bool) -> RegisterVerifyClaims { + let time_now = Utc::now(); + RegisterVerifyClaims { + nbf: time_now.timestamp(), + exp: (time_now + TimeDelta::try_minutes(30).unwrap()).timestamp(), + iss: JWT_REGISTER_VERIFY_ISSUER.to_string(), + sub: email, + name, + verified, + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct BasicJwtClaims { // Not before diff --git a/src/config.rs b/src/config.rs index 9ce45d20..518f26b8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -487,7 +487,8 @@ make_config! { disable_icon_download: bool, true, def, false; /// Allow new signups |> Controls whether new users can register. Users can be invited by the vaultwarden admin even if this is disabled signups_allowed: bool, true, def, true; - /// Require email verification on signups. This will prevent logins from succeeding until the address has been verified + /// Require email verification on signups. On new client versions, this will require verification at signup time. On older clients, + /// this will prevent logins from succeeding until the address has been verified signups_verify: bool, true, def, false; /// If signups require email verification, automatically re-send verification email if it hasn't been sent for a while (in seconds) signups_verify_resend_time: u64, true, def, 3_600; @@ -773,7 +774,7 @@ make_config! { email_expiration_time: u64, true, def, 600; /// Maximum attempts |> Maximum attempts before an email token is reset and a new email will need to be sent email_attempts_limit: u64, true, def, 3; - /// Automatically enforce at login |> Setup email 2FA provider regardless of any organization policy + /// Setup email 2FA at signup |> Setup email 2FA provider on registration regardless of any organization policy email_2fa_enforce_on_verified_invite: bool, true, def, false; /// Auto-enable 2FA (Know the risks!) |> Automatically setup email 2FA as fallback provider when needed email_2fa_auto_fallback: bool, true, def, false; @@ -881,6 +882,9 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { "inline-menu-positioning-improvements", "ssh-key-vault-item", "ssh-agent", + "anon-addy-self-host-alias", + "simple-login-self-host-alias", + "mutual-tls", ]; let configured_flags = parse_experimental_client_feature_flags(&cfg.experimental_client_feature_flags); let invalid_flags: Vec<_> = configured_flags.keys().filter(|flag| !KNOWN_FLAGS.contains(&flag.as_str())).collect(); @@ -1505,6 +1509,7 @@ where reg!("email/protected_action", ".html"); reg!("email/pw_hint_none", ".html"); reg!("email/pw_hint_some", ".html"); + reg!("email/register_verify_email", ".html"); reg!("email/send_2fa_removed_from_org", ".html"); reg!("email/send_emergency_access_invite", ".html"); reg!("email/send_org_invite", ".html"); diff --git a/src/crypto.rs b/src/crypto.rs index 5ab8f1fb..ada0a26a 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -110,7 +110,6 @@ pub fn generate_api_key() -> String { // Constant time compare // pub fn ct_eq, U: AsRef<[u8]>>(a: T, b: U) -> bool { - use ring::constant_time::verify_slices_are_equal; - - verify_slices_are_equal(a.as_ref(), b.as_ref()).is_ok() + use subtle::ConstantTimeEq; + a.as_ref().ct_eq(b.as_ref()).into() } diff --git a/src/mail.rs b/src/mail.rs index a707ec54..5758301a 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -201,6 +201,27 @@ pub async fn send_verify_email(address: &str, user_id: &UserId) -> EmptyResult { send_email(address, &subject, body_html, body_text).await } +pub async fn send_register_verify_email(email: &str, token: &str) -> EmptyResult { + let mut query = url::Url::parse("https://query.builder").unwrap(); + query.query_pairs_mut().append_pair("email", email).append_pair("token", token); + let query_string = match query.query() { + None => err!("Failed to build verify URL query parameters"), + Some(query) => query, + }; + + let (subject, body_html, body_text) = get_text( + "email/register_verify_email", + json!({ + // `url.Url` would place the anchor `#` after the query parameters + "url": format!("{}/#/finish-signup/?{}", CONFIG.domain(), query_string), + "img_src": CONFIG._smtp_img_src(), + "email": email, + }), + )?; + + send_email(email, &subject, body_html, body_text).await +} + pub async fn send_welcome(address: &str) -> EmptyResult { let (subject, body_html, body_text) = get_text( "email/welcome", diff --git a/src/static/templates/email/register_verify_email.hbs b/src/static/templates/email/register_verify_email.hbs new file mode 100644 index 00000000..37eaab9e --- /dev/null +++ b/src/static/templates/email/register_verify_email.hbs @@ -0,0 +1,8 @@ +Verify Your Email + +Verify this email address to finish creating your account by clicking the link below. + +Verify Email Address Now: {{{url}}} + +If you did not request to verify your account, you can safely ignore this email. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/src/static/templates/email/register_verify_email.html.hbs b/src/static/templates/email/register_verify_email.html.hbs new file mode 100644 index 00000000..b3d382a0 --- /dev/null +++ b/src/static/templates/email/register_verify_email.html.hbs @@ -0,0 +1,24 @@ +Verify Your Email + +{{> email/email_header }} + + + + + + + + + + +
+ Verify this email address to finish creating your account by clicking the link below. +
+ + Verify Email Address Now + +
+ If you did not request to verify your account, you can safely ignore this email. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/src/static/templates/scss/vaultwarden.scss.hbs b/src/static/templates/scss/vaultwarden.scss.hbs index 0ea820e9..4a7e47bf 100644 --- a/src/static/templates/scss/vaultwarden.scss.hbs +++ b/src/static/templates/scss/vaultwarden.scss.hbs @@ -88,12 +88,19 @@ bit-nav-logo bit-nav-item .bwi-shield { /**** END Static Vaultwarden Changes ****/ /**** START Dynamic Vaultwarden Changes ****/ {{#if signup_disabled}} +/* From web vault 2025.1.2 and onwards, the signup button is hidden + when signups are disabled as the web vault checks the /api/config endpoint. + Note that the clients tend to aggressively cache this endpoint, so it might + take a while for the change to take effect. To avoid the button appearing + when it shouldn't, we'll keep this style in place for a couple of versions */ +{{#if webver "<2025.3.0"}} /* Hide the register link on the login screen */ app-login form div + div + div + div + hr, app-login form div + div + div + div + hr + p { @extend %vw-hide; } {{/if}} +{{/if}} {{#if sso_only}} /* Hide Master password login */