oxide_auth/code_grant/
accesstoken.rs

1//! Provides the handling for Access Token Requests
2use std::mem;
3use std::borrow::Cow;
4use std::collections::HashMap;
5
6use chrono::Utc;
7use serde::{Deserialize, Serialize};
8use serde_json;
9
10use crate::code_grant::error::{AccessTokenError, AccessTokenErrorType};
11use crate::primitives::authorizer::Authorizer;
12use crate::primitives::issuer::{IssuedToken, Issuer};
13use crate::primitives::grant::{Extensions, Grant};
14use crate::primitives::registrar::{Registrar, RegistrarError};
15use crate::primitives::scope::Scope;
16
17/// Token Response
18#[derive(Deserialize, Serialize)]
19pub struct TokenResponse {
20    /// The access token issued by the authorization server.
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub access_token: Option<String>,
23
24    /// The refresh token, which can be used to obtain new access tokens.
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub refresh_token: Option<String>,
27
28    /// The type of the token issued.
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub token_type: Option<String>,
31
32    /// The lifetime in seconds of the access token.
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub expires_in: Option<i64>,
35
36    /// The scope, which limits the permissions on the access token.
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub scope: Option<String>,
39
40    /// Error code
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub error: Option<String>,
43}
44
45/// Authorization information from the request
46#[non_exhaustive]
47pub enum Authorization<'a> {
48    /// None present
49    None,
50    /// Only a username
51    Username(Cow<'a, str>),
52    /// Username and password combination
53    UsernamePassword(Cow<'a, str>, Cow<'a, [u8]>),
54}
55
56/// Trait based retrieval of parameters necessary for access token request handling.
57pub trait Request {
58    /// Received request might not be encoded correctly. This method gives implementors the chance
59    /// to signal that a request was received but its encoding was generally malformed. If this is
60    /// the case, then no other attribute will be queried. This method exists mainly to make
61    /// frontends straightforward by not having them handle special cases for malformed requests.
62    fn valid(&self) -> bool;
63
64    /// The authorization code grant for which an access token is wanted.
65    fn code(&self) -> Option<Cow<str>>;
66
67    /// User:password of a basic authorization header.
68    fn authorization(&self) -> Authorization;
69
70    /// The client_id, optional parameter for public clients.
71    fn client_id(&self) -> Option<Cow<str>>;
72
73    /// Valid request have the redirect url used to request the authorization code grant.
74    fn redirect_uri(&self) -> Option<Cow<str>>;
75
76    /// Valid requests have this set to "authorization_code"
77    fn grant_type(&self) -> Option<Cow<str>>;
78
79    /// Retrieve an additional parameter used in an extension
80    fn extension(&self, key: &str) -> Option<Cow<str>>;
81
82    /// Credentials in body should only be enabled if use of HTTP Basic is not possible.
83    ///
84    /// Allows the request body to contain the `client_secret` as a form parameter. This is NOT
85    /// RECOMMENDED and need not be supported. The parameters MUST NOT appear in the request URI
86    /// itself.
87    ///
88    /// Under these considerations, support must be explicitely enabled.
89    fn allow_credentials_in_body(&self) -> bool {
90        false
91    }
92}
93
94/// A system of addons provided additional data.
95///
96/// An endpoint not having any extension may use `&mut ()` as the result of system.
97pub trait Extension {
98    /// Inspect the request and extension data to produce extension data.
99    ///
100    /// The input data comes from the extension data produced in the handling of the
101    /// authorization code request.
102    fn extend(&mut self, request: &dyn Request, data: Extensions)
103        -> std::result::Result<Extensions, ()>;
104}
105
106impl Extension for () {
107    fn extend(&mut self, _: &dyn Request, _: Extensions) -> std::result::Result<Extensions, ()> {
108        Ok(Extensions::new())
109    }
110}
111
112/// Required functionality to respond to access token requests.
113///
114/// Each method will only be invoked exactly once when processing a correct and authorized request,
115/// and potentially less than once when the request is faulty.  These methods should be implemented
116/// by internally using `primitives`, as it is implemented in the `frontend` module.
117pub trait Endpoint {
118    /// Get the client corresponding to some id.
119    fn registrar(&self) -> &dyn Registrar;
120
121    /// Get the authorizer from which we can recover the authorization.
122    fn authorizer(&mut self) -> &mut dyn Authorizer;
123
124    /// Return the issuer instance to create the access token.
125    fn issuer(&mut self) -> &mut dyn Issuer;
126
127    /// The system of used extension, extending responses.
128    ///
129    /// It is possible to use `&mut ()`.
130    fn extension(&mut self) -> &mut dyn Extension;
131}
132
133enum Credentials<'a> {
134    /// No credentials were offered.
135    None,
136    /// One set of credentials was offered.
137    Authenticated {
138        client_id: &'a str,
139        passphrase: &'a [u8],
140    },
141    /// No password but name was offered.
142    ///
143    /// This must happen only when the credentials were part of the request body but used to
144    /// indicate the name of a public client.
145    Unauthenticated { client_id: &'a str },
146    /// Multiple possible credentials were offered.
147    ///
148    /// This is a security issue, only one attempt must be made per request.
149    Duplicate,
150}
151
152/// Access token issuing process
153///
154/// This state machine will go through four phases. On creation, the request will be validated and
155/// parameters for the first step will be extracted from it. It will pose some requests in the form
156/// of [`Output`] which should be satisfied with the next [`Input`] data. This will eventually
157/// produce a [`BearerToken`] or an [`Error`]. Note that the executing environment will need to use
158/// a [`Registrar`], an [`Authorizer`], an optionnal [`Extension`] and an [`Issuer`] to which some
159/// requests should be forwarded.
160///
161/// [`Input`]: struct.Input.html
162/// [`Output`]: struct.Output.html
163/// [`BearerToken`]: struct.BearerToken.html
164/// [`Error`]: struct.Error.html
165/// [`Issuer`] ../primitives/issuer/trait.Issuer.html
166/// [`Registrar`] ../primitives/registrar/trait.Registrar.html
167/// [`Authorizer`] ../primitives/authorizer/trait.Authorizer.html
168/// [`Extension`] trait.Extension.html
169///
170/// A rough sketch of the operational phases:
171///
172/// 1. Ensure the request is valid based on the basic requirements (includes required parameters)
173/// 2. Try to produce a new token
174///     2.1. Authenticate the client
175///     2.2. If there was no authentication, assert token does not require authentication
176///     2.3. Recover the current grant corresponding to the `code`
177///     2.4. Check the intrinsic validity (scope)
178/// 3. Query the backend for a new (bearer) token
179pub struct AccessToken {
180    state: AccessTokenState,
181}
182
183/// Inner state machine for access token
184enum AccessTokenState {
185    /// State after the request has been validated.
186    Authenticate {
187        client: String,
188        passdata: Option<Vec<u8>>,
189        code: String,
190        // TODO: parsing here is unnecessary if we compare a string representation.
191        redirect_uri: url::Url,
192    },
193    Recover {
194        client: String,
195        code: String,
196        redirect_uri: url::Url,
197    },
198    Extend {
199        saved_params: Box<Grant>,
200        extensions: Extensions,
201    },
202    Issue {
203        grant: Box<Grant>,
204    },
205    Err(Error),
206}
207
208/// Input injected by the executor into the state machine.
209pub enum Input<'req> {
210    /// The request to be processed.
211    Request(&'req dyn Request),
212    /// Positively answer an authentication query.
213    Authenticated,
214    /// Provide the queried refresh token.
215    Recovered(Option<Box<Grant>>),
216    /// Provide extensions
217    Extended {
218        /// The grant extension
219        access_extensions: Extensions,
220    },
221    /// The token produced by the backend
222    Issued(IssuedToken),
223    /// Advance without input as far as possible, or just retrieve the output again.
224    None,
225}
226
227/// A request by the statemachine to the executor.
228///
229/// Each variant is fulfilled by certain variants of the next inputs as an argument to
230/// `AccessToken::advance`. The output of most states is simply repeated if `Input::None` is
231/// provided instead but note that the successful bearer token response is **not** repeated.
232pub enum Output<'machine> {
233    /// The registrar should authenticate a client.
234    ///
235    /// Fulfilled by `Input::Authenticated`. In an unsuccessful case, the executor should not
236    /// continue and discard the flow.
237    Authenticate {
238        /// The to-be-authenticated client.
239        client: &'machine str,
240        /// The supplied passdata/password.
241        passdata: Option<&'machine [u8]>,
242    },
243    /// The issuer should try to recover the grant for this `code`
244    ///
245    /// Fulfilled by `Input::Recovered`.
246    Recover {
247        /// The `code` from current request
248        code: &'machine str,
249    },
250    /// The extension (if any) should provide the extensions
251    ///
252    /// Fullfilled by `Input::Extended`
253    Extend {
254        /// The grant extensions if any
255        extensions: &'machine mut Extensions,
256    },
257    /// The issue should issue a new access token
258    ///
259    /// Fullfilled by `Input::Issued`
260    Issue {
261        /// The grant to be used in the token generation
262        grant: &'machine Grant,
263    },
264    /// The state machine finished and a new bearer token was generated
265    ///
266    /// This output **can not** be requested repeatedly, any future `Input` will yield a primitive
267    /// error instead.
268    Ok(BearerToken),
269    /// The state machine finished in an error.
270    ///
271    /// The error will be repeated on *any* following input.
272    Err(Box<Error>),
273}
274
275impl AccessToken {
276    /// Create the state machine. validating the request in the process
277    pub fn new(request: &dyn Request) -> Self {
278        AccessToken {
279            state: Self::validate(request).unwrap_or_else(AccessTokenState::Err),
280        }
281    }
282
283    /// Go to next state
284    pub fn advance(&mut self, input: Input) -> Output<'_> {
285        self.state = match (self.take(), input) {
286            (current, Input::None) => current,
287            (
288                AccessTokenState::Authenticate {
289                    client,
290                    code,
291                    redirect_uri,
292                    ..
293                },
294                Input::Authenticated,
295            ) => Self::authenticated(client, code, redirect_uri),
296            (
297                AccessTokenState::Recover {
298                    client, redirect_uri, ..
299                },
300                Input::Recovered(grant),
301            ) => Self::recovered(client, redirect_uri, grant).unwrap_or_else(AccessTokenState::Err),
302            (AccessTokenState::Extend { saved_params, .. }, Input::Extended { access_extensions }) => {
303                Self::issue(saved_params, access_extensions)
304            }
305            (AccessTokenState::Issue { grant }, Input::Issued(token)) => {
306                return Output::Ok(Self::finish(grant, token));
307            }
308            (AccessTokenState::Err(err), _) => AccessTokenState::Err(err),
309            (_, _) => AccessTokenState::Err(Error::Primitive(Box::new(PrimitiveError::empty()))),
310        };
311
312        self.output()
313    }
314
315    fn output(&mut self) -> Output<'_> {
316        match &mut self.state {
317            AccessTokenState::Err(err) => Output::Err(Box::new(err.clone())),
318            AccessTokenState::Authenticate { client, passdata, .. } => Output::Authenticate {
319                client,
320                passdata: passdata.as_ref().map(Vec::as_slice),
321            },
322            AccessTokenState::Recover { code, .. } => Output::Recover { code },
323            AccessTokenState::Extend { extensions, .. } => Output::Extend { extensions },
324            AccessTokenState::Issue { grant } => Output::Issue { grant },
325        }
326    }
327
328    fn take(&mut self) -> AccessTokenState {
329        mem::replace(
330            &mut self.state,
331            AccessTokenState::Err(Error::Primitive(Box::new(PrimitiveError::empty()))),
332        )
333    }
334
335    fn validate(request: &dyn Request) -> Result<AccessTokenState> {
336        if !request.valid() {
337            return Err(Error::invalid());
338        }
339
340        let authorization = request.authorization();
341        let client_id = request.client_id();
342        let client_secret = request.extension("client_secret");
343
344        let mut credentials = Credentials::None;
345
346        match &authorization {
347            Authorization::None => {}
348            Authorization::Username(username) => credentials.unauthenticated(&username),
349            Authorization::UsernamePassword(username, password) => {
350                credentials.authenticate(&username, &password)
351            }
352        }
353
354        if let Some(client_id) = &client_id {
355            match &client_secret {
356                Some(auth) if request.allow_credentials_in_body() => {
357                    credentials.authenticate(client_id.as_ref(), auth.as_ref().as_bytes())
358                }
359                // Ignore parameter if not allowed.
360                Some(_) | None => credentials.unauthenticated(client_id.as_ref()),
361            }
362        }
363
364        match request.grant_type() {
365            Some(ref cow) if cow == "authorization_code" => (),
366            None => return Err(Error::invalid()),
367            Some(_) => return Err(Error::invalid_with(AccessTokenErrorType::UnsupportedGrantType)),
368        };
369
370        let (client_id, passdata) = credentials.into_client().ok_or_else(Error::invalid)?;
371
372        let redirect_uri = request
373            .redirect_uri()
374            .ok_or_else(Error::invalid)?
375            .parse()
376            .map_err(|_| Error::invalid())?;
377
378        let code = request.code().ok_or_else(Error::invalid)?;
379
380        Ok(AccessTokenState::Authenticate {
381            client: client_id.to_string(),
382            passdata: passdata.map(Vec::from),
383            redirect_uri,
384            code: code.into_owned(),
385        })
386    }
387
388    fn authenticated(client: String, code: String, redirect_uri: url::Url) -> AccessTokenState {
389        AccessTokenState::Recover {
390            client,
391            code,
392            redirect_uri,
393        }
394    }
395
396    fn recovered(
397        client_id: String, redirect_uri: url::Url, grant: Option<Box<Grant>>,
398    ) -> Result<AccessTokenState> {
399        let mut saved_params = match grant {
400            None => return Err(Error::invalid()),
401            Some(v) => v,
402        };
403
404        if (saved_params.client_id.as_str(), &saved_params.redirect_uri) != (&client_id, &redirect_uri) {
405            return Err(Error::invalid_with(AccessTokenErrorType::InvalidGrant));
406        }
407
408        if saved_params.until < Utc::now() {
409            return Err(Error::invalid_with(AccessTokenErrorType::InvalidGrant));
410        }
411
412        let extensions = mem::take(&mut saved_params.extensions);
413        Ok(AccessTokenState::Extend {
414            saved_params,
415            extensions,
416        })
417    }
418
419    fn issue(grant: Box<Grant>, extensions: Extensions) -> AccessTokenState {
420        AccessTokenState::Issue {
421            grant: Box::new(Grant { extensions, ..*grant }),
422        }
423    }
424
425    fn finish(grant: Box<Grant>, token: IssuedToken) -> BearerToken {
426        BearerToken(token, grant.scope.clone())
427    }
428}
429
430// FiXME: use state machine instead
431/// Try to redeem an authorization code.
432pub fn access_token(handler: &mut dyn Endpoint, request: &dyn Request) -> Result<BearerToken> {
433    enum Requested<'a> {
434        None,
435        Authenticate {
436            client: &'a str,
437            passdata: Option<&'a [u8]>,
438        },
439        Recover(&'a str),
440        Extend {
441            extensions: &'a mut Extensions,
442        },
443        Issue {
444            grant: &'a Grant,
445        },
446    }
447
448    let mut access_token = AccessToken::new(request);
449    let mut requested = Requested::None;
450
451    loop {
452        let input = match requested {
453            Requested::None => Input::None,
454            Requested::Authenticate { client, passdata } => {
455                handler
456                    .registrar()
457                    .check(client, passdata)
458                    .map_err(|err| match err {
459                        RegistrarError::Unspecified => Error::unauthorized("basic"),
460                        RegistrarError::PrimitiveError => Error::Primitive(Box::new(PrimitiveError {
461                            grant: None,
462                            extensions: None,
463                        })),
464                    })?;
465                Input::Authenticated
466            }
467            Requested::Recover(code) => {
468                let opt_grant = handler.authorizer().extract(code).map_err(|_| {
469                    Error::Primitive(Box::new(PrimitiveError {
470                        grant: None,
471                        extensions: None,
472                    }))
473                })?;
474                Input::Recovered(opt_grant.map(Box::new))
475            }
476            Requested::Extend { extensions } => {
477                let access_extensions = handler
478                    .extension()
479                    .extend(request, extensions.clone())
480                    .map_err(|_| Error::invalid())?;
481                Input::Extended { access_extensions }
482            }
483            Requested::Issue { grant } => {
484                let token = handler.issuer().issue(grant.clone()).map_err(|_| {
485                    Error::Primitive(Box::new(PrimitiveError {
486                        // FIXME: endpoint should get and handle these.
487                        grant: None,
488                        extensions: None,
489                    }))
490                })?;
491                Input::Issued(token)
492            }
493        };
494
495        requested = match access_token.advance(input) {
496            Output::Authenticate { client, passdata } => Requested::Authenticate { client, passdata },
497            Output::Recover { code } => Requested::Recover(code),
498            Output::Extend { extensions } => Requested::Extend { extensions },
499            Output::Issue { grant } => Requested::Issue { grant },
500            Output::Ok(token) => return Ok(token),
501            Output::Err(e) => return Err(*e),
502        };
503    }
504}
505
506impl<'a> Credentials<'a> {
507    pub fn authenticate(&mut self, client_id: &'a str, passphrase: &'a [u8]) {
508        self.add(Credentials::Authenticated {
509            client_id,
510            passphrase,
511        })
512    }
513
514    pub fn unauthenticated(&mut self, client_id: &'a str) {
515        self.add(Credentials::Unauthenticated { client_id })
516    }
517
518    pub fn into_client(self) -> Option<(&'a str, Option<&'a [u8]>)> {
519        match self {
520            Credentials::Authenticated {
521                client_id,
522                passphrase,
523            } => Some((client_id, Some(passphrase))),
524            Credentials::Unauthenticated { client_id } => Some((client_id, None)),
525            _ => None,
526        }
527    }
528
529    fn add(&mut self, new: Self) {
530        *self = match self {
531            Credentials::None => new,
532            _ => Credentials::Duplicate,
533        };
534    }
535}
536
537/// Defines actions for the response to an access token request.
538#[derive(Clone)]
539pub enum Error {
540    /// The token did not represent a valid token.
541    Invalid(ErrorDescription),
542
543    /// The client did not properly authorize itself.
544    Unauthorized(ErrorDescription, String),
545
546    /// An underlying primitive operation did not complete successfully.
547    ///
548    /// This is expected to occur with some endpoints. See `PrimitiveError` for
549    /// more details on when this is returned.
550    Primitive(Box<PrimitiveError>),
551}
552
553/// The endpoint should have enough control over its primitives to find
554/// out what has gone wrong, e.g. they may externall supply error
555/// information.
556///
557/// In this case, all previous results returned by the primitives are
558/// included in the return value. Through this mechanism, one can
559/// accomodate async handlers by implementing a sync-based result cache
560/// that is filled with these partial values. In case only parts of the
561/// outstanding futures, invoked during internal calls, are ready the
562/// cache can be refilled through the error eliminating polls to already
563/// sucessful futures.
564///
565/// Note that `token` is not included in this list, since the handler
566/// can never fail after supplying a token to the backend.
567#[derive(Clone)]
568pub struct PrimitiveError {
569    /// The already extracted grant.
570    ///
571    /// You may reuse this, or more precisely you must to fulfill this exact request in case of
572    /// an error recovery attempt.
573    pub grant: Option<Grant>,
574
575    /// The extensions that were computed.
576    pub extensions: Option<Extensions>,
577}
578
579/// Simple wrapper around AccessTokenError to imbue the type with addtional json functionality. In
580/// addition this enforces backend specific behaviour for obtaining or handling the access error.
581#[derive(Clone)]
582pub struct ErrorDescription {
583    pub(crate) error: AccessTokenError,
584}
585
586type Result<T> = std::result::Result<T, Error>;
587
588/// Represents an access token, a refresh token and the associated scope for serialization.
589pub struct BearerToken(pub(crate) IssuedToken, pub(crate) Scope);
590
591impl Error {
592    /// Create invalid error type
593    pub fn invalid() -> Self {
594        Error::Invalid(ErrorDescription {
595            error: AccessTokenError::default(),
596        })
597    }
598
599    pub(crate) fn invalid_with(with_type: AccessTokenErrorType) -> Self {
600        Error::Invalid(ErrorDescription {
601            error: {
602                let mut error = AccessTokenError::default();
603                error.set_type(with_type);
604                error
605            },
606        })
607    }
608
609    /// Create unauthorized error type
610    pub fn unauthorized(authtype: &str) -> Error {
611        Error::Unauthorized(
612            ErrorDescription {
613                error: {
614                    let mut error = AccessTokenError::default();
615                    error.set_type(AccessTokenErrorType::InvalidClient);
616                    error
617                },
618            },
619            authtype.to_string(),
620        )
621    }
622
623    /// Get a handle to the description the client will receive.
624    ///
625    /// Some types of this error don't return any description which is represented by a `None`
626    /// result.
627    pub fn description(&mut self) -> Option<&mut AccessTokenError> {
628        match self {
629            Error::Invalid(description) => Some(description.description()),
630            Error::Unauthorized(description, _) => Some(description.description()),
631            Error::Primitive(_) => None,
632        }
633    }
634}
635
636impl PrimitiveError {
637    /// Create a default empty error
638    pub fn empty() -> Self {
639        PrimitiveError {
640            grant: None,
641            extensions: None,
642        }
643    }
644}
645
646impl ErrorDescription {
647    /// Create this from an access token error
648    pub fn new(error: AccessTokenError) -> Self {
649        Self { error }
650    }
651
652    /// Convert the error into a json string, viable for being sent over a network with
653    /// `application/json` encoding.
654    pub fn to_json(&self) -> String {
655        let asmap = self
656            .error
657            .iter()
658            .map(|(k, v)| (k.to_string(), v.into_owned()))
659            .collect::<HashMap<String, String>>();
660        serde_json::to_string(&asmap).unwrap()
661    }
662
663    /// Get a handle to the description the client will receive.
664    pub fn description(&mut self) -> &mut AccessTokenError {
665        &mut self.error
666    }
667}
668
669impl BearerToken {
670    /// Convert the token into a json string, viable for being sent over a network with
671    /// `application/json` encoding.
672    pub fn to_json(&self) -> String {
673        let remaining = self.0.until.signed_duration_since(Utc::now());
674        let token_response = TokenResponse {
675            access_token: Some(self.0.token.clone()),
676            refresh_token: self.0.refresh.clone(),
677            token_type: Some("bearer".to_owned()),
678            expires_in: Some(remaining.num_seconds()),
679            scope: Some(self.1.to_string()),
680            error: None,
681        };
682
683        serde_json::to_string(&token_response).unwrap()
684    }
685}
686
687#[cfg(test)]
688mod tests {
689    use super::*;
690    use crate::primitives::issuer::TokenType;
691
692    #[test]
693    fn bearer_token_encoding() {
694        let token = BearerToken(
695            IssuedToken {
696                token: "access".into(),
697                refresh: Some("refresh".into()),
698                until: Utc::now(),
699                token_type: TokenType::Bearer,
700            },
701            "scope".parse().unwrap(),
702        );
703
704        let json = token.to_json();
705        let token = serde_json::from_str::<TokenResponse>(&json).unwrap();
706
707        assert_eq!(token.access_token, Some("access".to_owned()));
708        assert_eq!(token.refresh_token, Some("refresh".to_owned()));
709        assert_eq!(token.scope, Some("scope".to_owned()));
710        assert_eq!(token.token_type, Some("bearer".to_owned()));
711        assert!(token.expires_in.is_some());
712    }
713
714    #[test]
715    fn no_refresh_encoding() {
716        let token = BearerToken(
717            IssuedToken::without_refresh("access".into(), Utc::now()),
718            "scope".parse().unwrap(),
719        );
720
721        let json = token.to_json();
722        let token = serde_json::from_str::<TokenResponse>(&json).unwrap();
723
724        assert_eq!(token.access_token, Some("access".to_owned()));
725        assert_eq!(token.refresh_token, None);
726        assert_eq!(token.scope, Some("scope".to_owned()));
727        assert_eq!(token.token_type, Some("bearer".to_owned()));
728        assert!(token.expires_in.is_some());
729    }
730}