98 lines
2.7 KiB
Rust
98 lines
2.7 KiB
Rust
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<String, jsonwebtoken::errors::Error> {
|
|
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<Claims, Error> {
|
|
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::<web::Data<AppState>>().unwrap().secret;
|
|
|
|
let validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256);
|
|
let claims = jsonwebtoken::decode::<Claims>(
|
|
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<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
|
|
|
|
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::<web::Data<AppState>>().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))
|
|
})
|
|
}
|
|
}
|