actix_web_httpauth/extractors/bearer.rs
1//! Extractor for the "Bearer" HTTP Authentication Scheme.
2
3use std::{borrow::Cow, default::Default};
4
5use actix_utils::future::{ready, Ready};
6use actix_web::{dev::Payload, http::header::Header, FromRequest, HttpRequest};
7
8use super::{config::AuthExtractorConfig, errors::AuthenticationError};
9pub use crate::headers::www_authenticate::bearer::Error;
10use crate::headers::{authorization, www_authenticate::bearer};
11
12/// [`BearerAuth`] extractor configuration.
13#[derive(Debug, Clone, Default)]
14pub struct Config(bearer::Bearer);
15
16impl Config {
17 /// Set challenge `scope` attribute.
18 ///
19 /// The `"scope"` attribute is a space-delimited list of case-sensitive
20 /// scope values indicating the required scope of the access token for
21 /// accessing the requested resource.
22 pub fn scope<T: Into<Cow<'static, str>>>(mut self, value: T) -> Config {
23 self.0.scope = Some(value.into());
24 self
25 }
26
27 /// Set challenge `realm` attribute.
28 ///
29 /// The "realm" attribute indicates the scope of protection in the manner
30 /// described in HTTP/1.1 [RFC 2617](https://tools.ietf.org/html/rfc2617#section-1.2).
31 pub fn realm<T: Into<Cow<'static, str>>>(mut self, value: T) -> Config {
32 self.0.realm = Some(value.into());
33 self
34 }
35}
36
37impl AsRef<bearer::Bearer> for Config {
38 fn as_ref(&self) -> &bearer::Bearer {
39 &self.0
40 }
41}
42
43impl AuthExtractorConfig for Config {
44 type Inner = bearer::Bearer;
45
46 fn into_inner(self) -> Self::Inner {
47 self.0
48 }
49}
50
51/// Extractor for HTTP Bearer auth
52///
53/// # Examples
54/// ```
55/// use actix_web_httpauth::extractors::bearer::BearerAuth;
56///
57/// async fn index(auth: BearerAuth) -> String {
58/// format!("Hello, user with token {}!", auth.token())
59/// }
60/// ```
61///
62/// If authentication fails, this extractor fetches the [`Config`] instance
63/// from the [app data] in order to properly form the `WWW-Authenticate`
64/// response header.
65///
66/// # Examples
67/// ```
68/// use actix_web::{web, App};
69/// use actix_web_httpauth::extractors::bearer::{self, BearerAuth};
70///
71/// async fn index(auth: BearerAuth) -> String {
72/// format!("Hello, {}!", auth.token())
73/// }
74///
75/// App::new()
76/// .app_data(
77/// bearer::Config::default()
78/// .realm("Restricted area")
79/// .scope("email photo"),
80/// )
81/// .service(web::resource("/index.html").route(web::get().to(index)));
82/// ```
83#[derive(Debug, Clone)]
84pub struct BearerAuth(authorization::Bearer);
85
86impl BearerAuth {
87 /// Returns bearer token provided by client.
88 pub fn token(&self) -> &str {
89 self.0.token()
90 }
91}
92
93impl FromRequest for BearerAuth {
94 type Future = Ready<Result<Self, Self::Error>>;
95 type Error = AuthenticationError<bearer::Bearer>;
96
97 fn from_request(req: &HttpRequest, _payload: &mut Payload) -> <Self as FromRequest>::Future {
98 ready(
99 authorization::Authorization::<authorization::Bearer>::parse(req)
100 .map(|auth| BearerAuth(auth.into_scheme()))
101 .map_err(|_| {
102 let bearer = req
103 .app_data::<Config>()
104 .map(|config| config.0.clone())
105 .unwrap_or_default();
106
107 AuthenticationError::new(bearer)
108 }),
109 )
110 }
111}
112
113/// Extended error customization for HTTP `Bearer` auth.
114impl AuthenticationError<bearer::Bearer> {
115 /// Attach `Error` to the current Authentication error.
116 ///
117 /// Error status code will be changed to the one provided by the `kind`
118 /// Error.
119 pub fn with_error(mut self, kind: Error) -> Self {
120 *self.status_code_mut() = kind.status_code();
121 self.challenge_mut().error = Some(kind);
122 self
123 }
124
125 /// Attach error description to the current Authentication error.
126 pub fn with_error_description<T>(mut self, desc: T) -> Self
127 where
128 T: Into<Cow<'static, str>>,
129 {
130 self.challenge_mut().error_description = Some(desc.into());
131 self
132 }
133
134 /// Attach error URI to the current Authentication error.
135 ///
136 /// It is up to implementor to provide properly formed absolute URI.
137 pub fn with_error_uri<T>(mut self, uri: T) -> Self
138 where
139 T: Into<Cow<'static, str>>,
140 {
141 self.challenge_mut().error_uri = Some(uri.into());
142 self
143 }
144}