Git Product home page Git Product logo

Comments (8)

w0rm avatar w0rm commented on September 25, 2024 1

Hi @naftulikay! In your original message you mentioned that you were going to use jwk_set of public keys, however the solution you provided was for a single jwk, and you created the token_verifier outside of the selector closure and referenced it. I am fairly new to rust, and I cannot figure out how to implement something like this that dynamically selects a verifier based on jwk:

let (_verified_claims, _verified_header) =
       jwt::decode_with_verifier_in_jwk_set(token, &jwk_set, |jwk| match jwk.algorithm() {
            Some("RS256") => Ok(Some(&RS256.verifier_from_jwk(jwk)?)),
            Some("P-521") => Ok(Some(&ES512.verifier_from_jwk(jwk)?)),
            _ => Ok(None),
        })?;

This doesn't work because of cannot return value referencing temporary value returns a value referencing data owned by the current function.

@hidekatsu-izuno I tried to search the documentation and the tests and couldn't find a usage example for any of the functions that take the selector Fn.

from josekit-rs.

Wicpar avatar Wicpar commented on September 25, 2024 1

Thanks @w0rm , but i found https://github.com/lawliet89/biscuit which is a lot easier to use.
Also i cannot rely on KID, as the jwts are user provided.

from josekit-rs.

Wicpar avatar Wicpar commented on September 25, 2024 1

@w0rm i noticed, but that is not a requirement for me yet. I guess i'll refactor it there when it comes to it.

from josekit-rs.

naftulikay avatar naftulikay commented on September 25, 2024

I was able to solve the issue, full source code available here.

In short:

use josekit::jwk::alg::ed::{EdCurve, EdKeyPair};
use josekit::jwk::{Jwk, KeyPair};
use josekit::jws::alg::eddsa::{EddsaJwsSigner, EdssaJwsVerifier};
use josekit::jws::{EdDSA, JwsHeader};
use josekit::jwt::{self, JwtPayload};

// generate the keypair and its jwk representations
let keypair: EdKeyPair = EdDSA.generate_key_pair(EdCurve::Ed25519).unwrap();
let jwk_keypair: Jwk = keypair.to_jwk_key_pair();
let jwk_public: Jwk = jwk_keypair.to_public_key().unwrap();

// create a token signer using the private key
let token_signer: EddsaJwsSigner = EdDSA.signer_from_jwk(&jwk_keypair).unwrap();

// generate header and claims to sign
let (header, claims): (JwsHeader, JwtPayload) = create_token();

// sign with the private key
let token: String = jwt::encode_with_signer(&claims, &header, &token_signer).unwrap();

// create a verifier
let token_verifier: EddsaJwsVerifier = EdDSA.verifier_from_jwk(&jwk_public).unwrap();

// verify the claims using a selector that chooses a key based on the header's key id
let (_verified_claims, _verified_header) =
    jwt::decode_with_verifier_selector(token, |header| {
        if let Some(header_key_id) = header.key_id() {
            // if the header's key id matches our key's key id, use that as the verifier
            if header_key_id == jwk_pub.key_id().unwrap() {
                return Ok(Some(&token_verifier));
            }
        }

        Ok(None)
    })
    .unwrap_or_else(|e| {
        eprintln!("Failed to decode/verify token: {}", e);
        exit(1)
    });

from josekit-rs.

hidekatsu-izuno avatar hidekatsu-izuno commented on September 25, 2024

I'm glad you were able to solve the problem. I'm sorry I am too busy to answer your issue.

from josekit-rs.

Wicpar avatar Wicpar commented on September 25, 2024

I'm also looking for a way to universally verify a JWT with any key in the keyset no matter the algorithm.
For now i'll make do with brute forcing all algorithms like this:

pub fn get_jwk_verifier(jwk: &Jwk) -> Option<Box<dyn JwsVerifier>> {
    None.or_else(
        || [ES256, ES384, ES512].iter().find_map(|it|it.verifier_from_jwk(jwk).map(Box::<dyn JwsVerifier>::new).ok())
    ).or_else(
        || [Eddsa].iter().find_map(|it|it.verifier_from_jwk(jwk).map(Box::<dyn JwsVerifier>::new).ok())
    )
}

keyset.keys().into_iter().filter_map(get_jwk_verifier).find_map(|v|josekit::jwt::decode_with_verifier(&create.public_keys_jwt, &v).ok())

i think there should be compile time associated const str that can be used to match and build a Box<dyn JwsVerifier> directly

from josekit-rs.

w0rm avatar w0rm commented on September 25, 2024

@Wicpar I've ended up loading a mapping from key id to a verifier:

let jws_verifiers = JwkSet::from_bytes(bytes)?
            .keys()
            .into_iter()
            .filter_map(|jwk| {
                let kid = jwk.key_id()?.to_string();
                if jwk.key_use() != Some("sig") {
                    return None;
                }
                match jwk.key_type() {
                    "RSA" => Some(RS256.verifier_from_jwk(jwk).map(|v| (kid, v.box_clone()))),
                    "EC" => Some(ES512.verifier_from_jwk(jwk).map(|v| (kid, v.box_clone()))),
                    "OKP" => Some(EdDSA.verifier_from_jwk(jwk).map(|v| (kid, v.box_clone()))),
                    _ => None,
                }
            })
            .collect::<Result<BTreeMap<String, Box<dyn JwsVerifier>>, _>>()?;

And then later I get a verifier by the key id and use it to validate the token:

        let (payload, _header) = decode_with_verifier_selector(&token, |header| {
            Ok(header
                .key_id()
                .and_then(|kid| jws_verifiers.get(kid))
                .map(|el| el.as_ref()))
        })?;

from josekit-rs.

w0rm avatar w0rm commented on September 25, 2024

@Wicpar I've just remembered why I didn't go with biscuit, it is because I needed ES512, that is not supported: https://github.com/lawliet89/biscuit/blob/0333da5a60f626d418d5575324a353c5eae1dc51/src/jwa.rs#L100

from josekit-rs.

Related Issues (17)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.