#jwt #session-management #session #security #management #password-hashing

jwtvault

高度灵活的库,用于管理和编排JWT工作流程

12个版本 (6个重大更新)

0.7.0 2020年5月8日
0.6.1 2020年3月14日
0.5.0 2020年2月29日
0.4.2 2020年2月23日
0.1.2 2020年2月15日

#507 in 身份验证

每月25次下载

MIT许可证

130KB
2.5K SLoC

JWT Vault

高度灵活的库,用于管理和编排JWT工作流程

Build Status Build status codecov Version RepoSize Crates.io Crates.io Crates.io Contributors Gitter

示例 | 网站 | 聊天

待办事项

  • 添加更多示例
  • 提高覆盖率

功能

  • 管理并编排JWT用于用户登录、登出和续订
    • 选项1:DynamicVault(使用动态分发,但需要较少的样板代码)
    • 选项2:DefaultVault(使用静态分发,但需要较多的样板代码)
  • 异步就绪
  • 易于开始
  • 无不安全代码
  • 在稳定版Rust上运行
  • 使用Argon(见视频
  • 库方法(不需要运行时)
  • 支持可插拔组件
  • 在刷新令牌续订时无效化旧刷新令牌
  • 在续订新认证令牌时无效化旧认证令牌
  • 不允许交叉喂养
  • 处理认证令牌过期时的雷霆群问题
  • 与任何Web服务器、任何密码散列和任何后端(这里)一起工作
  • 带有actixpostgres的完整功能webserver示例

快速入门

先决条件

 [dependencies]
 jwtvault = "*"
$ curl https://raw.githubusercontent.com/sgrust01/jwtvault/master/generate_certificates.sh > ./generate_certificates.sh
$ chmod 700 generate_certificates.sh && ./generate_certificates.sh

选项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();
}

选项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();
}

工作流程

  • 开始使用login用户密码

    • 成功登录后,将向用户提供JWT对(认证/刷新)

    • 认证令牌用于访问任何资源

    • 刷新令牌用于在过期时续订认证令牌

  • 使用 resolve_session_from_client_authentication_token用户认证令牌 来恢复用户会话

  • 使用 renew用户刷新令牌 来生成新的认证令牌

  • 使用 logout用户认证令牌 将移除与用户关联的所有令牌

  • 使用辅助函数 continue_generate_temporary_token 为用户生成临时令牌

    • 临时令牌创建临时会话,不会破坏原始会话信息
    • 临时令牌不能用于登录/登出/续签/撤销原始会话
    • 临时令牌没有刷新密钥(这是故意的),以避免令牌刷新
    • 注意: DynamicVault 用户可以直接使用实例方法 generate_temporary_token
    • 典型用例: 忘记密码
  • 使用辅助函数 resolve_temporary_session_from_client_authentication_token 来恢复用户临时会话

    • 临时会话是隔离的实例,因此没有原始会话的指纹
    • 典型用例: 重置密码

依赖项

~15–21MB
~451K SLoC