use std::{pin::Pin, time::SystemTime}; use actix_web::{error::ErrorUnauthorized, http::header, web, Error, FromRequest, HttpRequest}; use futures::Future; use jsonwebtoken::{DecodingKey, EncodingKey, Header}; use sea_orm::EntityTrait; use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::AppState; #[derive(Deserialize, Serialize)] struct Claims { sub: Uuid, name: String, exp: u64, } pub fn create_jwt( user: entity::user::Model, key: &EncodingKey, ) -> Result { let time = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .expect("SystemTime before UNIX EPOCH") .as_secs() + 86400; let claims = Claims { sub: user.id, name: user.name, exp: time, }; jsonwebtoken::encode(&Header::default(), &claims, key) } pub struct AuthedUser(entity::user::Model); impl AuthedUser { fn parse_token(req: &HttpRequest) -> Result { let header = req .headers() .get(header::AUTHORIZATION) .ok_or(ErrorUnauthorized("Unauthorized"))?; if header.len() < 8 { return Err(ErrorUnauthorized("Unauthorized")); } let mut parts = header .to_str() .map_err(|_| ErrorUnauthorized("Unauthorized"))? .splitn(2, ' '); match parts.next() { Some("Bearer") => {} _ => return Err(ErrorUnauthorized("Unauthorized")), } let token = parts.next().ok_or(ErrorUnauthorized("Unauthorized"))?; let key = &req.app_data::>().unwrap().secret; let validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256); let claims = jsonwebtoken::decode::( token, &DecodingKey::from_secret(key.as_bytes()), &validation, ) .map_err(|e| ErrorUnauthorized(e))?; Ok(claims.claims) } } impl FromRequest for AuthedUser { type Error = actix_web::Error; type Future = Pin>>>; fn from_request( req: &actix_web::HttpRequest, _payload: &mut actix_web::dev::Payload, ) -> Self::Future { let res = AuthedUser::parse_token(req); let state = req.app_data::>().unwrap().db.clone(); Box::pin(async move { let claims = res?; let id = claims.sub; let user = entity::prelude::User::find_by_id(id) .one(&state) .await .map_err(|_| ErrorUnauthorized("Unauthorized"))? .ok_or(ErrorUnauthorized("Unauthorized"))?; Ok(AuthedUser(user)) }) } }