oxide_auth/endpoint/mod.rs
1//! Polymorphic HTTP wrappers for code grant authorization and other flows.
2//!
3//! An endpoint is concerned with executing the abstract behaviours given by the backend in terms
4//! of the actions of the endpoint types. This means translating Redirect errors to the correct
5//! Redirect http response for example or optionally sending internal errors to loggers. The
6//! frontends, which are the bindings to particular server libraries, can instantiate the endpoint
7//! api or simple reuse existing types.
8//!
9//! To ensure the adherence to the oauth2 rfc and the improve general implementations, some control
10//! flow of incoming packets is specified here instead of the frontend implementations. Instead,
11//! traits are offered to make this compatible with other endpoints. In theory, this makes
12//! endpoints pluggable which could improve testing.
13//!
14//! Custom endpoint
15//! ---------------
16//! In order to not place restrictions on the web server library in use, it is possible to
17//! implement an endpoint completely with user defined types.
18//!
19//! This requires custom, related implementations of [`WebRequest`] and [`WebResponse`].
20//! _WARNING_: Custom endpoints MUST ensure a secure communication layer with confidential clients.
21//! This means using TLS for communication over https.
22//!
23//! After receiving an authorization grant, access token or access request, initiate the respective
24//! flow by collecting the [`Authorizer`], [`Issuer`], and [`Registrar`] instances. For example:
25//!
26//! [`WebRequest`]: trait.WebRequest.html
27//! [`WebResponse`]: trait.WebResponse.html
28//! [`Authorizer`]: ../../primitives/authorizer/trait.Authorizer.html
29//! [`Issuer`]: ../../primitives/issuer/trait.Issuer.html
30//! [`Registrar`]: ../../primitives/registrar/trait.Registrar.html
31mod authorization;
32mod accesstoken;
33mod client_credentials;
34mod error;
35mod refresh;
36mod resource;
37mod query;
38
39#[cfg(test)]
40mod tests;
41
42use std::borrow::Cow;
43use std::marker::PhantomData;
44
45pub use crate::primitives::authorizer::Authorizer;
46pub use crate::primitives::issuer::Issuer;
47pub use crate::primitives::registrar::Registrar;
48pub use crate::primitives::scope::Scope;
49
50use crate::code_grant::resource::{Error as ResourceError};
51use crate::code_grant::error::{AuthorizationError, AccessTokenError};
52
53use url::Url;
54
55// Re-export the extension traits under prefixed names.
56pub use crate::code_grant::authorization::Extension as AuthorizationExtension;
57pub use crate::code_grant::accesstoken::Extension as AccessTokenExtension;
58pub use crate::code_grant::client_credentials::Extension as ClientCredentialsExtension;
59
60pub use crate::primitives::registrar::PreGrant;
61pub use self::authorization::*;
62pub use self::accesstoken::*;
63pub use self::client_credentials::ClientCredentialsFlow;
64pub use self::error::OAuthError;
65pub use self::refresh::RefreshFlow;
66pub use self::resource::*;
67pub use self::query::*;
68
69/// Answer from OwnerAuthorizer to indicate the owners choice.
70pub enum OwnerConsent<Response: WebResponse> {
71 /// The owner did not authorize the client.
72 Denied,
73
74 /// The owner has not yet decided, i.e. the returned page is a form for the user.
75 InProgress(Response),
76
77 /// Authorization was granted by the specified user.
78 Authorized(String),
79
80 /// An error occurred while checking authorization.
81 Error(Response::Error),
82}
83
84/// Modifiable reason for creating a response to the client.
85///
86/// Not all responses indicate failure. A redirect will also occur in the a regular of providing an
87/// access token to the third party client. When an error is present (see several methods) it is
88/// mostly possible to customize it. This hook provides advanced endpoints with the opportunity to
89/// set additional parameters and informational messages before they are encoded.
90///
91/// See the provided methods for more information and examples.
92#[derive(Debug)]
93pub struct Template<'a> {
94 inner: InnerTemplate<'a>,
95}
96
97/// The general manner of the response.
98///
99/// These are parallels for HTTP status codes of the same name.
100#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
101pub enum ResponseStatus {
102 /// The response is issued because the requesting party was not authorized.
103 Unauthorized,
104
105 /// The response redirects in the code grant flow.
106 Redirect,
107
108 /// The request was malformed.
109 BadRequest,
110
111 /// This response is normal and expected.
112 Ok,
113}
114
115/// Encapsulated different types of responses reasons.
116///
117/// Each variant contains some form of context information about the response. This can be used either
118/// purely informational or in some cases provides additional customization points. The addition of
119/// fields to some variant context can occur in any major release until `1.0`. It is discouraged to
120/// exhaustively match the fields directly. Since some context could not permit cloning, the enum will
121/// not derive this until this has shown unlikely but strongly requested. Please open an issue if you
122/// think the pros or cons should be evaluated differently.
123#[derive(Debug)]
124#[non_exhaustive]
125enum InnerTemplate<'a> {
126 /// Authorization to access the resource has not been granted.
127 Unauthorized {
128 /// The underlying cause for denying access.
129 ///
130 /// The http authorization header is to be set according to this field.
131 #[allow(dead_code)]
132 error: Option<ResourceError>,
133
134 /// Information on an access token error.
135 ///
136 /// Endpoints may modify this description to add additional explanatory text or a reference
137 /// uri for clients seeking explanation.
138 access_token_error: Option<&'a mut AccessTokenError>,
139 },
140
141 /// Redirect the user-agent to another url.
142 ///
143 /// The endpoint has the opportunity to inspect and modify error information to some extent.
144 /// For example to log an error rate or to provide a pointer to a custom human readable
145 /// explanation page. The response will generally not contain a body.
146 Redirect {
147 /// Information on an authorization error.
148 ///
149 /// Endpoints may modify this description to add additional explanatory text or a reference
150 /// uri for clients or resource owners seeking explanation.
151 authorization_error: Option<&'a mut AuthorizationError>,
152 },
153
154 /// The request did not conform to specification or was otheriwse invalid.
155 ///
156 /// As such, it was not handled further. Some processes still warrant a response body to be
157 /// set in the case of an invalid request, containing additional information for the client.
158 /// For example, an authorized client sending a malformed but authenticated request for an
159 /// access token will receive additional hints on the cause of his mistake.
160 BadRequest {
161 /// Information on an invalid-access-token-request error.
162 ///
163 /// Endpoints may modify this description to add additional explanatory text or a reference
164 /// uri for clients seeking explanation.
165 access_token_error: Option<&'a mut AccessTokenError>,
166 },
167
168 /// An expected, normal response.
169 ///
170 /// The content of the response may require precise semantics to be standard compliant,
171 /// therefore it is constructed using the `WebResponse` trait methods. Try not to tamper with
172 /// the format too much, such as unsetting a body etc. after the flow has finished.
173 Ok,
174}
175
176/// A pending solicitation to a resource owner.
177///
178/// This encapsulates the information available to an [`OwnerSolicitor`] when querying consent
179/// information.
180///
181/// [`OwnerSolicitor`]: trait.OwnerSolicitor.html
182pub struct Solicitation<'flow> {
183 pub(crate) grant: Cow<'flow, PreGrant>,
184 pub(crate) state: Option<Cow<'flow, str>>,
185}
186
187impl<'flow> Solicitation<'flow> {
188 /// Clone the solicitation into an owned structure.
189 ///
190 /// This mainly helps with sending it across threads.
191 pub fn into_owned(self) -> Solicitation<'static> {
192 Solicitation {
193 grant: Cow::Owned(self.grant.into_owned()),
194 state: self.state.map(|state| Cow::Owned(state.into_owned())),
195 }
196 }
197
198 /// Return the pre-grant associated with the request.
199 ///
200 /// The information in the `PreGrant` is the authoritative information on the client and scopes
201 /// associated with the request. It has already been validated against those settings and
202 /// restrictions that were applied when registering the client.
203 pub fn pre_grant(&self) -> &PreGrant {
204 self.grant.as_ref()
205 }
206
207 /// The state provided by the client request.
208 ///
209 /// This will need to be provided to the response back to the client so it must be preserved
210 /// across a redirect or a consent screen presented by the user agent.
211 pub fn state(&self) -> Option<&str> {
212 match self.state {
213 None => None,
214 Some(ref state) => Some(&state),
215 }
216 }
217
218 /// Create a new solicitation request from a pre grant.
219 ///
220 /// You usually wouldn't need to call this manually as it is called by the endpoint's flow and
221 /// then handed with all available information to the solicitor.
222 pub fn new(grant: &'flow PreGrant) -> Self {
223 Solicitation {
224 grant: Cow::Borrowed(grant),
225 state: None,
226 }
227 }
228
229 /// Add a client state to the solicitation.
230 pub fn with_state(self, state: &'flow str) -> Self {
231 Solicitation {
232 state: Some(Cow::Borrowed(state)),
233 ..self
234 }
235 }
236}
237
238/// Checks consent with the owner of a resource, identified in a request.
239///
240/// See [`frontends::simple`] for an implementation that permits arbitrary functions.
241///
242/// [`frontends::simple`]: ../frontends/simple/endpoint/struct.FnSolicitor.html
243pub trait OwnerSolicitor<Request: WebRequest> {
244 /// Ensure that a user (resource owner) is currently authenticated (for example via a session
245 /// cookie) and determine if he has agreed to the presented grants.
246 fn check_consent(&mut self, _: &mut Request, _: Solicitation) -> OwnerConsent<Request::Response>;
247}
248
249/// Determine the scopes applying to a request of a resource.
250///
251/// It is possible to use a slice of [`Scope`]s as an implementation of this trait. You can inspect
252/// the request that was used to access the resource for which the scopes are to be determined but
253/// should generally avoid doing so. Sometimes the scope depends on external parameters and this is
254/// unavoidable, e.g. if the scope is created dynamically from the path of the resource.
255///
256/// ## Example
257///
258/// Here's a possible new implementation that allows you to update your scope list at runtime:
259///
260/// ```
261/// # use oxide_auth::endpoint::Scopes;
262/// # use oxide_auth::endpoint::WebRequest;
263/// use oxide_auth::primitives::scope::Scope;
264/// use std::sync::{Arc, RwLock};
265///
266/// struct MyScopes {
267/// update: RwLock<Arc<[Scope]>>,
268/// current: Arc<[Scope]>,
269/// };
270///
271/// impl<R: WebRequest> Scopes<R> for MyScopes {
272/// fn scopes(&mut self, _: &mut R) -> &[Scope] {
273/// let update = self.update.read().unwrap();
274/// if !Arc::ptr_eq(&update, &self.current) {
275/// self.current = update.clone();
276/// }
277/// &self.current
278/// }
279/// }
280/// ```
281///
282/// [`Scope`]: ../primitives/scope/struct.Scope.html
283pub trait Scopes<Request: WebRequest> {
284 /// A list of alternative scopes.
285 ///
286 /// One of the scopes needs to be fulfilled by the access token in the request to grant access.
287 /// A scope is fulfilled if the set of its part is a subset of the parts in the grant. If the
288 /// slice is empty, then no scope can be fulfilled and the request is always blocked.
289 fn scopes(&mut self, request: &mut Request) -> &[Scope];
290}
291
292/// Abstraction of web requests with several different abstractions and constructors needed by an
293/// endpoint. It is assumed to originate from an HTTP request, as defined in the scope of the rfc,
294/// but theoretically other requests are possible.
295pub trait WebRequest {
296 /// The error generated from access of malformed or invalid requests.
297 type Error;
298
299 /// The corresponding type of Responses returned from this module.
300 type Response: WebResponse<Error = Self::Error>;
301
302 /// Retrieve a parsed version of the url query.
303 ///
304 /// An Err return value indicates a malformed query or an otherwise malformed WebRequest. Note
305 /// that an empty query should result in `Ok(HashMap::new())` instead of an Err.
306 fn query(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error>;
307
308 /// Retrieve the parsed `application/x-form-urlencoded` body of the request.
309 ///
310 /// An Err value / indicates a malformed body or a different Content-Type.
311 fn urlbody(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error>;
312
313 /// Contents of the authorization header or none if none exists. An Err value indicates a
314 /// malformed header or request.
315 fn authheader(&mut self) -> Result<Option<Cow<str>>, Self::Error>;
316}
317
318/// Response representation into which the Request is transformed by the code_grant types.
319///
320/// At most one of the methods `body_text`, `body_json` will be called. Some flows will
321/// however not call any of those methods.
322pub trait WebResponse {
323 /// The error generated when trying to construct an unhandled or invalid response.
324 type Error;
325
326 /// Set the response status to 200.
327 fn ok(&mut self) -> Result<(), Self::Error>;
328
329 /// A response which will redirect the user-agent to which the response is issued.
330 fn redirect(&mut self, url: Url) -> Result<(), Self::Error>;
331
332 /// Set the response status to 400.
333 fn client_error(&mut self) -> Result<(), Self::Error>;
334
335 /// Set the response status to 401 and add a `WWW-Authenticate` header.
336 fn unauthorized(&mut self, header_value: &str) -> Result<(), Self::Error>;
337
338 /// A pure text response with no special media type set.
339 fn body_text(&mut self, text: &str) -> Result<(), Self::Error>;
340
341 /// Json repsonse data, with media type `aplication/json.
342 fn body_json(&mut self, data: &str) -> Result<(), Self::Error>;
343}
344
345/// Intermediate trait to flow specific extensions.
346///
347/// The existence of this 1) promotes writing of extensions so that they can be reused independent
348/// of endpoint and request types; 2) makes it possible to provide some of these in this library.
349///
350/// Note that all methods will by default return `None` so that adding to other flows is possible
351/// without affecting existing implementations.
352pub trait Extension {
353 /// The handler for authorization code extensions.
354 fn authorization(&mut self) -> Option<&mut dyn AuthorizationExtension> {
355 None
356 }
357
358 /// The handler for access token extensions.
359 fn access_token(&mut self) -> Option<&mut dyn AccessTokenExtension> {
360 None
361 }
362
363 /// The handler for client credentials extensions.
364 fn client_credentials(&mut self) -> Option<&mut dyn ClientCredentialsExtension> {
365 None
366 }
367}
368
369/// Fuses requests and primitives into a coherent system to give a response.
370///
371/// There are multiple different valid ways to produce responses and react to internal errors for a
372/// single request type. This trait should provide those mechanisms, including trying to recover
373/// from primitive errors where appropriate.
374///
375/// To reduce the number of necessary impls and provide a single interface to a single trait, this
376/// trait defines accessor methods for all possibly needed primitives. Note that not all flows
377/// actually access all primitives. Thus, an implementation does not necessarily have to return
378/// something in `registrar`, `authorizer`, `issuer_mut` but failing to do so will also fail flows
379/// that try to use them.
380///
381/// # Panics
382///
383/// It is expected that the endpoint primitive functions are consistent, i.e. they don't begin
384/// returning `None` after having returned `Some(registrar)` previously for example. This ensures
385/// that the checks executed by the flow preparation methods catch missing primitives. When this
386/// contract is violated, the execution of a flow may lead to a panic.
387pub trait Endpoint<Request: WebRequest> {
388 /// The error typed used as the error representation of each flow.
389 type Error;
390
391 /// A registrar if this endpoint can access one.
392 ///
393 /// Returning `None` will implicate failing any flow that requires a registrar but does not
394 /// have any effect on flows that do not require one.
395 fn registrar(&self) -> Option<&dyn Registrar>;
396
397 /// An authorizer if this endpoint can access one.
398 ///
399 /// Returning `None` will implicate failing any flow that requires an authorizer but does not
400 /// have any effect on flows that do not require one.
401 fn authorizer_mut(&mut self) -> Option<&mut dyn Authorizer>;
402
403 /// An issuer if this endpoint can access one.
404 ///
405 /// Returning `None` will implicate failing any flow that requires an issuer but does not have
406 /// any effect on flows that do not require one.
407 fn issuer_mut(&mut self) -> Option<&mut dyn Issuer>;
408
409 /// Return the system that checks owner consent.
410 ///
411 /// Returning `None` will implicated failing the authorization code flow but does have any
412 /// effect on other flows.
413 fn owner_solicitor(&mut self) -> Option<&mut dyn OwnerSolicitor<Request>>;
414
415 /// Determine the required scopes for a request.
416 ///
417 /// The client must fulfill any one scope, so returning an empty slice will always deny the
418 /// request.
419 fn scopes(&mut self) -> Option<&mut dyn Scopes<Request>>;
420
421 /// Generate a prototype response.
422 ///
423 /// The endpoint can rely on this being called at most once for each flow, if it wants
424 /// to preallocate the response or return a handle on an existing prototype.
425 fn response(
426 &mut self, request: &mut Request, kind: Template,
427 ) -> Result<Request::Response, Self::Error>;
428
429 /// Wrap an error.
430 fn error(&mut self, err: OAuthError) -> Self::Error;
431
432 /// Wrap an error in the request/response types.
433 fn web_error(&mut self, err: Request::Error) -> Self::Error;
434
435 /// Get the central extension instance this endpoint.
436 ///
437 /// Returning `None` is the default implementation and acts as simply providing any extensions.
438 fn extension(&mut self) -> Option<&mut dyn Extension> {
439 None
440 }
441}
442
443impl<'a> Template<'a> {
444 /// Create an OK template
445 pub fn new_ok() -> Self {
446 InnerTemplate::Ok.into()
447 }
448
449 /// Create a bad request template
450 pub fn new_bad(access_token_error: Option<&'a mut AccessTokenError>) -> Self {
451 InnerTemplate::BadRequest { access_token_error }.into()
452 }
453
454 /// Create an unauthorized template
455 pub fn new_unauthorized(
456 error: Option<ResourceError>, access_token_error: Option<&'a mut AccessTokenError>,
457 ) -> Self {
458 InnerTemplate::Unauthorized {
459 error,
460 access_token_error,
461 }
462 .into()
463 }
464
465 /// Create a redirect template
466 pub fn new_redirect(authorization_error: Option<&'a mut AuthorizationError>) -> Self {
467 InnerTemplate::Redirect { authorization_error }.into()
468 }
469
470 /// The corresponding status code.
471 pub fn status(&self) -> ResponseStatus {
472 match self.inner {
473 InnerTemplate::Unauthorized { .. } => ResponseStatus::Unauthorized,
474 InnerTemplate::Redirect { .. } => ResponseStatus::Redirect,
475 InnerTemplate::BadRequest { .. } => ResponseStatus::BadRequest,
476 InnerTemplate::Ok => ResponseStatus::Ok,
477 }
478 }
479
480 /// Supplementary information about an error in the authorization code flow.
481 ///
482 /// The referenced object can be inspected and manipulated to provided additional information
483 /// that is specific to this server or endpoint. Such information could be an error page with
484 /// explanatory information or a customized message.
485 ///
486 /// ```
487 /// # use oxide_auth::endpoint::Template;
488 /// fn explain(mut template: Template) {
489 /// if let Some(error) = template.authorization_error() {
490 /// eprintln!("[authorization] An error occurred: {:?}", error.kind());
491 /// error.explain("This server is still in its infancy. Sorry.");
492 /// error.explain_uri("/authorization_error.html".parse().unwrap());
493 /// }
494 /// }
495 /// ```
496 pub fn authorization_error(&mut self) -> Option<&mut AuthorizationError> {
497 match &mut self.inner {
498 InnerTemplate::Redirect {
499 authorization_error, ..
500 } => reborrow(authorization_error),
501 _ => None,
502 }
503 }
504
505 /// Supplementary information about an error in the access token flow.
506 ///
507 /// The referenced object can be inspected and manipulated to provided additional information
508 /// that is specific to this server or endpoint. Such information could be an error page with
509 /// explanatory information or a customized message.
510 ///
511 /// ```
512 /// # use oxide_auth::endpoint::Template;
513 /// fn explain(mut template: Template) {
514 /// if let Some(error) = template.access_token_error() {
515 /// eprintln!("[access_code] An error occurred: {:?}", error.kind());
516 /// error.explain("This server is still in its infancy. Sorry.");
517 /// error.explain_uri("/access_token_error.html".parse().unwrap());
518 /// }
519 /// }
520 /// ```
521 pub fn access_token_error(&mut self) -> Option<&mut AccessTokenError> {
522 match &mut self.inner {
523 InnerTemplate::Unauthorized {
524 access_token_error, ..
525 } => reborrow(access_token_error),
526 InnerTemplate::BadRequest {
527 access_token_error, ..
528 } => reborrow(access_token_error),
529 _ => None,
530 }
531 }
532}
533
534/// Reborrow contained optional reference.
535///
536/// Slightly tweaked from an `Into`, there is `Option<&'a mut T>` from `&'a mut Option<T>`.
537fn reborrow<'a, T>(opt: &'a mut Option<&mut T>) -> Option<&'a mut T> {
538 match opt {
539 // Magically does correct lifetime coercision.
540 Some(inner) => Some(inner),
541 None => None,
542 }
543}
544
545impl<'a, W: WebRequest> WebRequest for &'a mut W {
546 type Error = W::Error;
547 type Response = W::Response;
548
549 fn query(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
550 (**self).query()
551 }
552
553 fn urlbody(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
554 (**self).urlbody()
555 }
556
557 fn authheader(&mut self) -> Result<Option<Cow<str>>, Self::Error> {
558 (**self).authheader()
559 }
560}
561
562impl<'a, R: WebRequest, E: Endpoint<R>> Endpoint<R> for &'a mut E {
563 type Error = E::Error;
564
565 fn registrar(&self) -> Option<&dyn Registrar> {
566 (**self).registrar()
567 }
568
569 fn authorizer_mut(&mut self) -> Option<&mut dyn Authorizer> {
570 (**self).authorizer_mut()
571 }
572
573 fn issuer_mut(&mut self) -> Option<&mut dyn Issuer> {
574 (**self).issuer_mut()
575 }
576
577 fn owner_solicitor(&mut self) -> Option<&mut dyn OwnerSolicitor<R>> {
578 (**self).owner_solicitor()
579 }
580
581 fn scopes(&mut self) -> Option<&mut dyn Scopes<R>> {
582 (**self).scopes()
583 }
584
585 fn response(&mut self, request: &mut R, kind: Template) -> Result<R::Response, Self::Error> {
586 (**self).response(request, kind)
587 }
588
589 fn error(&mut self, err: OAuthError) -> Self::Error {
590 (**self).error(err)
591 }
592
593 fn web_error(&mut self, err: R::Error) -> Self::Error {
594 (**self).web_error(err)
595 }
596
597 fn extension(&mut self) -> Option<&mut dyn Extension> {
598 (**self).extension()
599 }
600}
601
602impl<'a, R: WebRequest, E: Endpoint<R> + 'a> Endpoint<R> for Box<E> {
603 type Error = E::Error;
604
605 fn registrar(&self) -> Option<&dyn Registrar> {
606 (**self).registrar()
607 }
608
609 fn authorizer_mut(&mut self) -> Option<&mut dyn Authorizer> {
610 (**self).authorizer_mut()
611 }
612
613 fn issuer_mut(&mut self) -> Option<&mut dyn Issuer> {
614 (**self).issuer_mut()
615 }
616
617 fn owner_solicitor(&mut self) -> Option<&mut dyn OwnerSolicitor<R>> {
618 (**self).owner_solicitor()
619 }
620
621 fn scopes(&mut self) -> Option<&mut dyn Scopes<R>> {
622 (**self).scopes()
623 }
624
625 fn response(&mut self, request: &mut R, kind: Template) -> Result<R::Response, Self::Error> {
626 (**self).response(request, kind)
627 }
628
629 fn error(&mut self, err: OAuthError) -> Self::Error {
630 (**self).error(err)
631 }
632
633 fn web_error(&mut self, err: R::Error) -> Self::Error {
634 (**self).web_error(err)
635 }
636
637 fn extension(&mut self) -> Option<&mut dyn Extension> {
638 (**self).extension()
639 }
640}
641
642impl Extension for () {}
643
644impl<'a, W: WebRequest, S: OwnerSolicitor<W> + 'a + ?Sized> OwnerSolicitor<W> for &'a mut S {
645 fn check_consent(
646 &mut self, request: &mut W, solicitation: Solicitation,
647 ) -> OwnerConsent<W::Response> {
648 (**self).check_consent(request, solicitation)
649 }
650}
651
652impl<'a, W: WebRequest, S: OwnerSolicitor<W> + 'a + ?Sized> OwnerSolicitor<W> for Box<S> {
653 fn check_consent(
654 &mut self, request: &mut W, solicitation: Solicitation,
655 ) -> OwnerConsent<W::Response> {
656 (**self).check_consent(request, solicitation)
657 }
658}
659
660impl<W: WebRequest> Scopes<W> for [Scope] {
661 fn scopes(&mut self, _: &mut W) -> &[Scope] {
662 self
663 }
664}
665
666impl<W: WebRequest> Scopes<W> for Vec<Scope> {
667 fn scopes(&mut self, _: &mut W) -> &[Scope] {
668 self.as_slice()
669 }
670}
671
672impl<'a, W: WebRequest> Scopes<W> for &'a [Scope] {
673 fn scopes(&mut self, _: &mut W) -> &[Scope] {
674 self
675 }
676}
677
678impl<'a, W: WebRequest, S: Scopes<W> + 'a + ?Sized> Scopes<W> for &'a mut S {
679 fn scopes(&mut self, request: &mut W) -> &[Scope] {
680 (**self).scopes(request)
681 }
682}
683
684impl<'a, W: WebRequest, S: Scopes<W> + 'a + ?Sized> Scopes<W> for Box<S> {
685 fn scopes(&mut self, request: &mut W) -> &[Scope] {
686 (**self).scopes(request)
687 }
688}
689
690impl<'a> From<InnerTemplate<'a>> for Template<'a> {
691 fn from(inner: InnerTemplate<'a>) -> Self {
692 Template { inner }
693 }
694}
695
696/// Check if the header is an authorization method
697pub fn is_authorization_method<'h>(header: &'h str, method: &'static str) -> Option<&'h str> {
698 let header_method = header.get(..method.len())?;
699 if header_method.eq_ignore_ascii_case(method) {
700 Some(&header[method.len()..])
701 } else {
702 None
703 }
704}