Skip to main content

tokkit/
lib.rs

1//! # tokkit
2//!
3//! [![crates.io](https://img.shields.io/crates/v/tokkit.svg)](https://crates.io/crates/tokkit)
4//! [![docs.rs](https://docs.rs/tokkit/badge.svg)](https://docs.rs/tokkit)
5//! [![downloads](https://img.shields.io/crates/d/tokkit.svg)](https://crates.io/crates/tokkit)
6//! [![build Status](https://travis-ci.org/chridou/tokkit.svg?branch=master)](https://travis-ci.org/chridou/tokkit)
7//! [![license-mit](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/chridou/tokkit/blob/master/LICENSE-MIT)
8//! [![license-apache](http://img.shields.io/badge/license-APACHE-blue.svg)](https://github.com/chridou/tokkit/blob/master/LICENSE-APACHE)
9//!
10//! `tokkit` is a simple(even simplistic) **tok**en tool**kit** for OAUTH2 token
11//! introspection
12//!
13//! ## Adding tokkit to your project
14//!
15//! tokkit is available on [crates.io](https://crates.io/crates/tokkit).
16//!
17//! ## Documentation
18//!
19//! The documentation is available [online](https://docs.rs/tokkit).
20//!
21//! ## Features
22//!
23//! * `async`: Adds a `reqwest` based async client.
24//! See also `TokenInfoServiceClientBuilder`
25//! * `metrix`: Add support for the [metrix](https://crates.io/crates/metrix)
26//! crate(async client only)
27//! See also `TokenInfoServiceClientBuilder`
28//!
29//! ### Verify Access Tokens
30//!
31//! `tokkit` contains a module `token_info` for protected resources to verify
32//! access tokens.
33//!
34//! ```rust,no_run
35//! use tokkit::client::*;
36//! use tokkit::*;
37//!
38//! let builder = TokenInfoServiceClientBuilder::google_v3();
39//!
40//! let service = builder.build().unwrap();
41//!
42//! let token = AccessToken::new("<token>");
43//!
44//! let tokeninfo = service.introspect(&token).unwrap();
45//! ```
46//!
47//! ## Recent changes
48//! * 0.17.0
49//!    * Futures 0.3 compatibility
50//!    * Replaced hyper with reqwest
51//!    * Removed a bunch of obsolete APIs
52//! * 0.15.3
53//!    * Use reqwest 0.9
54//! * 0.15.2
55//!    * Async Client has default https connector
56//! * 0.15.1
57//!    * Async Client can be created with a given Executor
58//! * 0.15.0
59//!    * updated metrix
60//! * 0.14.0
61//!    * Add a client that takes an HttpClient as a parameter
62//! * 0.13.0
63//!    * parser does not need the http client in an Arc
64//!
65//! ## License
66//!
67//! tokkit is primarily distributed under the terms of
68//! both the MIT license and the Apache License (Version 2.0).
69//!
70//! Copyright (c) 2017 Christian Douven
71//! Token verification for protected resources on resource servers.
72//!
73//! See [OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662)
74//! and
75//! [Roles](https://tools.ietf.org/html/rfc6749#section-1.1)
76#[macro_use]
77extern crate log;
78
79#[macro_use]
80extern crate failure;
81
82use std::fmt;
83
84#[cfg(feature = "async")]
85pub mod async_client;
86pub mod client;
87mod error;
88pub mod metrics;
89pub mod parsers;
90pub mod token_manager;
91
92pub use error::{TokenInfoError, TokenInfoErrorKind, TokenInfoResult};
93
94/// An access token
95///
96/// See [RFC6749](https://tools.ietf.org/html/rfc6749#section-1.4)
97#[derive(Clone)]
98pub struct AccessToken(pub String);
99
100impl AccessToken {
101    /// Creates a new `AccessToken`
102    pub fn new<T: Into<String>>(token: T) -> Self {
103        AccessToken(token.into())
104    }
105}
106
107impl fmt::Display for AccessToken {
108    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109        write!(f, "<secret-access-token>")
110    }
111}
112
113impl fmt::Debug for AccessToken {
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115        write!(f, "AccessToken(<secret>)")
116    }
117}
118
119/// An access token scope
120///
121/// See [RFC6749](https://tools.ietf.org/html/rfc6749#page-23)
122#[derive(PartialEq, Eq, Hash, Debug, Clone)]
123pub struct Scope(pub String);
124
125impl Scope {
126    /// Creates a new `Scope`
127    pub fn new<T: Into<String>>(scope: T) -> Scope {
128        Scope(scope.into())
129    }
130}
131
132impl fmt::Display for Scope {
133    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134        write!(f, "{}", self.0)
135    }
136}
137
138/// Gives a `TokenInfo` for an `AccessToken`.
139///
140/// See [OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662)
141pub trait TokenInfoService {
142    /// Gives a `TokenInfo` for an `AccessToken`.
143    fn introspect(&self, token: &AccessToken) -> TokenInfoResult<TokenInfo>;
144}
145
146/// A `Result` where the failure is always an `InitializationError`
147pub type InitializationResult<T> = ::std::result::Result<T, InitializationError>;
148
149/// An error to be returned if the initialization of a component
150/// or else fails.
151#[derive(Debug, Fail)]
152#[fail(display = "{}", _0)]
153pub struct InitializationError(pub String);
154
155/// An id that uniquely identifies the owner of a protected resource
156#[derive(PartialEq, Eq, Hash, Debug, Clone)]
157pub struct UserId(pub String);
158
159impl UserId {
160    pub fn new<T: Into<String>>(uid: T) -> UserId {
161        UserId(uid.into())
162    }
163}
164
165impl fmt::Display for UserId {
166    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167        write!(f, "{}", self.0)
168    }
169}
170
171/// Information on an `AccessToken` returned by a `TokenInfoService`.
172///
173/// See [OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662)
174#[derive(Debug, PartialEq)]
175pub struct TokenInfo {
176    /// REQUIRED.  Boolean indicator of whether or not the presented token
177    /// is currently active.  The specifics of a token's "active" state
178    /// will vary depending on the implementation of the authorization
179    /// server and the information it keeps about its tokens, but a "true"
180    /// value return for the "active" property will generally indicate
181    /// that a given token has been issued by this authorization server,
182    /// has not been revoked by the resource owner, and is within its
183    /// given time window of validity (e.g., after its issuance time and
184    /// before its expiration time).
185    /// See [Section 4](https://tools.ietf.org/html/rfc7662#section-4)
186    /// for information on implementation of such checks.
187    pub active: bool,
188    /// OPTIONAL.  Human-readable identifier for the resource owner who
189    /// authorized this token.
190    ///
191    /// Remark: This is usually not a human readable id but a custom field
192    /// since we are in the realm of S2S authorization.
193    pub user_id: Option<UserId>,
194    /// OPTIONAL.  A JSON string containing a space-separated list of
195    /// scopes associated with this token, in the format described in
196    /// [Section 3.3](https://tools.ietf.org/html/rfc7662#section-5.1)
197    /// of OAuth 2.0 [RFC6749](https://tools.ietf.org/html/rfc6749).
198    pub scope: Vec<Scope>,
199    /// OPTIONAL.  Integer timestamp, measured in the number of seconds
200    /// since January 1 1970 UTC, indicating when this token will expire,
201    /// as defined in JWT [RFC7519](https://tools.ietf.org/html/rfc7519).
202    ///
203    /// Remark: Contains the number of seconds until the token expires.
204    /// This seems to be used by most introspection services.
205    pub expires_in_seconds: Option<u64>,
206}
207
208impl TokenInfo {
209    /// Use for authorization. Checks whether this `TokenInfo` has the given
210    /// `Scope`.
211    pub fn has_scope(&self, scope: &Scope) -> bool {
212        self.scope.iter().any(|s| s == scope)
213    }
214
215    /// Use for authorization. Checks whether this `TokenInfo` has all of the
216    /// given `Scopes`.
217    pub fn has_scopes(&self, scopes: &[Scope]) -> bool {
218        scopes.iter().all(|scope| self.has_scope(scope))
219    }
220
221    /// If the `TokenInfo` does not have the scope this method will fail.
222    pub fn must_have_scope(&self, scope: &Scope) -> ::std::result::Result<(), NotAuthorized> {
223        if self.has_scope(scope) {
224            Ok(())
225        } else {
226            Err(NotAuthorized(format!(
227                "Required scope '{}' not present.",
228                scope
229            )))
230        }
231    }
232}
233
234/// There is no authorization for the requested resource
235#[derive(Debug, Fail)]
236pub struct NotAuthorized(pub String);
237
238impl NotAuthorized {
239    pub fn new<T: Into<String>>(msg: T) -> NotAuthorized {
240        NotAuthorized(msg.into())
241    }
242}
243
244impl fmt::Display for NotAuthorized {
245    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246        write!(f, "Not authorized: {}", self.0)
247    }
248}