small_jwt 1.1.0

Simple and small JWT libary
Documentation
//! Base64 and JSON utilities.

/// Converts bytes into base64 (url safe, no padding).
pub fn to_b64(data: &[u8]) -> String {
    use base64::Engine;

    base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(data)
}

/// Converts string to base64 and then serializes it to json string.
pub fn to_json_b64<D>(data: &D) -> crate::error::Result<String>
where
    D: serde::Serialize,
{
    serde_json::to_string(data)
        .map(|data| to_b64(data.as_bytes()))
        .map_err(crate::error::Error::from)
}

/// Converts string from base64 (url safe, no padding).
pub fn from_b64(data: &str) -> crate::error::Result<String> {
    use base64::Engine;

    base64::prelude::BASE64_URL_SAFE_NO_PAD.decode(data)
        .map_err(crate::error::Error::from)
        .map(String::from_utf8)?
        .map_err(crate::error::Error::from)
}

/// Converts string from base64 and then deserializes it from json string.
pub fn from_json_b64<D>(data: &str) -> crate::error::Result<D>
where
    D: serde::de::DeserializeOwned,
{
    let data = from_b64(data)?;

    serde_json::from_str(&data)
        .map_err(crate::error::Error::from)
}

#[cfg(test)]
mod tests {
    use super::*;

    // INFO:
    // - For encoding tests: https://www.base64encode.org/
    // - For decoding tests: https://www.base64decode.org/

    #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
    struct TestDataStruct {
        name: String,
        age: u16,
    }

    #[test]
    fn to_b64_success() {
        let data = b"test";
        let encoded = to_b64(data);

        // encoded: test
        assert_eq!("dGVzdA", encoded);
    }

    #[test]
    fn to_json_b64_success() {
        let data = TestDataStruct {
            name: String::from("test"),
            age: 16,
        };
        let encoded = to_json_b64(&data);

        assert!(encoded.is_ok(), "Should be ok");
        // encoded: {"name":"test","age":16}
        assert_eq!("eyJuYW1lIjoidGVzdCIsImFnZSI6MTZ9", encoded.unwrap());
    }

    #[test]
    fn from_b64_success() {
        // encoded: test
        let data = "dGVzdA";
        let decoded = from_b64(data);

        assert!(decoded.is_ok(), "Should be ok");
        assert_eq!("test", decoded.unwrap());
    }

    #[test]
    fn from_b64_fail() {
        let data = "NOT_AN_ACTUAL_DATA=/";
        let decoded = from_b64(data);

        match decoded {
            Err(err) => match err {
                crate::error::Error::B64DecodeError(_) => (),
                _ => panic!("Decoded isn't a B64DecodeError"),
            },
            _ => panic!("Decoded isn't a error"),
        }
    }

    #[test]
    fn from_json_b64_success() {
        // encoded: {"name":"test","age":16}
        let data = r#"eyJuYW1lIjoidGVzdCIsImFnZSI6MTZ9"#;
        let decoded = from_json_b64::<TestDataStruct>(data);

        assert!(decoded.is_ok(), "Should be ok");
        assert_eq!(TestDataStruct { name: String::from("test"), age: 16 }, decoded.unwrap());
    }

    #[test]
    fn from_json_b64_fail_serde() {
        // encoded {"name":"test","age":"asd"}, should panic bc "age" is not a u16.
        let data = "eyJuYW1lIjoidGVzdCIsImFnZSI6ImFzZCJ9";
        let decoded = from_json_b64::<TestDataStruct>(data);

        match decoded {
            Err(err) => match err {
                crate::error::Error::SerdeJsonError(_) => (),
                _ => panic!("Decoded isn't a SerdeJsonError"),
            },
            _ => panic!("Decoded isn't a error"),
        }
    }

    #[test]
    fn from_json_b64_fail_b64() {
        // should panic bc '/' is not a url safe character.
        let data = "eyJu/YW1lIjoidGVzdCIsImFnZSI6ImFzZCJ9";
        let decoded = from_json_b64::<TestDataStruct>(data);

        match decoded {
            Err(err) => match err {
                crate::error::Error::B64DecodeError(_) => (),
                _ => panic!("Decoded isn't a B64DecodeError"),
            },
            _ => panic!("Decoded isn't a error"),
        }
    }
}