oxide_auth_axum/
request.rs

1use oxide_auth::frontends::dev::{NormalizedParameter, QueryParameter, WebRequest};
2use axum::{
3    extract::{Query, Form, FromRequest, FromRequestParts, Request},
4    http::{header, request::Parts},
5};
6use crate::{OAuthResponse, WebError};
7use std::borrow::Cow;
8
9#[derive(Clone, Debug, Default)]
10/// Type implementing `WebRequest` as well as `FromRequest` for use in route handlers
11///
12/// This type consumes the body of the Request upon extraction, so be careful not to use it in
13/// places you also expect an application payload
14pub struct OAuthRequest {
15    auth: Option<String>,
16    query: Option<NormalizedParameter>,
17    body: Option<NormalizedParameter>,
18}
19
20/// Type implementing `WebRequest` as well as `FromRequest` for use in guarding resources
21///
22/// This is useful over [OAuthRequest] since [OAuthResource] doesn't consume the body of the
23/// request upon extraction
24pub struct OAuthResource {
25    auth: Option<String>,
26}
27
28impl OAuthRequest {
29    /// Fetch the authorization header from the request
30    pub fn authorization_header(&self) -> Option<&str> {
31        self.auth.as_deref()
32    }
33
34    /// Fetch the query for this request
35    pub fn query(&self) -> Option<&NormalizedParameter> {
36        self.query.as_ref()
37    }
38
39    /// Fetch the query mutably
40    pub fn query_mut(&mut self) -> Option<&mut NormalizedParameter> {
41        self.query.as_mut()
42    }
43
44    /// Fetch the body of the request
45    pub fn body(&self) -> Option<&NormalizedParameter> {
46        self.body.as_ref()
47    }
48}
49
50impl From<OAuthResource> for OAuthRequest {
51    fn from(r: OAuthResource) -> OAuthRequest {
52        OAuthRequest {
53            auth: r.auth,
54            ..Default::default()
55        }
56    }
57}
58
59impl WebRequest for OAuthRequest {
60    type Error = WebError;
61    type Response = OAuthResponse;
62
63    fn query(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
64        self.query
65            .as_ref()
66            .map(|q| Cow::Borrowed(q as &dyn QueryParameter))
67            .ok_or(WebError::Query)
68    }
69
70    fn urlbody(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
71        self.body
72            .as_ref()
73            .map(|b| Cow::Borrowed(b as &dyn QueryParameter))
74            .ok_or(WebError::Body)
75    }
76
77    fn authheader(&mut self) -> Result<Option<Cow<str>>, Self::Error> {
78        Ok(self.auth.as_deref().map(Cow::Borrowed))
79    }
80}
81
82impl<S> FromRequest<S> for OAuthRequest
83where
84    S: Send + Sync,
85{
86    type Rejection = WebError;
87
88    async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
89        let mut all_auth = req.headers().get_all(header::AUTHORIZATION).iter();
90        let optional = all_auth.next();
91
92        let auth = if all_auth.next().is_some() {
93            return Err(WebError::Authorization);
94        } else {
95            optional.and_then(|hv| hv.to_str().ok().map(str::to_owned))
96        };
97
98        let (mut parts, body) = req.into_parts();
99        let query = Query::from_request_parts(&mut parts, state)
100            .await
101            .ok()
102            .map(|q: Query<NormalizedParameter>| q.0);
103
104        let req = Request::from_parts(parts, body);
105        let body = Form::from_request(req, state)
106            .await
107            .ok()
108            .map(|b: Form<NormalizedParameter>| b.0);
109
110        Ok(Self { auth, query, body })
111    }
112}
113
114impl<S> FromRequestParts<S> for OAuthResource
115where
116    S: Send + Sync,
117{
118    type Rejection = WebError;
119
120    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
121        let mut all_auth = parts.headers.get_all(header::AUTHORIZATION).iter();
122        let optional = all_auth.next();
123
124        let auth = if all_auth.next().is_some() {
125            return Err(WebError::Authorization);
126        } else {
127            optional.and_then(|hv| hv.to_str().ok().map(str::to_owned))
128        };
129
130        Ok(Self { auth })
131    }
132}
133
134impl OAuthResource {
135    /// Fetch the authorization header from the request
136    pub fn authorization_header(&self) -> Option<&str> {
137        self.auth.as_deref()
138    }
139}