#jwt #openid-connect #jose

bbjwt

一个简单易用、文档完善的JWT验证库,主要用于验证OpenID Connect ID Tokens

6个版本

0.4.1 2024年8月20日
0.4.0 2024年1月30日
0.3.0 2023年11月7日
0.2.2 2023年6月6日
0.2.1 2023年1月13日

密码学类别中排名第334

Download history 11/week @ 2024-05-02 3/week @ 2024-05-23 19/week @ 2024-05-30 19/week @ 2024-06-06 9/week @ 2024-06-13 3/week @ 2024-06-20 4/week @ 2024-06-27 36/week @ 2024-07-04 12/week @ 2024-07-11 102/week @ 2024-07-18 40/week @ 2024-07-25 67/week @ 2024-08-01 54/week @ 2024-08-08 160/week @ 2024-08-15

每月下载量339

MIT许可

73KB
1K SLoC

用于basebox(以及可能的其他)的JWT验证库

Build Status crates.io docs.rs

概述

这个库是为了提供一种简单、直接且可靠的验证JWT的方式,这些JWT通过从URL加载的公钥集合进行验证。我们在basebox使用它来验证OpenID Connect ID Tokens(它们是JWT),使用OpenID服务器(例如Keycloak)发布的公钥集合。

它提供了以下功能

  • 从URL下载一组公钥(一个JSON Web Key Set
  • 提供更新密钥集的入口点,如果需要的话
  • 解析JWT并使用下载的密钥集中的密钥进行验证。

就这样。

此外,我们设计bbjwt以满足以下要求

  • 无不安全代码
  • 从不panic
  • API中没有生命周期指定符
  • 异步
  • 线程安全

算法支持

下表显示了bbjwt支持的签名算法,以及它们在JWK、JWT等中的使用信息。

注意 请注意,bbjwt 0.3.0.*中已移除Ed448和ES512签名支持。

名称 JOSE "kty" JOSE "alg" JOSE "curve"
RSA256 RSA RS256
RSA384 RSA RS384
RSA512 RSA RS512
ES256 EC ES256 P-256
ES384 EC ES384 P-384
Ed25519 OKP EdDSA Ed25519

不支持加密JWT。

顺便说一下,如果可以选择,请使用Ed25519。它是安全和快速的。

为什么还需要另一个Rust JWT验证库?

我们尝试了各种其他的Rust JWT库,但没有一个适合我们。问题包括复杂的API、缺乏文档和/或功能。这是我们尝试做得更好的尝试:-)

使用方法

为了验证JWT,您必须拥有发行者的公钥。使用bbjwt,您可以通过从发行者提供的URL下载它们,或者从本地缓冲区/文件中加载它们来获取它们。

从URL下载公钥

请参阅以下示例

use bbjwt::KeyStore;

#[tokio::main]
async fn main() {

  // bbjwt provides a function to determine the public keyset URL by loading discovery
  // info from the issuer; this is common for OpenID Connect servers.

  // If you are using Keycloak, you can use this convenience function to get the discovery
  // endpoint URL; all you need is the base URL and the realm name:
  let discovery_url = KeyStore::keycloak_discovery_url(
    "https://server.tld", "testing"
  ).unwrap();

  // If you're not using Keycloak, the URL might be different.
  let discovery_url = "https://idp-host.tld/.well-known/discovery";

  // Call IdP's discovery endpoint to query the keyset URL; this is a common feature on
  // OpenID Connect servers.
  let keyset_url = KeyStore::idp_certs_url(discovery_url).await.unwrap();

  // Now we can load the keys into a new KeyStore:
  let keystore = KeyStore::new_from_url(&keyset_url).await.unwrap();
}

使用内存中的公钥

在从本地文件或缓冲区加载公钥时,您可以加载一个JWK JSON或PEM编码的文本。JWK包含识别密钥类型所需的所有信息,但针对PEM,您需要使用与密钥类型对应的函数。

请参阅以下示例

use bbjwt::{KeyStore, KeyAlgorithm, EcCurve};

#[tokio::main]
async fn main() {
  // Create an empty keystore
  let mut keystore = KeyStore::new().await.unwrap();

  // Load public key from a JWK JSON; see
  // https://openid.net/specs/draft-jones-json-web-key-03.html#ExampleJWK
  let json_key = r#"
  {
    "kty":"RSA",
    "use":"sig",
    ... abbreviated ...,
  }"#;
  // Add the key
  keystore.add_key(json_key).await;

  let pem_key = r#"-----BEGIN PUBLIC KEY-----
..."#;

  // Load a RSA key from a PEM buffer
  keystore.add_rsa_pem_key(
    pem_key,
    Some("key-rsa"),
    KeyAlgorithm::RS256
  ).await.unwrap();

  // Load a EC key from a PEM buffer
  keystore.add_ec_pem_key(
    pem_key,
    Some("key-ec"),
    EcCurve::P256,
    KeyAlgorithm::ES256
  ).await.unwrap();

  // Load EdDSA key from a PEM buffer
  keystore.add_ec_pem_key(
    pem_key,
    Some("key-ed"),
    EcCurve::Ed25519,
    KeyAlgorithm::EdDSA
  ).await.unwrap();

  // You can add more keys; in this case, the keys should have an ID and the JWT to be
  // validated should have a "kid" claim. Otherwise, bbjwt uses the first key in the set.
}

验证JWT

JWT以Base64编码的字符串传递;有关此格式的详细信息,请参阅例如https://jwt.node.org.cn

要验证JWT,您需要将base64编码的JWT和一个ValidationStep向量传递给validate_jwt。bbjwt提供了一个名为default_validations的便利函数来创建一个默认验证步骤向量。

如果JWT有效,validate_jwt将返回JWT包含的所有声明(头部和负载)。

示例

use bbjwt::{KeyStore, default_validations, validate_jwt};

#[tokio::main]
async fn main() {
  // Create a keystore; see examples above
  let keystore = KeyStore::new_from_url("https://server.tld/keyset").await.unwrap();

  // Validate a JWT
  let jwt = validate_jwt(
    "<Base64 encoded JWT>",
    &default_validations(
      // required value for the "iss" claim
      "https://idp.domain.url/realm/testing",
      None,
      None),
    &keystore
  )
  .await
  .unwrap();

  // Read some claims (JWT fields)
  assert_eq!(jwt.claims["nonce"].as_str().unwrap(), "UZ1BSZFvy7jKkj1o9p3r7w");
}

版权(c)2022 basebox GmbH,版权所有。

许可证:MIT

用❤️和Emacs制作 :-)

依赖项

~15–28MB
~525K SLoC