{"id":13621350,"url":"https://github.com/sgrust01/jwtvault","last_synced_at":"2025-04-15T01:32:18.457Z","repository":{"id":62441487,"uuid":"240643032","full_name":"sgrust01/jwtvault","owner":"sgrust01","description":"Highly flexible library to manage and orchestrate JWT workflow","archived":false,"fork":false,"pushed_at":"2020-05-25T06:51:06.000Z","size":167,"stargazers_count":68,"open_issues_count":0,"forks_count":6,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-07T02:36:35.509Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sgrust01.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-02-15T04:11:59.000Z","updated_at":"2024-06-21T10:02:39.000Z","dependencies_parsed_at":"2022-11-01T22:01:29.832Z","dependency_job_id":null,"html_url":"https://github.com/sgrust01/jwtvault","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgrust01%2Fjwtvault","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgrust01%2Fjwtvault/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgrust01%2Fjwtvault/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgrust01%2Fjwtvault/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sgrust01","download_url":"https://codeload.github.com/sgrust01/jwtvault/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223635641,"owners_count":17177186,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T21:01:04.989Z","updated_at":"2024-11-08T08:30:39.417Z","avatar_url":"https://github.com/sgrust01.png","language":"Rust","funding_links":[],"categories":["Libraries","库 Libraries"],"sub_categories":["Authentication","身份验证 Authentication"],"readme":"\u003cdiv align=\"center\"\u003e\n \u003cp\u003e\u003ch1\u003eJWT Vault\u003c/h1\u003e \u003c/p\u003e\n  \u003cp\u003e\u003cstrong\u003eHighly flexible library to manage and orchestrate JWT workflow\u003c/strong\u003e \u003c/p\u003e\n  \u003cp\u003e\n  \n[![Build Status](https://travis-ci.org/sgrust01/jwtvault.svg?branch=master)](https://travis-ci.org/sgrust01/jwtvault) \n[![Build status](https://ci.appveyor.com/api/projects/status/x903r0kr9sivyus2?svg=true)](https://ci.appveyor.com/project/sgrust01/jwtvault)\n[![codecov](https://codecov.io/gh/sgrust01/jwtvault/branch/master/graph/badge.svg)](https://codecov.io/gh/sgrust01/jwtvault)\n[![Version](https://img.shields.io/badge/rustc-1.39+-blue.svg)](https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html) \n![RepoSize](https://img.shields.io/github/repo-size/sgrust01/jwtvault)\n![Crates.io](https://img.shields.io/crates/l/jwtvault)\n![Crates.io](https://img.shields.io/crates/v/jwtvault)\n![Crates.io](https://img.shields.io/crates/d/jwtvault)\n![Contributors](https://img.shields.io/github/contributors/sgrust01/jwtvault)\n[![Gitter](https://badges.gitter.im/jwtvault/community.svg)](https://gitter.im/jwtvault/community?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge)\n[![Say Thanks!](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://saythanks.io/to/sgrust01@gmail.com)\n\u003c/p\u003e\n\n  \u003ch3\u003e\n    \u003ca href=\"https://github.com/sgrust01/jwtvault_examples\"\u003eExamples\u003c/a\u003e\n    \u003cspan\u003e | \u003c/span\u003e\n    \u003ca href=\"#\"\u003eWebsite\u003c/a\u003e\n    \u003cspan\u003e | \u003c/span\u003e\n    \u003ca href=\"https://gitter.im/jwtvault/community?utm_source=share-link\u0026utm_medium=link\u0026utm_campaign=share-link\"\u003eChat\u003c/a\u003e\n  \u003c/h3\u003e\n\u003c/div\u003e\n\n## TODO\n* Add more examples\n* Improve coverage\n\n## Features\n* Manages \u0026 Orchestrates JWT for user login, logout \u0026 renew\n    * Option 1: DynamicVault (uses dynamic dispatch but requires considerable less boiler-plate code)\n    * Option 2: DefaultVault (uses static dispatch but requires considerable more boiler-plate code)\n* Async ready\n* Easy start\n* No un-safe code\n* Runs on stable rust\n* Uses [Argon](https://en.wikipedia.org/wiki/Argon2) (see [video](https://www.youtube.com/watch?v=Sc3aHMCc4h0\u0026t=339s))\n* Library approach (Requires no runtime)\n* Supports plugable components\n* Invalidates old refresh upon new refresh token renewal\n* Invalidates old authentication upon new authentication token renewal\n* Cross feeding not allowed\n* Handles Thundering herd problem upon authentication token expiry\n* Works with any web-server, any password hashing, and any backend ([here](https://github.com/sgrust01/jwtvault_examples))\n* Fully functional \n[webserver](https://github.com/sgrust01/jwtvault_examples#example-5-webserver) \nwith [actix](https://github.com/actix/actix) \nand [postgres](https://github.com/sfackler/rust-postgres/tree/master/tokio-postgres)\n\n\n## Quickstart\n\n### Prerequisite:\n\n\n ```toml\n  [dependencies]\n  jwtvault = \"*\"\n```\n\n ```shell script\n$ curl https://raw.githubusercontent.com/sgrust01/jwtvault/master/generate_certificates.sh \u003e ./generate_certificates.sh\n```\n\n```shell script\n$ chmod 700 generate_certificates.sh \u0026\u0026 ./generate_certificates.sh\n```\n\n#### Option 1: DynamicVault\n\n```rust\nuse std::collections::HashMap;\nuse std::collections::hash_map::DefaultHasher;\n\nuse jwtvault::prelude::*;\n\nfn main() {\n    let hasher = ArgonPasswordHasher::default();\n    // User: John Doe\n    let user_john = \"john_doe\";\n    let password_for_john = \"john\";\n    // Save value 'hashed_password_for_john' to persistent storage\n    // This is more relevant during user signup/password reset\n    let hashed_password_for_john = hasher.hash_user_password(user_john, password_for_john).unwrap();\n\n    // User: Jane Doe\n    let user_jane = \"jane_doe\";\n    let password_for_jane = \"jane\";\n    // Save 'hashed_password_for_jane' to persistent storage\n    // This is more relevant during user signup/password reset\n    let hashed_password_for_jane = hasher.hash_user_password(user_jane, password_for_jane).unwrap();\n\n    let mut users = HashMap::new();\n\n    // load users and their password from database/somewhere\n    users.insert(user_john.to_string(), hashed_password_for_john.to_string());\n    users.insert(user_jane.to_string(), hashed_password_for_jane.to_string());\n\n    // Setup app users\n    let login = LoginInfo::new(users);\n\n     // Initialize vault\n    let mut vault = DynamicVault::default(Box::new(login));\n\n    // John needs to login now\n    let token = block_on(vault.login(\n        user_john,\n        password_for_john,\n        None,\n        None,\n    ));\n    let token = token.ok().unwrap();\n    // When John presents authentication token, it can be used to restore John's session info\n    let server_refresh_token = block_on(resolve_session_from_client_authentication_token(\n        \u0026mut vault,\n        user_john,\n        token.authentication(),\n    ));\n    let server_refresh_token = server_refresh_token.ok().unwrap();\n\n    // server_refresh_token (variable) contains server method which captures client private info\n    // which never leaves the server\n    let private_info_about_john = server_refresh_token.server().unwrap();\n    let key = digest::\u003c_, DefaultHasher\u003e(user_john);\n    let data_on_server_side = private_info_about_john.get(\u0026key).unwrap();\n\n    // server_refresh_token (variable) contains client method which captures client public info\n    // which is also send back to client\n    assert!(server_refresh_token.client().is_none());\n\n    // Check out the data on client and server which are public and private respectively\n    println!(\"[Private] John Info: {}\",\n             String::from_utf8_lossy(data_on_server_side.as_slice()).to_string());\n\n    // lets renew authentication token\n    let new_token = block_on(vault.renew(\n        user_john,\n        token.refresh(),\n        None,\n    ));\n    let new_token = new_token.ok().unwrap();\n\n    // When John presents new authentication token it can be used to restore session info\n    let result = block_on(resolve_session_from_client_authentication_token(\n        \u0026mut vault,\n        user_john,\n        new_token.as_str(),\n    ));\n    let _ = result.ok().unwrap();\n}\n```\n\n#### Option 2: DefaultVault\n\n```rust\nuse jwtvault::prelude::*;\nuse std::collections::HashMap;\nuse std::collections::hash_map::DefaultHasher;\n\n\nfn main() {\n\n    let mut users = HashMap::new();\n\n    let loader = CertificateManger::default();\n\n    // User: John Doe\n    let user_john = \"john_doe\";\n    let password_for_john = \"john\";\n\n    // This should ideally be pre-computed during user sign-up/password reset/change password\n    let hashed_password_for_john = hash_password_with_argon(\n        password_for_john,\n        loader.password_hashing_secret().as_str(),\n    ).unwrap();\n\n    // User: Jane Doe\n    let user_jane = \"jane_doe\";\n    let password_for_jane = \"jane\";\n\n    // This should ideally be pre-computed during user sign-up/password reset/change password\n    let hashed_password_for_jane = hash_password_with_argon(\n        password_for_jane,\n        loader.password_hashing_secret().as_str(),\n    ).unwrap();\n\n    // load users and their (argon hashed) password from database/somewhere\n    users.insert(user_john.to_string(), hashed_password_for_john);\n    users.insert(user_jane.to_string(), hashed_password_for_jane);\n\n    // Initialize vault\n    let mut vault = DefaultVault::new(loader, users, false);\n\n    // John needs to login now\n    let token = block_on(vault.login(\n        user_john,\n        password_for_john,\n        None,\n        None,\n    ));\n    let token = token.ok().unwrap();\n    // When John presents authentication token, it can be used to restore John's session info\n    let server_refresh_token = block_on(resolve_session_from_client_authentication_token(\n        \u0026mut vault,\n        user_john,\n        token.authentication(),\n    ));\n    let server_refresh_token = server_refresh_token.ok().unwrap();\n\n    // server_refresh_token (variable) contains server method which captures client private info\n    // which never leaves the server\n    let private_info_about_john = server_refresh_token.server().unwrap();\n    let key = digest::\u003c_, DefaultHasher\u003e(user_john);\n    let data_on_server_side = private_info_about_john.get(\u0026key).unwrap();\n\n    // server_refresh_token (variable) contains client method which captures client public info\n    // which is also send back to client\n    assert!(server_refresh_token.client().is_none());\n\n    // Check out the data on client and server which are public and private respectively\n    println!(\"[Private] John Info: {}\",\n             String::from_utf8_lossy(data_on_server_side.as_slice()).to_string());\n\n    // lets renew authentication token\n    let new_token = block_on(vault.renew(\n        user_john,\n        token.refresh(),\n        None,\n    ));\n    let new_token = new_token.ok().unwrap();\n\n    // When John presents new authentication token it can be used to restore session info\n    let result = block_on(resolve_session_from_client_authentication_token(\n        \u0026mut vault,\n        user_john,\n        new_token.as_str(),\n    ));\n    let _ = result.ok().unwrap();\n}\n```\n\n\n# Workflows\n\n* To begin use `login` with ___***user***___ and ___***password***___\n\n    * Upon successful login is provides user will be provided with JWT pair (authentication/refresh)\n\n    * Authentication token is then provided to access any resources\n\n    * Refresh token is used to renew an authentication token upon expiry\n\n* Use `resolve_session_from_client_authentication_token` with ___***user***___ and ___***authentication_token***___ to restore user session\n\n* Use `renew` with ___***user***___ and ___***refresh_token***___ to generate new authentication token\n\n* Use `logout` with ___***user***___ and ___***authentication_token***___ will remove all tokens associated with the user\n\n* Use helper `continue_generate_temporary_token` to generate temporary token for user\n    * Temporary token creates temporary session, and does not corrupt the original session info\n    * Temporary token cannot be used to login/logout/renew/revoke original session\n    * Temporary token does not have refresh key (this is intentional), to avoid token refresh\n    * __**Note:**__ DynamicVault users can directly use instance method `generate_temporary_token`\n    * __**Typical use-case:**__ Forgot Password\n    \n* Use helper `resolve_temporary_session_from_client_authentication_token` to restore user temporary session\n    * Temporary session are isolated instance, and thus has no fingerprints of the originals session\n    * __**Typical use-case:**__ Reset Password ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsgrust01%2Fjwtvault","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsgrust01%2Fjwtvault","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsgrust01%2Fjwtvault/lists"}