diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 607147a0b..4dcc0fa4c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,7 +54,7 @@ Just clone the project and run `cargo test`. You can see how we test in [.github/workflows](.github/workflows/) #### Snapshots -To update/create a snapshots we are using [insta](https://github.com/mitsuhiko/insta). all you need to do is install insta and run the following command: +To update/create a snapshots we are using [insta](https://github.com/mitsuhiko/insta). all you need to do is install insta (cargo install cargo-insta) and run the following command: ``` cargo insta test --review ``` diff --git a/docs-site/content/docs/getting-started/tour/index.md b/docs-site/content/docs/getting-started/tour/index.md index 7f211ba9e..990351953 100644 --- a/docs-site/content/docs/getting-started/tour/index.md +++ b/docs-site/content/docs/getting-started/tour/index.md @@ -198,6 +198,7 @@ The response includes a JWT token for authentication, user ID, name, and verific "token": "...", "pid": "2b20f998-b11e-4aeb-96d7-beca7671abda", "name": "Loco user", + "claims": null "is_verified": false } ``` diff --git a/examples/demo/src/models/users.rs b/examples/demo/src/models/users.rs index 640c139ae..8fbf27d1d 100644 --- a/examples/demo/src/models/users.rs +++ b/examples/demo/src/models/users.rs @@ -2,6 +2,7 @@ use async_trait::async_trait; use chrono::offset::Local; use loco_rs::{auth::jwt, hash, prelude::*}; use serde::{Deserialize, Serialize}; +use serde_json::json; use uuid::Uuid; pub use super::_entities::users::{self, ActiveModel, Entity, Model}; @@ -213,7 +214,11 @@ impl super::_entities::users::Model { /// /// when could not convert user claims to jwt token pub fn generate_jwt(&self, secret: &str, expiration: &u64) -> ModelResult { - Ok(jwt::JWT::new(secret).generate_token(expiration, self.pid.to_string())?) + Ok(jwt::JWT::new(secret).generate_token( + expiration, + self.pid.to_string(), + Some(json!({"Roll": "Administrator"})), + )?) } } diff --git a/src/auth/jwt.rs b/src/auth/jwt.rs index 83cd68f9b..a32c424b8 100644 --- a/src/auth/jwt.rs +++ b/src/auth/jwt.rs @@ -8,6 +8,7 @@ use jsonwebtoken::{ EncodingKey, Header, TokenData, Validation, }; use serde::{Deserialize, Serialize}; +use serde_json::Value; /// Represents the default JWT algorithm used by the [`JWT`] struct. const JWT_ALGORITHM: Algorithm = Algorithm::HS512; @@ -17,6 +18,7 @@ const JWT_ALGORITHM: Algorithm = Algorithm::HS512; pub struct UserClaims { pub pid: String, exp: usize, + claims: Option, } /// Represents the JWT configuration and operations. @@ -60,13 +62,18 @@ impl JWT { /// ```rust /// use loco_rs::auth; /// - /// auth::jwt::JWT::new("PqRwLF2rhHe8J22oBeHy").generate_token(&604800, "PID".to_string()); + /// auth::jwt::JWT::new("PqRwLF2rhHe8J22oBeHy").generate_token(&604800, "PID".to_string(), None); /// ``` - pub fn generate_token(&self, expiration: &u64, pid: String) -> JWTResult { + pub fn generate_token( + &self, + expiration: &u64, + pid: String, + claims: Option, + ) -> JWTResult { #[allow(clippy::cast_possible_truncation)] let exp = (get_current_timestamp() + expiration) as usize; - let claims = UserClaims { pid, exp }; + let claims = UserClaims { pid, exp, claims }; let token = encode( &Header::new(self.algorithm), @@ -107,16 +114,24 @@ mod tests { use insta::{assert_debug_snapshot, with_settings}; use rstest::rstest; + use serde_json::json; use super::*; #[rstest] - #[case("valid token", 60)] - #[case("token expired", 1)] + #[case("valid token", 60, None)] + #[case("token expired", 1, None)] + #[case("valid token and custom claims", 60, Some(json!({})))] #[tokio::test] - async fn can_generate_token(#[case] test_name: &str, #[case] expiration: u64) { + async fn can_generate_token( + #[case] test_name: &str, + #[case] expiration: u64, + #[case] claims: Option, + ) { let jwt = JWT::new("PqRwLF2rhHe8J22oBeHy"); - let token = jwt.generate_token(&expiration, "pid".to_string()).unwrap(); + let token = jwt + .generate_token(&expiration, "pid".to_string(), claims) + .unwrap(); std::thread::sleep(std::time::Duration::from_secs(3)); with_settings!({filters => vec![ diff --git a/src/auth/snapshots/loco_rs__auth__jwt__tests__valid token and custom claims.snap b/src/auth/snapshots/loco_rs__auth__jwt__tests__valid token and custom claims.snap new file mode 100644 index 000000000..654ddac07 --- /dev/null +++ b/src/auth/snapshots/loco_rs__auth__jwt__tests__valid token and custom claims.snap @@ -0,0 +1,30 @@ +--- +source: src/auth/jwt.rs +assertion_line: 133 +expression: jwt.validate(&token) +--- +Ok( + TokenData { + header: Header { + typ: Some( + "JWT", + ), + alg: HS512, + cty: None, + jku: None, + jwk: None, + kid: None, + x5u: None, + x5c: None, + x5t: None, + x5t_s256: None, + }, + claims: UserClaims { + pid: "pid", + exp: EXP, + claims: Some( + Object {}, + ), + }, + }, +) diff --git a/src/auth/snapshots/loco_rs__auth__jwt__tests__valid token.snap b/src/auth/snapshots/loco_rs__auth__jwt__tests__valid token.snap index 33e03bafe..d7255f840 100644 --- a/src/auth/snapshots/loco_rs__auth__jwt__tests__valid token.snap +++ b/src/auth/snapshots/loco_rs__auth__jwt__tests__valid token.snap @@ -21,6 +21,7 @@ Ok( claims: UserClaims { pid: "pid", exp: EXP, + claims: None, }, }, )