diff --git a/Cargo.lock b/Cargo.lock index 6997d4d..0e1f858 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.22.1" @@ -319,6 +325,18 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.7" @@ -338,6 +356,33 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "der" version = "0.7.10" @@ -398,6 +443,44 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2 0.10.9", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.15.0" @@ -407,6 +490,27 @@ dependencies = [ "serde", ] +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -445,6 +549,22 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -568,6 +688,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -595,6 +716,17 @@ dependencies = [ "wasip3", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -917,12 +1049,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1" dependencies = [ "base64", + "ed25519-dalek", "getrandom 0.2.17", + "hmac", "js-sys", + "p256", + "p384", "pem", "rand 0.8.6", + "rsa", "serde", "serde_json", + "sha2 0.10.9", "signature", "simple_asn1", ] @@ -1122,6 +1260,30 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + [[package]] name = "parking" version = "2.2.1" @@ -1260,6 +1422,15 @@ dependencies = [ "syn", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -1366,6 +1537,16 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "rhythm-backend" version = "0.1.0" @@ -1382,6 +1563,7 @@ dependencies = [ "sha2 0.11.0", "sqlx", "thiserror", + "time", "tokio", "tower-cookies", "tower-http", @@ -1412,6 +1594,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -1430,6 +1621,20 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "1.0.28" diff --git a/Cargo.toml b/Cargo.toml index c636359..8a93e8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ thiserror = "2" serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.149" argon2 = "0.5.3" -jsonwebtoken = { version = "10.3.0", features = ["rand"] } +jsonwebtoken = { version = "10.3.0", features = ["rand", "rust_crypto"] } chrono = { version = "0.4.44", features = ["serde"] } uuid = { version = "1.23.1", features = ["serde", "v4"] } rand = "0.10.1" @@ -24,3 +24,4 @@ sha2 = "0.11.0" hex = "0.4.3" tower-cookies = "0.11.0" tower-http = { version = "0.6.8", features = ["trace"] } +time = "0.3.47" diff --git a/http_client/rhythm/.gitignore b/http_client/rhythm/.gitignore new file mode 100644 index 0000000..e19311f --- /dev/null +++ b/http_client/rhythm/.gitignore @@ -0,0 +1,9 @@ +# Secrets +.env* + +# Dependencies +node_modules + +# OS files +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/http_client/rhythm/base ping health.yml b/http_client/rhythm/base ping health.yml new file mode 100644 index 0000000..4d3a17a --- /dev/null +++ b/http_client/rhythm/base ping health.yml @@ -0,0 +1,15 @@ +info: + name: base ping health + type: http + seq: 2 + +http: + method: GET + url: "{{base_url}}/" + auth: inherit + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 diff --git a/http_client/rhythm/environments/test.yml b/http_client/rhythm/environments/test.yml new file mode 100644 index 0000000..f5ad812 --- /dev/null +++ b/http_client/rhythm/environments/test.yml @@ -0,0 +1,8 @@ +name: test +variables: + - name: access_token + value: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI5NWM0NWVlNC1iNmY4LTRjY2ItYTEzNi0wMDVlYzdmMmNjMTMiLCJpYXQiOjE3Nzc1NjY3MjYsImV4cCI6MTc3NzU2NzYyNiwianRpIjoiNDU1MTFkNzQtNmU0OS00NzA2LTljOGYtNWY2ODcxYjgzNTY5In0.0VGbyYuExdt8K-WuAILZJCbIzGxy3_MhexgcDUpyiCI + - name: refresh_token + value: refresh_token=3f9e5d21748cebd5113604c045ed485ec82ef6ea46f539bd86f0a7de1b03d025; HttpOnly; SameSite=Strict; Secure; Path=/; Max-Age=604800 + - name: base_url + value: http://localhost:6969 diff --git a/http_client/rhythm/opencollection.yml b/http_client/rhythm/opencollection.yml new file mode 100644 index 0000000..3d96ac5 --- /dev/null +++ b/http_client/rhythm/opencollection.yml @@ -0,0 +1,10 @@ +opencollection: 1.0.0 + +info: + name: rhythm +bundled: false +extensions: + bruno: + ignore: + - node_modules + - .git diff --git a/http_client/rhythm/register.yml b/http_client/rhythm/register.yml new file mode 100644 index 0000000..db637b1 --- /dev/null +++ b/http_client/rhythm/register.yml @@ -0,0 +1,37 @@ +info: + name: register + type: http + seq: 1 + +http: + method: POST + url: "{{base_url}}/api/v1/auth/register" + body: + type: json + data: | + { + "email": "d3@v.it", + "password": "password" + } + auth: inherit + +runtime: + scripts: + - type: after-response + code: |- + // Post-response script on your login endpoint + const response = res.getBody(); + const token = response.access_token; + // Save to collection variables + bru.setEnvVar("access_token", token); + console.log("access_token", token) + + const cookies = res.getHeaders()['set-cookie']; + bru.setEnvVar("refresh_token", cookies[0]); + console.log("refresh_token", cookies[0]) + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 diff --git a/src/controller/model/auth_model.rs b/src/controller/model/auth_model.rs index e45e548..ad92d69 100644 --- a/src/controller/model/auth_model.rs +++ b/src/controller/model/auth_model.rs @@ -14,5 +14,4 @@ pub struct RegisterRequest { #[derive(Serialize)] pub struct AuthResponse { pub access_token: String, - pub refresh_token: String, } diff --git a/src/service/auth_service.rs b/src/service/auth_service.rs index b9fb03b..b2022a5 100644 --- a/src/service/auth_service.rs +++ b/src/service/auth_service.rs @@ -1,7 +1,7 @@ use std::time::Instant; use axum::Json; -use chrono::{Duration, Utc}; +use chrono::Duration; use tower_cookies::{Cookie, Cookies}; use crate::controller::model::auth_model::*; @@ -33,7 +33,8 @@ pub async fn register( if user.is_some() { // user already registered anti_enumeration_delay(start, 150, 300).await; - return Err(AppError::Internal); + tracing::warn!("registering with an already used email address"); + return Err(AppError::Validation("bad request".to_string())); } } let h = hash::hash(&req.password)?; @@ -46,13 +47,33 @@ pub async fn register( tx.commit().await?; anti_enumeration_delay(start, 150, 300).await; - // TODO: put refresh token in cookie + + set_refresh_cookie(&cookies, &refresh_plain); + Ok(Json(AuthResponse { access_token: access_token, - refresh_token: refresh_plain, })) } pub async fn refresh(state: &AppState, cookies: Cookies) -> Result<(), AppError> { todo!() } + +const REFRESH_COOKIE_NAME: &str = "refresh_token"; +fn set_refresh_cookie(cookies: &Cookies, token: &str) { + let cookie = Cookie::build((REFRESH_COOKIE_NAME, token.to_owned())) + .http_only(true) + .secure(true) + .same_site(tower_cookies::cookie::SameSite::Strict) + .path("/") + .max_age(time::Duration::days(7)) + .build(); + + cookies.add(cookie); +} + +fn get_refresh_cookie(cookies: &Cookies) -> Option { + cookies + .get(REFRESH_COOKIE_NAME) + .map(|c| c.value().to_string()) +}