Skip to main content

cataclysm_jwt/
jwt_session.rs

1mod rs256_session;
2mod hs256_session;
3
4pub use rs256_session::{JWTRS256Session, JWTRS256Builder};
5pub use hs256_session::{JWTHS256Session, JWTHS256Builder};
6
7use crate::{Error, error::JWTError, JWT};
8
9use base64::{engine::general_purpose, Engine};
10use serde_json::Value;
11use std::collections::HashMap;
12use cataclysm::{session::SessionCreator, http::Request};
13
14/// # Ussage
15///
16/// Trait employed for HS256 and RS256 session to avoid code repetition and specify what needs to be done to verify JWT correctly
17/// The `obtain_token_from_req` function is the same in both cases, since it returns a `JWT` instance
18/// This trait is made public in case someone finds it useful.
19/// 
20pub trait JWTSession: Clone + SessionCreator {
21
22    /// Should perform any kind of validation necessary before reading and manipulating the request
23    fn initial_validation(&self, jwt: &JWT) -> Result<(),Error>;
24
25    /// Deserializes any token from a string.
26    /// The functionality split apart from the function obtain_token_from_req so to use it in cases
27    /// in which a JWT needs to be built without a session.
28    fn deserialize_token(token: String) -> Result<JWT,Error> {
29
30        let token_parts: Vec<&str> = token.split('.').collect();
31
32        // Get header and convert it to HashMap
33        let header = match general_purpose::URL_SAFE_NO_PAD.decode(token_parts[0]) {
34            Ok(h) => match std::str::from_utf8(&h) {
35                Ok(h_s) => {
36
37                    serde_json::from_str::<HashMap<String,Value>>(h_s).map_err(|e| Error::Serde(e))?.into_iter().map(|(k,v)| -> Result<(String,String),Error> {
38                        let v = if v.is_string() {
39                            v.as_str().ok_or(Error::JWT(JWTError::HeaderField))?.to_string()
40                        } else {
41                            v.to_string()
42                        };
43                        Ok((k,v))
44                    }).collect::<Result<HashMap<String,String>,_>>()?
45
46                },
47                Err(e) => return Err(Error::UTF8(e))
48            },
49            Err(e) => return Err(Error::Decode(e, "Unable to decode jwt header into HashMap!"))
50        };
51        
52        // Get payload and convert it to HashMap
53        let payload: HashMap<String,String> = match general_purpose::URL_SAFE_NO_PAD.decode(token_parts[1]) {
54            Ok(p) => match std::str::from_utf8(&p) {
55                Ok(p_s) => {
56
57                    serde_json::from_str::<HashMap<String,Value>>(p_s).map_err(|e| Error::Serde(e))?.into_iter().map(|(k,v)| -> Result<(String,String),Error> {
58                        let v = if v.is_string() {
59                            v.as_str().ok_or(Error::JWT(JWTError::PayloadField))?.to_string()
60                        } else {
61                            v.to_string()
62                        };
63                        Ok((k,v))
64                    }).collect::<Result<HashMap<String,String>,_>>()?
65
66                },
67                Err(e) => return Err(Error::UTF8(e))
68            },
69            Err(e) => return Err(Error::Decode(e,"Unable to decode jwt payload into HashMap!"))
70        };
71
72        // Get signature
73        let signature = token_parts[2].to_string();
74        
75        Ok(JWT {
76            header,
77            payload,
78            signature,
79            raw_jwt: token
80        })
81
82    }
83
84    /// From request extract JWT to manipulate it easier later on
85    fn obtain_token_from_req(req: &Request) -> Result<JWT,Error> {
86
87        // Get authorizarion header
88        let authorization_header = match req.headers.get("Authorization") {
89            Some(a) => a,
90            None => {
91                match req.headers.get("authorization") {
92                    Some(a_t) => a_t,
93                    None => {
94                        return Err(Error::JWT(JWTError::Header));
95                    }
96                }
97            }
98        };
99        
100        // Split token
101        let token: String = authorization_header[0].split(' ').collect::<Vec<&str>>()[1].to_string();
102        Self::deserialize_token(token)
103    
104    }
105
106    /// Build session from incoming request. Should return a HashMap with values needed to validate session and possibly receive user information (through jwt)
107    fn build_from_req(&self, req: &Request) -> Result<HashMap<String,String>, Error>;
108
109}
110
111
112/// Empty struct for easier user interface
113pub struct JWTSessionBuilder();
114
115impl JWTSessionBuilder {
116
117    /// Return a builder for assymmetric signing
118    pub fn with_rs256() -> JWTRS256Builder {
119        JWTRS256Builder::default()
120    }
121
122    /// Return a builder for symmetric signing
123    pub fn with_hs256() -> JWTHS256Builder {
124        JWTHS256Builder::default()
125    }
126
127}
128