oxide_auth/endpoint/
authorization.rs

1use crate::code_grant::authorization::{
2    authorization_code, Error as AuthorizationError, Extension, Endpoint as AuthorizationEndpoint,
3    Request as AuthorizationRequest, Pending,
4};
5
6use super::*;
7
8/// All relevant methods for handling authorization code requests.
9pub struct AuthorizationFlow<E, R>
10where
11    E: Endpoint<R>,
12    R: WebRequest,
13{
14    endpoint: WrappedAuthorization<E, R>,
15}
16
17struct WrappedAuthorization<E: Endpoint<R>, R: WebRequest> {
18    inner: E,
19    extension_fallback: (),
20    r_type: PhantomData<R>,
21}
22
23struct WrappedRequest<'a, R: WebRequest + 'a> {
24    /// Original request.
25    request: PhantomData<R>,
26
27    /// The query in the url.
28    query: Cow<'a, dyn QueryParameter + 'static>,
29
30    /// An error if one occurred.
31    error: Option<R::Error>,
32}
33
34struct AuthorizationPending<'a, E: 'a, R: 'a>
35where
36    E: Endpoint<R>,
37    R: WebRequest,
38{
39    endpoint: &'a mut WrappedAuthorization<E, R>,
40    pending: Pending,
41    request: R,
42}
43
44/// A processed authentication request that may be waiting for authorization by the resource owner.
45///
46/// Note that this borrows from the `AuthorizationFlow` used to create it. You can `finish` the
47/// authorization flow for this request to produce a response or an error.
48struct AuthorizationPartial<'a, E: 'a, R: 'a>
49where
50    E: Endpoint<R>,
51    R: WebRequest,
52{
53    inner: AuthorizationPartialInner<'a, E, R>,
54}
55
56/// Result type from processing an authentication request.
57enum AuthorizationPartialInner<'a, E: 'a, R: 'a>
58where
59    E: Endpoint<R>,
60    R: WebRequest,
61{
62    /// No error happened during processing and the resource owner can decide over the grant.
63    Pending {
64        /// A utility struct with which the request can be decided.
65        pending: AuthorizationPending<'a, E, R>,
66    },
67
68    /// The request was faulty, e.g. wrong client data, but there is a well defined response.
69    Failed {
70        /// The request passed in.
71        request: R,
72
73        /// Final response to the client.
74        ///
75        /// This should be forwarded unaltered to the client. Modifications and amendments risk
76        /// deviating from the OAuth2 rfc, enabling DoS, or even leaking secrets. Modify with care.
77        response: R::Response,
78    },
79
80    /// An internal error happened during the request.
81    Error {
82        /// The request passed in.
83        request: R,
84
85        /// Error that happened while handling the request.
86        error: E::Error,
87    },
88}
89
90impl<E, R> AuthorizationFlow<E, R>
91where
92    E: Endpoint<R>,
93    R: WebRequest,
94{
95    /// Check that the endpoint supports the necessary operations for handling requests.
96    ///
97    /// Binds the endpoint to a particular type of request that it supports, for many
98    /// implementations this is probably single type anyways.
99    ///
100    /// ## Panics
101    ///
102    /// Indirectly `execute` may panic when this flow is instantiated with an inconsistent
103    /// endpoint, for details see the documentation of `Endpoint`. For consistent endpoints,
104    /// the panic is instead caught as an error here.
105    pub fn prepare(mut endpoint: E) -> Result<Self, E::Error> {
106        if endpoint.registrar().is_none() {
107            return Err(endpoint.error(OAuthError::PrimitiveError));
108        }
109
110        if endpoint.authorizer_mut().is_none() {
111            return Err(endpoint.error(OAuthError::PrimitiveError));
112        }
113
114        Ok(AuthorizationFlow {
115            endpoint: WrappedAuthorization {
116                inner: endpoint,
117                extension_fallback: (),
118                r_type: PhantomData,
119            },
120        })
121    }
122
123    /// Use the checked endpoint to execute the authorization flow for a request.
124    ///
125    /// In almost all cases this is followed by executing `finish` on the result but some users may
126    /// instead want to inspect the partial result.
127    ///
128    /// ## Panics
129    ///
130    /// When the registrar or the authorizer returned by the endpoint is suddenly `None` when
131    /// previously it was `Some(_)`.
132    pub fn execute(&mut self, mut request: R) -> Result<R::Response, E::Error> {
133        let negotiated = authorization_code(&mut self.endpoint, &WrappedRequest::new(&mut request));
134
135        let inner = match negotiated {
136            Err(err) => match authorization_error(&mut self.endpoint.inner, &mut request, err) {
137                Ok(response) => AuthorizationPartialInner::Failed { request, response },
138                Err(error) => AuthorizationPartialInner::Error { request, error },
139            },
140            Ok(negotiated) => AuthorizationPartialInner::Pending {
141                pending: AuthorizationPending {
142                    endpoint: &mut self.endpoint,
143                    pending: negotiated,
144                    request,
145                },
146            },
147        };
148
149        let partial = AuthorizationPartial { inner };
150
151        partial.finish()
152    }
153}
154
155impl<'a, E: Endpoint<R>, R: WebRequest> AuthorizationPartial<'a, E, R> {
156    /// Finish the authentication step.
157    ///
158    /// If authorization has not yet produced a hard error or an explicit response, executes the
159    /// owner solicitor of the endpoint to determine owner consent.
160    pub fn finish(self) -> Result<R::Response, E::Error> {
161        let (_request, result) = match self.inner {
162            AuthorizationPartialInner::Pending { pending } => pending.finish(),
163            AuthorizationPartialInner::Failed { request, response } => (request, Ok(response)),
164            AuthorizationPartialInner::Error { request, error } => (request, Err(error)),
165        };
166
167        result
168    }
169}
170
171fn authorization_error<E: Endpoint<R>, R: WebRequest>(
172    endpoint: &mut E, request: &mut R, error: AuthorizationError,
173) -> Result<R::Response, E::Error> {
174    match error {
175        AuthorizationError::Ignore => Err(endpoint.error(OAuthError::DenySilently)),
176        AuthorizationError::Redirect(mut target) => {
177            let mut response = endpoint.response(
178                request,
179                InnerTemplate::Redirect {
180                    authorization_error: Some(target.description()),
181                }
182                .into(),
183            )?;
184            response
185                .redirect(target.into())
186                .map_err(|err| endpoint.web_error(err))?;
187            Ok(response)
188        }
189        AuthorizationError::PrimitiveError => Err(endpoint.error(OAuthError::PrimitiveError)),
190    }
191}
192
193impl<'a, E: Endpoint<R>, R: WebRequest> AuthorizationPending<'a, E, R> {
194    /// Resolve the pending status using the endpoint to query owner consent.
195    fn finish(mut self) -> (R, Result<R::Response, E::Error>) {
196        let checked = self
197            .endpoint
198            .owner_solicitor()
199            .check_consent(&mut self.request, self.pending.as_solicitation());
200
201        match checked {
202            OwnerConsent::Denied => self.deny(),
203            OwnerConsent::InProgress(resp) => self.in_progress(resp),
204            OwnerConsent::Authorized(who) => self.authorize(who),
205            OwnerConsent::Error(err) => (self.request, Err(self.endpoint.inner.web_error(err))),
206        }
207    }
208
209    /// Postpones the decision over the request, to display data to the resource owner.
210    ///
211    /// This should happen at least once for each request unless the resource owner has already
212    /// acknowledged and pre-approved a specific grant.  The response can also be used to determine
213    /// the resource owner, if no login has been detected or if multiple accounts are allowed to be
214    /// logged in at the same time.
215    fn in_progress(self, response: R::Response) -> (R, Result<R::Response, E::Error>) {
216        (self.request, Ok(response))
217    }
218
219    /// Denies the request, the client is not allowed access.
220    fn deny(mut self) -> (R, Result<R::Response, E::Error>) {
221        let result = self.pending.deny();
222        let result = Self::convert_result(result, &mut self.endpoint.inner, &mut self.request);
223
224        (self.request, result)
225    }
226
227    /// Tells the system that the resource owner with the given id has approved the grant.
228    fn authorize(mut self, who: String) -> (R, Result<R::Response, E::Error>) {
229        let result = self.pending.authorize(self.endpoint, who.into());
230        let result = Self::convert_result(result, &mut self.endpoint.inner, &mut self.request);
231
232        (self.request, result)
233    }
234
235    fn convert_result(
236        result: Result<Url, AuthorizationError>, endpoint: &mut E, request: &mut R,
237    ) -> Result<R::Response, E::Error> {
238        match result {
239            Ok(url) => {
240                let mut response = endpoint.response(
241                    request,
242                    InnerTemplate::Redirect {
243                        authorization_error: None,
244                    }
245                    .into(),
246                )?;
247                response.redirect(url).map_err(|err| endpoint.web_error(err))?;
248                Ok(response)
249            }
250            Err(err) => authorization_error(endpoint, request, err),
251        }
252    }
253}
254
255impl<E: Endpoint<R>, R: WebRequest> WrappedAuthorization<E, R> {
256    fn owner_solicitor(&mut self) -> &mut dyn OwnerSolicitor<R> {
257        self.inner.owner_solicitor().unwrap()
258    }
259}
260
261impl<E: Endpoint<R>, R: WebRequest> AuthorizationEndpoint for WrappedAuthorization<E, R> {
262    fn registrar(&self) -> &dyn Registrar {
263        self.inner.registrar().unwrap()
264    }
265
266    fn authorizer(&mut self) -> &mut dyn Authorizer {
267        self.inner.authorizer_mut().unwrap()
268    }
269
270    fn extension(&mut self) -> &mut dyn Extension {
271        self.inner
272            .extension()
273            .and_then(super::Extension::authorization)
274            .unwrap_or(&mut self.extension_fallback)
275    }
276}
277
278impl<'a, R: WebRequest + 'a> WrappedRequest<'a, R> {
279    pub fn new(request: &'a mut R) -> Self {
280        Self::new_or_fail(request).unwrap_or_else(Self::from_err)
281    }
282
283    fn new_or_fail(request: &'a mut R) -> Result<Self, R::Error> {
284        Ok(WrappedRequest {
285            request: PhantomData,
286            query: request.query()?,
287            error: None,
288        })
289    }
290
291    fn from_err(err: R::Error) -> Self {
292        WrappedRequest {
293            request: PhantomData,
294            query: Cow::Owned(Default::default()),
295            error: Some(err),
296        }
297    }
298}
299
300impl<'a, R: WebRequest + 'a> AuthorizationRequest for WrappedRequest<'a, R> {
301    fn valid(&self) -> bool {
302        self.error.is_none()
303    }
304
305    fn client_id(&self) -> Option<Cow<str>> {
306        self.query.unique_value("client_id")
307    }
308
309    fn scope(&self) -> Option<Cow<str>> {
310        self.query.unique_value("scope")
311    }
312
313    fn redirect_uri(&self) -> Option<Cow<str>> {
314        self.query.unique_value("redirect_uri")
315    }
316
317    fn state(&self) -> Option<Cow<str>> {
318        self.query.unique_value("state")
319    }
320
321    fn response_type(&self) -> Option<Cow<str>> {
322        self.query.unique_value("response_type")
323    }
324
325    fn extension(&self, key: &str) -> Option<Cow<str>> {
326        self.query.unique_value(key)
327    }
328}