server: Add a extractor for the authenticated user

This commit is contained in:
traxys 2023-05-28 20:58:39 +02:00
parent f50d7c1076
commit 66249a5e82
3 changed files with 78 additions and 4 deletions

View file

@ -2,11 +2,13 @@ use std::sync::Arc;
use api::{LoginRequest, LoginResponse};
use axum::{
extract::State,
http::{header::CONTENT_TYPE, HeaderValue, Method, StatusCode},
async_trait,
extract::{FromRef, FromRequestParts, State},
headers::{authorization::Bearer, Authorization},
http::{header::CONTENT_TYPE, request::Parts, HeaderValue, Method, StatusCode},
response::IntoResponse,
routing::post,
Json, Router,
Json, Router, TypedHeader,
};
use jwt_simple::prelude::*;
use sea_orm::prelude::*;
@ -23,6 +25,10 @@ enum RouteError {
Db(#[from] DbErr),
#[error("JWT error encountered")]
Jwt(#[from] jwt_simple::Error),
#[error("User provided JWT token is invalid")]
UserJwt(jwt_simple::Error),
#[error("Request is missing the bearer token")]
MissingAuthorization,
}
impl IntoResponse for RouteError {
@ -31,6 +37,13 @@ impl IntoResponse for RouteError {
RouteError::UnknownAccount => {
(StatusCode::NOT_FOUND, "Account not found").into_response()
}
RouteError::MissingAuthorization => {
(StatusCode::BAD_REQUEST, "Missing authorization header").into_response()
}
RouteError::UserJwt(e) => {
tracing::debug!("Invalid user JWT: {e:?}");
(StatusCode::BAD_REQUEST, "Invalid authorization header").into_response()
}
e => {
tracing::error!("Internal error: {e:?}");
StatusCode::INTERNAL_SERVER_ERROR.into_response()
@ -43,6 +56,41 @@ type JsonResult<T, E = RouteError> = Result<Json<T>, E>;
type AppState = Arc<crate::AppState>;
#[derive(Debug)]
struct AuthenticatedUser {
pub id: Uuid,
}
#[async_trait]
impl<S> FromRequestParts<S> for AuthenticatedUser
where
S: Send + Sync,
AppState: FromRef<S>,
{
type Rejection = RouteError;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let State(app_state): State<AppState> = State::from_request_parts(parts, state)
.await
.expect("Could not get state");
let TypedHeader(Authorization(bearer)) =
TypedHeader::<Authorization<Bearer>>::from_request_parts(parts, state)
.await
.map_err(|_| RouteError::MissingAuthorization)?;
let claims = app_state
.jwt_secret
.0
.verify_token::<NoCustomClaims>(bearer.token(), None)
.map_err(RouteError::UserJwt)?;
Ok(AuthenticatedUser {
id: claims.subject.unwrap().parse().unwrap(),
})
}
}
async fn login(
State(state): State<AppState>,
Json(req): Json<LoginRequest>,