oxide_auth/primitives/
authorizer.rs

1//! Authorizers are need to exchange code grants for bearer tokens.
2//!
3//! The role of an authorizer is the ensure the consistency and security of request in which a
4//! client is willing to trade a code grant for a bearer token. As such, it will first issue grants
5//! to client according to parameters given by the resource owner and the registrar. Upon a client
6//! side request, it will then check the given parameters to determine the authorization of such
7//! clients.
8use std::collections::HashMap;
9use std::sync::{MutexGuard, RwLockWriteGuard};
10
11use super::grant::Grant;
12use super::generator::TagGrant;
13
14/// Authorizers create and manage authorization codes.
15///
16/// The authorization code can be traded for a bearer token at the token endpoint.
17pub trait Authorizer {
18    /// Create a code which allows retrieval of a bearer token at a later time.
19    fn authorize(&mut self, _: Grant) -> Result<String, ()>;
20
21    /// Retrieve the parameters associated with a token, invalidating the code in the process. In
22    /// particular, a code should not be usable twice (there is no stateless implementation of an
23    /// authorizer for this reason).
24    fn extract(&mut self, token: &str) -> Result<Option<Grant>, ()>;
25}
26
27/// An in-memory hash map.
28///
29/// This authorizer saves a mapping of generated strings to their associated grants. The generator
30/// is itself trait based and can be chosen during construction. It is assumed to not be possible
31/// for two different grants to generate the same token in the issuer.
32pub struct AuthMap<I: TagGrant = Box<dyn TagGrant + Send + Sync + 'static>> {
33    tagger: I,
34    usage: u64,
35    tokens: HashMap<String, Grant>,
36}
37
38impl<I: TagGrant> AuthMap<I> {
39    /// Create an authorizer generating tokens with the `tagger`.
40    ///
41    /// The token map is initially empty and is filled by methods provided in its [`Authorizer`]
42    /// implementation.
43    ///
44    /// [`Authorizer`]: ./trait.Authorizer.html
45    pub fn new(tagger: I) -> Self {
46        AuthMap {
47            tagger,
48            usage: 0,
49            tokens: HashMap::new(),
50        }
51    }
52}
53
54impl<'a, A: Authorizer + ?Sized> Authorizer for &'a mut A {
55    fn authorize(&mut self, grant: Grant) -> Result<String, ()> {
56        (**self).authorize(grant)
57    }
58
59    fn extract(&mut self, code: &str) -> Result<Option<Grant>, ()> {
60        (**self).extract(code)
61    }
62}
63
64impl<A: Authorizer + ?Sized> Authorizer for Box<A> {
65    fn authorize(&mut self, grant: Grant) -> Result<String, ()> {
66        (**self).authorize(grant)
67    }
68
69    fn extract(&mut self, code: &str) -> Result<Option<Grant>, ()> {
70        (**self).extract(code)
71    }
72}
73
74impl<'a, A: Authorizer + ?Sized> Authorizer for MutexGuard<'a, A> {
75    fn authorize(&mut self, grant: Grant) -> Result<String, ()> {
76        (**self).authorize(grant)
77    }
78
79    fn extract(&mut self, code: &str) -> Result<Option<Grant>, ()> {
80        (**self).extract(code)
81    }
82}
83
84impl<'a, A: Authorizer + ?Sized> Authorizer for RwLockWriteGuard<'a, A> {
85    fn authorize(&mut self, grant: Grant) -> Result<String, ()> {
86        (**self).authorize(grant)
87    }
88
89    fn extract(&mut self, code: &str) -> Result<Option<Grant>, ()> {
90        (**self).extract(code)
91    }
92}
93
94impl<I: TagGrant> Authorizer for AuthMap<I> {
95    fn authorize(&mut self, grant: Grant) -> Result<String, ()> {
96        // The (usage, grant) tuple needs to be unique. Since this wraps after 2^64 operations, we
97        // expect the validity time of the grant to have changed by then. This works when you don't
98        // set your system time forward/backward ~20billion seconds, assuming ~10^9 operations per
99        // second.
100        let next_usage = self.usage.wrapping_add(1);
101        let token = self.tagger.tag(next_usage - 1, &grant)?;
102        self.tokens.insert(token.clone(), grant);
103        self.usage = next_usage;
104        Ok(token)
105    }
106
107    fn extract<'a>(&mut self, grant: &'a str) -> Result<Option<Grant>, ()> {
108        Ok(self.tokens.remove(grant))
109    }
110}
111
112#[cfg(test)]
113/// Tests for authorizer implementations, including those provided here.
114pub mod tests {
115    use super::*;
116    use chrono::Utc;
117    use crate::primitives::grant::Extensions;
118    use crate::primitives::generator::{Assertion, AssertionKind, RandomGenerator};
119
120    /// Tests some invariants that should be upheld by all authorizers.
121    ///
122    /// Custom implementations may want to import and use this in their own tests.
123    pub fn simple_test_suite(authorizer: &mut dyn Authorizer) {
124        let grant = Grant {
125            owner_id: "Owner".to_string(),
126            client_id: "Client".to_string(),
127            scope: "One two three scopes".parse().unwrap(),
128            redirect_uri: "https://example.com/redirect_me".parse().unwrap(),
129            until: Utc::now(),
130            extensions: Extensions::new(),
131        };
132
133        let token = authorizer
134            .authorize(grant.clone())
135            .expect("Authorization should not fail here");
136        let recovered_grant = authorizer
137            .extract(&token)
138            .expect("Primitive failed extracting grant")
139            .expect("Could not extract grant for valid token");
140
141        if grant != recovered_grant {
142            panic!("Grant was not stored correctly");
143        }
144
145        if authorizer.extract(&token).unwrap().is_some() {
146            panic!("Token must only be usable once");
147        }
148
149        // Authorize the same token again.
150        let token_again = authorizer
151            .authorize(grant.clone())
152            .expect("Authorization should not fail here");
153        // We don't produce the same token twice.
154        assert_ne!(token, token_again);
155    }
156
157    #[test]
158    fn random_test_suite() {
159        let mut storage = AuthMap::new(RandomGenerator::new(16));
160        simple_test_suite(&mut storage);
161    }
162
163    #[test]
164    fn signing_test_suite() {
165        let assertion = Assertion::new(
166            AssertionKind::HmacSha256,
167            b"7EGgy8zManReq9l/ez0AyYE+xPpcTbssgW+8gBnIv3s=",
168        );
169        let mut storage = AuthMap::new(assertion);
170        simple_test_suite(&mut storage);
171    }
172
173    #[test]
174    #[should_panic]
175    fn bad_generator() {
176        struct BadGenerator;
177        impl TagGrant for BadGenerator {
178            fn tag(&mut self, _: u64, _: &Grant) -> Result<String, ()> {
179                Ok("YOLO.HowBadCanItBeToRepeatTokens?".into())
180            }
181        }
182
183        let mut storage = AuthMap::new(BadGenerator);
184        simple_test_suite(&mut storage);
185    }
186}