extern crate hmac; extern crate jwt; extern crate sha2; use std::collections::BTreeMap; use std::env; use actix_web::HttpRequest; use dotenv::dotenv; use hmac::{Hmac, Mac}; use jwt::{Header, SignWithKey, Token, VerifyWithKey}; use sha2::Sha256; pub struct JwtToken { pub user_id: i32, } type HmacSha256 = Hmac; fn signing_key() -> HmacSha256 { dotenv().ok(); let secret = env::var("JWT_SECRET").expect("JWT_SECRET must be set"); HmacSha256::new_from_slice(secret.as_bytes()).unwrap() } impl JwtToken { pub fn encode(user_id: i32) -> String { let key: HmacSha256 = signing_key(); let mut claims = BTreeMap::new(); claims.insert("user_id", user_id); claims.sign_with_key(&key).unwrap() } pub fn decode(encoded_token: String) -> Result { let key: HmacSha256 = signing_key(); let token_str: &str = encoded_token.as_str(); let token: Result, jwt::Verified>, jwt::Error> = VerifyWithKey::verify_with_key(token_str, &key); match token { Ok(token) => { let _header = token.header(); let claims = token.claims(); Ok(JwtToken { user_id: claims["user_id"], }) } Err(_err) => Err("could not decode token"), } } #[allow(dead_code)] pub fn decode_from_request(request: HttpRequest) -> Result { match request.headers().get("user-token") { Some(token) => JwtToken::decode(String::from(token.to_str().unwrap())), None => Err("There is no token"), } } } #[cfg(test)] mod jwt_test { use actix_web::{http::header, test}; use super::JwtToken; #[test] async fn encode_decode() { let encoded_token: String = JwtToken::encode(32); let decoded_token: JwtToken = JwtToken::decode(encoded_token).unwrap(); assert_eq!(32, decoded_token.user_id); } #[test] async fn decode_incorrect_token() { let encoded_token: String = String::from("test"); match JwtToken::decode(encoded_token) { Err(message) => assert_eq!(message, "could not decode token"), _ => panic!("Incorrect token should not be able to decode."), } } #[actix_web::test] async fn decode_from_request_with_correct_token() { let encoded_token: String = JwtToken::encode(32); let request = test::TestRequest::default() .insert_header(header::ContentType::json()) .insert_header(("user-token", encoded_token)) .to_http_request(); let out_come = JwtToken::decode_from_request(request); match out_come { Ok(token) => assert_eq!(32, token.user_id), _ => panic!("Token is not returned with it should be."), } } #[actix_web::test] async fn decode_from_request_with_no_token() { let request = test::TestRequest::default() .insert_header(("test", "test")) .to_http_request(); let out_come = JwtToken::decode_from_request(request); match out_come { Err(message) => assert_eq!("There is no token", message), _ => panic!("Token should not be returned when it is not present in the header."), } } }