/// /// JWT Handling /// use util::read_file; use std::path::Path; use time::Duration; use jwt; use serde::ser::Serialize; use serde::de::Deserialize; use CONFIG; const JWT_ALGORITHM: jwt::Algorithm = jwt::Algorithm::RS256; pub const JWT_ISSUER: &'static str = "localhost:8000/identity"; lazy_static! { pub static ref DEFAULT_VALIDITY: Duration = Duration::hours(2); static ref JWT_HEADER: jwt::Header = jwt::Header::new(JWT_ALGORITHM); static ref PRIVATE_RSA_KEY: Vec = match read_file(&CONFIG.private_rsa_key) { Ok(key) => key, Err(e) => panic!("Error loading private RSA Key from {}\n Error: {}", CONFIG.private_rsa_key, e) }; static ref PUBLIC_RSA_KEY: Vec = match read_file(&CONFIG.public_rsa_key) { Ok(key) => key, Err(e) => panic!("Error loading public RSA Key from {}\n Error: {}", CONFIG.public_rsa_key, e) }; } pub fn encode_jwt(claims: &T) -> String { match jwt::encode(&JWT_HEADER, claims, &PRIVATE_RSA_KEY) { Ok(token) => return token, Err(e) => panic!("Error encoding jwt {}", e) }; } pub fn decode_jwt(token: &str) -> Result { let validation = jwt::Validation { leeway: 30, // 30 seconds validate_exp: true, validate_iat: true, validate_nbf: true, aud: None, iss: Some(JWT_ISSUER.into()), sub: None, algorithms: vec![JWT_ALGORITHM], }; match jwt::decode(token, &PUBLIC_RSA_KEY, &validation) { Ok(decoded) => Ok(decoded.claims), Err(msg) => { println!("Error validating jwt - {:#?}", msg); Err(msg.to_string()) } } } #[derive(Debug, Serialize, Deserialize)] pub struct JWTClaims { // Not before pub nbf: i64, // Expiration time pub exp: i64, // Issuer pub iss: String, // Subject pub sub: String, pub premium: bool, pub name: String, pub email: String, pub email_verified: bool, // user security_stamp pub sstamp: String, // device uuid pub device: String, // [ "api", "offline_access" ] pub scope: Vec, // [ "Application" ] pub amr: Vec, } /// /// Bearer token authentication /// use rocket::Outcome; use rocket::http::Status; use rocket::request::{self, Request, FromRequest}; use db::DbConn; use db::models::{User, Device}; pub struct Headers { pub device_type: Option, pub device: Device, pub user: User, } impl<'a, 'r> FromRequest<'a, 'r> for Headers { type Error = &'static str; fn from_request(request: &'a Request<'r>) -> request::Outcome { let headers = request.headers(); /// Get device type let device_type = match headers.get_one("Device-Type") .map(|s| s.parse::()) { Some(Ok(dt)) => Some(dt),// dt, _ => None // return err_handler!("Device-Type is invalid or missing") }; /// Get access_token let access_token: &str = match request.headers().get_one("Authorization") { Some(a) => { let split: Option<&str> = a.rsplit("Bearer ").next(); if split.is_none() { err_handler!("No access token provided") } split.unwrap() } None => err_handler!("No access token provided") }; /// Check JWT token is valid and get device and user from it let claims: JWTClaims = match decode_jwt(access_token) { Ok(claims) => claims, Err(msg) => { println!("Invalid claim: {}", msg); err_handler!("Invalid claim") } }; let device_uuid = claims.device; let user_uuid = claims.sub; let conn = match request.guard::() { Outcome::Success(conn) => conn, _ => err_handler!("Error getting DB") }; let device = match Device::find_by_uuid(&device_uuid, &conn) { Some(device) => device, None => err_handler!("Invalid device id") }; let user = match User::find_by_uuid(&user_uuid, &conn) { Some(user) => user, None => err_handler!("Device has no user associated") }; if user.security_stamp != claims.sstamp { err_handler!("Invalid security stamp") } Outcome::Success(Headers { device_type, device, user }) } }