#jwt #kvarn #token #cookies #web-server

kvarn-auth

Kvarn 的 JWT 认证

2 个不稳定版本

0.2.0 2024年2月4日
0.1.0 2022年8月29日

#173认证


用于 moella

Apache-2.0GPL-2.0-or-later

120KB
2.5K SLoC

kvarn-auth

一个用于 Kvarn 的快速、简单、可自定义的认证扩展。不可能自伤!

提供基于 JWT 的易于使用的认证助手,支持持久登录和验证服务器。

您提供一个异步回调,它给用户一定的授权级别。您可以根据 serde 返回任何结构化数据。JWT 会自动续订,因为服务器存储了一个凭证 cookie(使用服务器的私钥加密)。一切都可以配置。

⚠️ 注意:这个 crate 还未经过审计。我使用的所有依赖都经过了审计。自行承担风险。

不过,我个人确实在生产系统中使用这个。

前端使用

提供了一个用于登录、登出和获取用户当前状态的微型 JS 库。请查看它及其文档以获取更多详细信息。

验证服务器

这个库的一个重要特性是验证服务器。这使得可以将 kvarn-auth 部署到多个不同的物理服务器,而不必共享可以签发任何人的私钥。这是通过使用快速非对称加密实现的。有关更多信息,请参阅 ecdsa_sk

持久登录

除了常用的 JWT cookie 之外,kvarn-auth 还发送一个凭证 cookie。它包含使用服务器的密钥/私钥加密的用户凭证。当 JWT 过期时,这允许自动续订(使用 Kvarn 的优秀扩展系统)。凭证 cookie 被加密,以防止 XSS 攻击窃取用户的密码(用户可能在其他网站上重复使用),这是帮助用户的努力。

您可以通过启用 Builder::with_force_relog_on_ip_change 使任何 cookie 窃取都变得无用。我们将用户的 IP 嵌入 JWT 和凭证中,并且仅当 IP 相同时才允许它们。这可能会让用户感到烦恼(特别是如果您的用户群体主要是移动用户),但大大降低了账户被盗的风险。所以,可能用于银行 :)

版本

  • 0.1.x - kvarn v0.5
  • 0.2.x - kvarn v0.6

示例

# use kvarn::prelude::*;
// please use a strong random secret (>1024bits of entropy to be safe)
let secret = b"this secret protects all the JWTs and the credentials".to_vec();
let mut accounts: HashMap<String, String> = HashMap::new();
accounts.insert("icelk".into(), "password".into());
let auth_config = kvarn_auth::Builder::new()
    // the authentication's scope is limited to routes starting with `/demo/`.
    .with_cookie_path("/demo/")
    .with_auth_page_name("/demo/auth")
    // according to Kvarn's internal redirects, `/demo/login.` is shorthand for `/demo/login.html`
    .with_show_auth_page_when_unauthorized("/demo/login.")
    .build::<(), _, _>(
        move |user, password, _addr, _req| {
            let v = if accounts.get(user).map_or(false, |pass| pass == password) {
                kvarn_auth::Validation::Authorized(kvarn_auth::AuthData::None)
            } else {
                kvarn_auth::Validation::Unauthorized
            };
            core::future::ready(v)
        },
        kvarn_auth::CryptoAlgo::EcdsaP256 { secret },
    );

let mut extensions = kvarn::Extensions::new();

auth_config.mount(&mut extensions);
let login_status = auth_config.login_status();

extensions.add_prepare_single(
    "/demo/api",
    prepare!(
    req,
    host,
    _path,
    addr,
    move |login_status: kvarn_auth::LoginStatusClosure<()>| {
        let auth_data =
            if let kvarn_auth::Validation::Authorized(ad) =
                login_status(req, addr)
        {
            ad
        } else {
            return default_error_response(
                StatusCode::UNAUTHORIZED,
                host,
                Some("log in at `/demo/login.html`"),
            )
            .await;
        };
        // continue with your API, with a guarantee
        FatResponse::no_cache(Response::new(Bytes::new()))
    }),
);

依赖项

约11-22MB
约299K SLoC