Skip to content

Commit

Permalink
Merge pull request #578 from Muxoid/role_based_auth
Browse files Browse the repository at this point in the history
Adding a custom claims Option<serde_json::Value> to the UserClaims struct.
  • Loading branch information
jondot authored May 20, 2024
2 parents e5d6f1a + 0158d02 commit ec2ba72
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 9 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down
1 change: 1 addition & 0 deletions docs-site/content/docs/getting-started/tour/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
```
Expand Down
7 changes: 6 additions & 1 deletion examples/demo/src/models/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<String> {
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"})),
)?)
}
}

Expand Down
29 changes: 22 additions & 7 deletions src/auth/jwt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -17,6 +18,7 @@ const JWT_ALGORITHM: Algorithm = Algorithm::HS512;
pub struct UserClaims {
pub pid: String,
exp: usize,
claims: Option<Value>,
}

/// Represents the JWT configuration and operations.
Expand Down Expand Up @@ -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<String> {
pub fn generate_token(
&self,
expiration: &u64,
pid: String,
claims: Option<Value>,
) -> JWTResult<String> {
#[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),
Expand Down Expand Up @@ -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<Value>,
) {
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![
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {},
),
},
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Ok(
claims: UserClaims {
pid: "pid",
exp: EXP,
claims: None,
},
},
)

0 comments on commit ec2ba72

Please sign in to comment.