use axum::{
extract::FromRequestParts,
http::{request::Parts, StatusCode},
RequestPartsExt,
};
use axum_extra::extract::CookieJar;
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
pub sub: String,
pub exp: usize,
}
pub fn create_token(secret: &str) -> Result<String, jsonwebtoken::errors::Error> {
let claims = Claims {
sub: "admin".to_string(),
exp: (chrono::Utc::now() + chrono::Duration::hours(24)).timestamp() as usize,
};
encode(
&Header::default(),
&claims,
&EncodingKey::from_secret(secret.as_bytes()),
)
}
pub fn verify_token(secret: &str, token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
let token_data = decode::<Claims>(
token,
&DecodingKey::from_secret(secret.as_bytes()),
&Validation::default(),
)?;
Ok(token_data.claims)
}
pub struct RequireAuth;
impl<S> FromRequestParts<S> for RequireAuth
where
S: Send + Sync,
{
type Rejection = StatusCode;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
let jar = parts.extract::<CookieJar>().await.unwrap_or_default();
let token = jar
.get("omniterm_token")
.map(|c| c.value().to_string())
.or_else(|| {
parts
.headers
.get("authorization")
.and_then(|v| v.to_str().ok())
.and_then(|v| v.strip_prefix("Bearer "))
.map(|v| v.to_string())
});
let token = token.ok_or(StatusCode::UNAUTHORIZED)?;
let secret = parts
.extensions
.get::<String>()
.cloned()
.unwrap_or_else(|| "omniterm-default-secret-change-me".to_string());
verify_token(&secret, &token).map_err(|_| StatusCode::UNAUTHORIZED)?;
Ok(RequireAuth)
}
}