7个版本 (破坏性)

0.6.1 2024年8月13日
0.5.0 2024年1月26日
0.4.3 2023年1月15日
0.4.1 2021年2月12日
0.1.0 2021年2月10日

#316 in 身份验证

Download history 2/week @ 2024-05-09 12/week @ 2024-05-16 29/week @ 2024-05-23 31/week @ 2024-05-30 24/week @ 2024-06-06 20/week @ 2024-06-13 29/week @ 2024-06-20 13/week @ 2024-06-27 29/week @ 2024-07-04 24/week @ 2024-07-11 34/week @ 2024-07-18 30/week @ 2024-07-25 28/week @ 2024-08-01 129/week @ 2024-08-08

229 每月下载
用于 4 个crate (3 直接)

Apache-2.0

25KB
491

提供了一个 Actor 和可序列化 Token 结构,支持使用自定义有效载荷验证JSON Web Tokens。有关JWT规范更多信息,请参阅 jwt.io

提供的 Actor 使用 ECDSA 算法签名令牌(使用 ed25519_dalek crate)。

与其他JWT实现不同,该库允许递归的 Token

请注意,如果在令牌链中指定相同的 (host, actor) 对多次,只有最新的一次由 Claims::get 返回。

示例

use rjwt::*;

#[derive(Clone)]
struct Resolver {
    hostname: String,
    actors: HashMap<String, Actor<String>>,
    peers: Vec<Self>,
}
// ...

#[async_trait]
impl Resolve for Resolver {
    type HostId = String;
    type ActorId = String;
    type Claims = String;

    async fn resolve(&self, host: &Self::HostId, actor_id: &Self::ActorId) -> Result<Actor<Self::ActorId>, Error> {
        if host == &self.hostname {
            self.actors.get(actor_id).cloned().ok_or_else(|| Error::fetch(actor_id))
        } else if let Some(peer) = self.peers.iter().filter(|p| &p.hostname == host).next() {
            peer.resolve(host, actor_id).await
        } else {
            Err(Error::fetch(host))
        }
    }
}

let now = SystemTime::now();

// Say that Bob is a user on example.com.
let bobs_id = "bob".to_string();
let example_dot_com = "example.com".to_string();

let actor_bob = Actor::new(bobs_id.clone());
let example = Resolver::new(example_dot_com.clone(), [actor_bob.clone()], vec![]);

// Bob makes a request through the retailer.com app.
let retailer_dot_com = "retailer.com".to_string();
let retail_app = Actor::new("app".to_string());
let retailer = Resolver::new(
    retailer_dot_com.clone(),
    [retail_app.clone()],
    vec![example.clone()]);

// The retailer.com app makes a request to Bob's bank.
let bank_account = Actor::new("bank".to_string());
let bank = Resolver::new(
    "bank.com".to_string(),
    [bank_account.clone()],
    vec![example, retailer.clone()]);

// First, example.com issues a token to authenticate Bob.
let bobs_claim = String::from("I am Bob and retailer.com may debit my bank.com account");

// This requires constructing the token...
let bobs_token = Token::new(
    example_dot_com.clone(),
    now,
    Duration::from_secs(30),
    actor_bob.id().to_string(),
    bobs_claim.clone());

// and signing it with Bob's private key.
let bobs_token = actor_bob.sign_token(bobs_token).expect("signed token");

// Then, retailer.com validates the token...
let bobs_token = block_on(retailer.verify(bobs_token.into_jwt(), now)).expect("claims");
assert!(bobs_token.claims().get(&example_dot_com, &bobs_id).expect("claim").starts_with("I am Bob"));

// and adds its own claim, that Bob owes it $1.
let retailer_claim = String::from("Bob spent $1 on retailer.com");
let retailer_token = retail_app.consume_and_sign(
    bobs_token,
    retailer_dot_com.clone(),
    retailer_claim.clone(),
    now).expect("signed token");

assert_eq!(retailer_token
    .claims()
    .get(&retailer_dot_com, retail_app.id()), Some(&retailer_claim));

assert_eq!(retailer_token
    .claims()
    .get(&example_dot_com, actor_bob.id()), Some(&bobs_claim));

// Finally, Bob's bank verifies the token...
let retailer_token_as_received = block_on(
    bank.verify(retailer_token.jwt().to_string(), now)
).expect("claims");

assert_eq!(retailer_token, retailer_token_as_received);

// to authenticate that the request came from Bob...
assert!(retailer_token_as_received
    .claims()
    .get(&example_dot_com, &bobs_id)
    .expect("claim")
    .starts_with("I am Bob and retailer.com may debit my bank.com account"));

// via retailer.com.
assert!(retailer_token_as_received
    .claims()
    .get(&retailer_dot_com, retail_app.id())
    .expect("claim")
    .starts_with("Bob spent $1"));

依赖项

~3.5–5.5MB
~110K SLoC