Repository: libsql/libsql
File: libsql-server/src/auth/parsers.rs (Rust)

Code before:
    const EXAMPLE3_JWT: &str = include_str!("../../assets/test/auth/example3.jwt");
    const MULTI_JWT_PUBLIC_KEY: &str = include_str!("../../assets/test/auth/combined123.pem");

    #[test]
    fn parse_jwt_key_single_pem() {
        let key = parse_jwt_key(EXAMPLE_JWT_PUBLIC_KEY);
        assert!(matches!(key, Ok(DecodingKeyContainer::Single(_))));
        match key {
            Ok(DecodingKeyContainer::Single(key)) => {
                let mut validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::EdDSA);
                validation.required_spec_claims.remove("exp");
                let decoded = jsonwebtoken::decode::<Token>(&EXAMPLE_JWT, &key, &validation);

                assert!(matches!(
                    decoded,
                    Ok(jsonwebtoken::TokenData {
                        header: _,
                        claims: _
                    })
                ));

                let jsonwebtoken::TokenData { header, claims } = decoded.unwrap();
                assert_eq!(header.alg, jsonwebtoken::Algorithm::EdDSA);

                let claims = format!("{:?}", claims);
                assert_eq!(claims, "Token { id: None, a: None, p: Some(Authorized { read_only: Some(Scopes { namespaces: Some({example1c, example1a, example1b}), tags: None }), read_write: None, read_only_attach: None, read_write_attach: None, ddl_override: None }), exp: Some(+33713-01-29T21:47:33Z) }");
            }
            _ => {}
        }
    }

    #[test]
    fn parse_jwt_key_multiple_pems() {
        let key = parse_jwt_key(MULTI_JWT_PUBLIC_KEY);
        assert!(matches!(key, Ok(DecodingKeyContainer::Multiple(_))));
        match key {
            Ok(DecodingKeyContainer::Multiple(keys)) => {
                let mut validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::EdDSA);
                validation.required_spec_claims.remove("exp");
                let decoded = jsonwebtoken::decode::<Token>(&EXAMPLE_JWT, &keys[0], &validation);

                assert!(matches!(
                    decoded,
                    Ok(jsonwebtoken::TokenData {
                        header: _,
                        claims: _
                    })
                ));

                let jsonwebtoken::TokenData { header, claims } = decoded.unwrap();
                assert_eq!(header.alg, jsonwebtoken::Algorithm::EdDSA);

Review comment (bug):
std::fmt::Debug does not guarantee the ordering of fields
maybe better to make assertions on individual claims fields
that way we can ignore fields that we're not concerned about
and such individual assertions could be easily reusable

Code after:
        let keys = parse_jwt_keys(EXAMPLE_JWT_PUBLIC_KEY);
        assert_eq!(keys.as_ref().map_or(0, |v| v.len()), 1);
        let key = keys.unwrap().into_iter().next().unwrap();

        let mut validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::EdDSA);
        validation.required_spec_claims.remove("exp");
        let decoded = jsonwebtoken::decode::<Token>(&EXAMPLE_JWT, &key, &validation);

        assert!(matches!(
            decoded,
            Ok(jsonwebtoken::TokenData {
                header: _,
                claims: _
            })
        ));

        let jsonwebtoken::TokenData { header, claims } = decoded.unwrap();
        assert_eq!(header.alg, jsonwebtoken::Algorithm::EdDSA);

        assert!(claims.p.is_some());
        let Some(authorized) = claims.p else {
            panic!("Assertion should have already failed");
        };

        let scopes: Scopes =
            serde_json::from_str(r##"{"ns":["example1a","example1b","example1c"]}"##)
                .expect("JSON failed to parse");
        assert_eq!(authorized.read_only, Some(scopes));
    }

    #[test]
    fn parse_jwt_keys_multiple_pems() {
        let keys = parse_jwt_keys(MULTI_JWT_PUBLIC_KEY);
        assert_eq!(keys.as_ref().map_or(0, |v| v.len()), 3);
        let keys = keys.unwrap();

        let mut validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::EdDSA);
        validation.required_spec_claims.remove("exp");
        let decoded = jsonwebtoken::decode::<Token>(&EXAMPLE_JWT, &keys[0], &validation);

        assert!(matches!(
            decoded,
            Ok(jsonwebtoken::TokenData {
                header: _,
                claims: _
            })
        ));

        let jsonwebtoken::TokenData { header, claims } = decoded.unwrap();
        assert_eq!(header.alg, jsonwebtoken::Algorithm::EdDSA);