1use crate::error::AuthError;
4use crate::identity::AuthIdentity;
5use crate::state::global;
6use crate::strategy::{identity_from_parts, require_identity};
7use crate::user::AuthUser;
8use axum::extract::FromRequestParts;
9use axum::http::request::Parts;
10use axum::http::StatusCode;
11use axum::response::{IntoResponse, Response};
12use http::header;
13use std::future::{ready, Future, Ready};
14
15pub struct CurrentUser<U>(pub U);
17
18pub struct MaybeUser<U>(pub Option<U>);
20
21pub struct RequireAuth(pub AuthIdentity);
23
24pub struct AuthToken(pub String);
26
27impl IntoResponse for AuthError {
28 fn into_response(self) -> Response {
29 let status = match self {
30 AuthError::Unauthorized | AuthError::InvalidCredentials | AuthError::InvalidToken => {
31 StatusCode::UNAUTHORIZED
32 }
33 AuthError::EmailTaken => StatusCode::UNPROCESSABLE_ENTITY,
34 _ => StatusCode::INTERNAL_SERVER_ERROR,
35 };
36 (status, self.to_string()).into_response()
37 }
38}
39
40impl<S, U> FromRequestParts<S> for CurrentUser<U>
41where
42 S: Send + Sync,
43 U: AuthUser,
44{
45 type Rejection = AuthError;
46
47 fn from_request_parts(
48 parts: &mut Parts,
49 _state: &S,
50 ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
51 let identity_result = require_identity(parts);
52 let db = global().db.clone();
53 async move {
54 let identity = identity_result?;
55 let id: U::Id = serde_json::from_value(identity.user_id)
56 .map_err(|e| AuthError::Internal(e.to_string()))?;
57 U::find_by_id(&db, id)
58 .await
59 .map_err(|e| AuthError::Internal(e.to_string()))?
60 .ok_or(AuthError::Unauthorized)
61 .map(CurrentUser)
62 }
63 }
64}
65
66impl<S, U> FromRequestParts<S> for MaybeUser<U>
67where
68 S: Send + Sync,
69 U: AuthUser,
70{
71 type Rejection = std::convert::Infallible;
72
73 fn from_request_parts(
74 parts: &mut Parts,
75 _state: &S,
76 ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
77 let identity = identity_from_parts(parts);
78 let db = global().db.clone();
79 async move {
80 let user = match identity {
81 Some(identity) => {
82 let id: U::Id = match serde_json::from_value(identity.user_id) {
83 Ok(id) => id,
84 Err(_) => return Ok(MaybeUser(None)),
85 };
86 U::find_by_id(&db, id).await.ok().flatten()
87 }
88 None => None,
89 };
90 Ok(MaybeUser(user))
91 }
92 }
93}
94
95impl<S> FromRequestParts<S> for RequireAuth
96where
97 S: Send + Sync,
98{
99 type Rejection = AuthError;
100
101 fn from_request_parts(
102 parts: &mut Parts,
103 _state: &S,
104 ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
105 ready(require_identity(parts).map(RequireAuth))
106 }
107}
108
109impl<S> FromRequestParts<S> for AuthToken
110where
111 S: Send + Sync,
112{
113 type Rejection = AuthError;
114
115 fn from_request_parts(
116 parts: &mut Parts,
117 _state: &S,
118 ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
119 let token = parts
120 .headers
121 .get(header::AUTHORIZATION)
122 .and_then(|v| v.to_str().ok())
123 .ok_or(AuthError::InvalidToken)
124 .and_then(|value| {
125 value
126 .strip_prefix("Bearer ")
127 .map(str::trim)
128 .filter(|t| !t.is_empty())
129 .map(str::to_string)
130 .ok_or(AuthError::InvalidToken)
131 });
132 ready(token.map(AuthToken))
133 }
134}
135
136pub use axum;
138
139type _ReadyCheck = Ready<Result<(), AuthError>>;