server: Add a extractor for the authenticated user
This commit is contained in:
parent
f50d7c1076
commit
66249a5e82
3 changed files with 78 additions and 4 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
|
@ -267,6 +267,7 @@ dependencies = [
|
|||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"headers",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
|
|
@ -1239,6 +1240,31 @@ dependencies = [
|
|||
"hashbrown 0.13.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"headers-core",
|
||||
"http",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"sha1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers-core"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429"
|
||||
dependencies = [
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ members = [".", "api", "app", "migration"]
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0.71"
|
||||
axum = "0.6.18"
|
||||
axum = { version = "0.6.18", features = ["headers"] }
|
||||
base64 = "0.21.0"
|
||||
config = "0.13.3"
|
||||
jwt-simple = "0.11.5"
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue