Rust – JWT Workflow Project
source link: https://github.com/sgrust01/jwtvault
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
TODO
- Add more examples
- Improve coverage
Features
- Manages & Orchestrates JWT for user login, logout & renew
- Option 1: DynamicVault (uses dynamic dispatch but requires considerable less boiler-plate code)
- Option 2: DefaultVault (uses static dispatch but requires considerable more boiler-plate code)
- Async ready
- Easy start
- No un-safe code
- Runs on stable rust
- Uses Argon (see video )
- Library approach (Requires no runtime)
- Supports plugable components
- Invalidates old refresh upon new refresh token renewal
- Invalidates old authentication upon new authentication token renewal
- Cross feeding not allowed
- Handles Thundering herd problem upon authentication token expiry
- Works with any web-server, any password hashing, and any backend ( here )
- Fully functional webserver with actix and postgres
Quickstart
Prerequisite:
[dependencies] jwtvault = "*"
$ curl https://raw.githubusercontent.com/sgrust01/jwtvault/master/generate_certificates.sh > ./generate_certificates.sh
$ chmod 700 generate_certificates.sh && ./generate_certificates.sh
Option 1: DynamicVault
use std::collections::HashMap; use std::collections::hash_map::DefaultHasher; use jwtvault::prelude::*; fn main() { let hasher = ArgonPasswordHasher::default(); // User: John Doe let user_john = "john_doe"; let password_for_john = "john"; // Save value 'hashed_password_for_john' to persistent storage // This is more relevant during user signup/password reset let hashed_password_for_john = hasher.hash_user_password(user_john, password_for_john).unwrap(); // User: Jane Doe let user_jane = "jane_doe"; let password_for_jane = "jane"; // Save 'hashed_password_for_jane' to persistent storage // This is more relevant during user signup/password reset let hashed_password_for_jane = hasher.hash_user_password(user_jane, password_for_jane).unwrap(); let mut users = HashMap::new(); // load users and their password from database/somewhere users.insert(user_john.to_string(), hashed_password_for_john.to_string()); users.insert(user_jane.to_string(), hashed_password_for_jane.to_string()); // Setup app users let login = LoginInfo::new(users); // Initialize vault let mut vault = DynamicVault::default(Box::new(login)); // John needs to login now let token = block_on(vault.login( user_john, password_for_john, None, None, )); let token = token.ok().unwrap(); // When John presents authentication token, it can be used to restore John's session info let server_refresh_token = block_on(resolve_session_from_client_authentication_token( &mut vault, user_john, token.authentication(), )); let server_refresh_token = server_refresh_token.ok().unwrap(); // server_refresh_token (variable) contains server method which captures client private info // which never leaves the server let private_info_about_john = server_refresh_token.server().unwrap(); let key = digest::<_, DefaultHasher>(user_john); let data_on_server_side = private_info_about_john.get(&key).unwrap(); // server_refresh_token (variable) contains client method which captures client public info // which is also send back to client assert!(server_refresh_token.client().is_none()); // Check out the data on client and server which are public and private respectively println!("[Private] John Info: {}", String::from_utf8_lossy(data_on_server_side.as_slice()).to_string()); // lets renew authentication token let new_token = block_on(vault.renew( user_john, token.refresh(), None, )); let new_token = new_token.ok().unwrap(); // When John presents new authentication token it can be used to restore session info let result = block_on(resolve_session_from_client_authentication_token( &mut vault, user_john, new_token.as_str(), )); let _ = result.ok().unwrap(); }
Option 2: DefaultVault
use jwtvault::prelude::*; use std::collections::HashMap; use std::collections::hash_map::DefaultHasher; fn main() { let mut users = HashMap::new(); let loader = CertificateManger::default(); // User: John Doe let user_john = "john_doe"; let password_for_john = "john"; // This should ideally be pre-computed during user sign-up/password reset/change password let hashed_password_for_john = hash_password_with_argon( password_for_john, loader.password_hashing_secret().as_str(), ).unwrap(); // User: Jane Doe let user_jane = "jane_doe"; let password_for_jane = "jane"; // This should ideally be pre-computed during user sign-up/password reset/change password let hashed_password_for_jane = hash_password_with_argon( password_for_jane, loader.password_hashing_secret().as_str(), ).unwrap(); // load users and their (argon hashed) password from database/somewhere users.insert(user_john.to_string(), hashed_password_for_john); users.insert(user_jane.to_string(), hashed_password_for_jane); // Initialize vault let mut vault = DefaultVault::new(loader, users, false); // John needs to login now let token = block_on(vault.login( user_john, password_for_john, None, None, )); let token = token.ok().unwrap(); // When John presents authentication token, it can be used to restore John's session info let server_refresh_token = block_on(resolve_session_from_client_authentication_token( &mut vault, user_john, token.authentication(), )); let server_refresh_token = server_refresh_token.ok().unwrap(); // server_refresh_token (variable) contains server method which captures client private info // which never leaves the server let private_info_about_john = server_refresh_token.server().unwrap(); let key = digest::<_, DefaultHasher>(user_john); let data_on_server_side = private_info_about_john.get(&key).unwrap(); // server_refresh_token (variable) contains client method which captures client public info // which is also send back to client assert!(server_refresh_token.client().is_none()); // Check out the data on client and server which are public and private respectively println!("[Private] John Info: {}", String::from_utf8_lossy(data_on_server_side.as_slice()).to_string()); // lets renew authentication token let new_token = block_on(vault.renew( user_john, token.refresh(), None, )); let new_token = new_token.ok().unwrap(); // When John presents new authentication token it can be used to restore session info let result = block_on(resolve_session_from_client_authentication_token( &mut vault, user_john, new_token.as_str(), )); let _ = result.ok().unwrap(); }
Workflows
-
To begin use
login
with user and password-
Upon successful login is provides user will be provided with JWT pair (authentication/refresh)
-
Authentication token is then provided to access any resources
-
Refresh token is used to renew an authentication token upon expiry
-
-
Use
resolve_session_from_client_authentication_token
with user and authentication_token to restore user session -
Use
renew
with user and refresh_token to generate new authentication token -
Use
logout
with user and authentication_token will remove all tokens associated with the user
Recommend
-
3
GitHub project contribution workflow Dec 26, 2013 I only recently started contributing to other projects on GitHub, and while I am quite comfortable with the normal git workflow, I had nev...
-
3
Your Basic Rust Workflow For the Noobie Rustacean May 8, 2020 Note 1: The term Rustacean is the ter...
-
18
Visual Studio Code project manager using Alfred workflowPublished: 2021.03.26 | 1 minutes readI spend a lot of time using Visual Studio Code, jumping from one project to another. To make it more efficient, I would love to have...
-
3
My project grading workflow Grading sucks. Even for a well crafted assignment it takes time and rarely does it give you the same insights into your students as you can get from just observing them and working with them, at l...
-
4
Writing Rust is pretty neat. But you know what's even neater? Continuously testing Rust, releasing Rust, and eventually, shipping Rust to production. And for that, we want more than plug-in for a code...
-
2
Flutter: Speed up your workflow with a custom project creation script For some reason, starting a new project always brings a small feeling of inherent joy and excitement. There is something about a clean slate that is just very comfo...
-
7
This article was published as a part of the Data Science Blogathon. Introduction Asides from dedication to discovery and explorat...
-
4
Why do you need JWT in your ASP.NET Core project?NotificationsTime to update all your call-to-actions to gain more traffic 🚗🛵🚌02/06/2024The WINNERS...
-
1
Rust中实现JWT身份验证 我们将讨论如何在 Rust 中使用 JSON Web Tokens (JWT) 实现身份...
-
4
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK