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 | AuthError::Validation(_) => StatusCode::UNPROCESSABLE_ENTITY,
34 AuthError::NotConfirmed | AuthError::AccountLocked => StatusCode::FORBIDDEN,
35 _ => StatusCode::INTERNAL_SERVER_ERROR,
36 };
37 (status, self.to_string()).into_response()
38 }
39}
40
41impl<S, U> FromRequestParts<S> for CurrentUser<U>
42where
43 S: Send + Sync,
44 U: AuthUser,
45{
46 type Rejection = AuthError;
47
48 fn from_request_parts(
49 parts: &mut Parts,
50 _state: &S,
51 ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
52 let identity_result = require_identity(parts);
53 let db = global().db.clone();
54 async move {
55 let identity = identity_result?;
56 let id: U::Id = serde_json::from_value(identity.user_id)
57 .map_err(|e| AuthError::Internal(e.to_string()))?;
58 U::find_by_id(&db, id)
59 .await
60 .map_err(|e| AuthError::Internal(e.to_string()))?
61 .ok_or(AuthError::Unauthorized)
62 .map(CurrentUser)
63 }
64 }
65}
66
67impl<S, U> FromRequestParts<S> for MaybeUser<U>
68where
69 S: Send + Sync,
70 U: AuthUser,
71{
72 type Rejection = std::convert::Infallible;
73
74 fn from_request_parts(
75 parts: &mut Parts,
76 _state: &S,
77 ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
78 let identity = identity_from_parts(parts);
79 let db = global().db.clone();
80 async move {
81 let user = match identity {
82 Some(identity) => {
83 let id: U::Id = match serde_json::from_value(identity.user_id) {
84 Ok(id) => id,
85 Err(_) => return Ok(MaybeUser(None)),
86 };
87 U::find_by_id(&db, id).await.ok().flatten()
88 }
89 None => None,
90 };
91 Ok(MaybeUser(user))
92 }
93 }
94}
95
96impl<S> FromRequestParts<S> for RequireAuth
97where
98 S: Send + Sync,
99{
100 type Rejection = AuthError;
101
102 fn from_request_parts(
103 parts: &mut Parts,
104 _state: &S,
105 ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
106 ready(require_identity(parts).map(RequireAuth))
107 }
108}
109
110impl<S> FromRequestParts<S> for AuthToken
111where
112 S: Send + Sync,
113{
114 type Rejection = AuthError;
115
116 fn from_request_parts(
117 parts: &mut Parts,
118 _state: &S,
119 ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
120 let token = parts
121 .headers
122 .get(header::AUTHORIZATION)
123 .and_then(|v| v.to_str().ok())
124 .ok_or(AuthError::InvalidToken)
125 .and_then(|value| {
126 value
127 .strip_prefix("Bearer ")
128 .map(str::trim)
129 .filter(|t| !t.is_empty())
130 .map(str::to_string)
131 .ok_or(AuthError::InvalidToken)
132 });
133 ready(token.map(AuthToken))
134 }
135}
136
137pub use axum;
139
140type _ReadyCheck = Ready<Result<(), AuthError>>;