mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-31 02:08:20 +02:00 
			
		
		
		
	First working version
This commit is contained in:
		
							
								
								
									
										164
									
								
								src/auth.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								src/auth.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| /// | ||||
| /// 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<u8> = 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<u8> = 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<T: Serialize>(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<JWTClaims, String> { | ||||
|     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<String>, | ||||
|     // [ "Application" ] | ||||
|     pub amr: Vec<String>, | ||||
| } | ||||
|  | ||||
| /// | ||||
| /// 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: i32, | ||||
|     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<Self, Self::Error> { | ||||
|         let headers = request.headers(); | ||||
|  | ||||
|         /// Get device type | ||||
|         let device_type = match headers.get_one("Device-Type") | ||||
|             .map(|s| s.parse::<i32>()) { | ||||
|             Some(Ok(dt)) => dt, | ||||
|             _ => 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::<DbConn>() { | ||||
|             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 }) | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user