tokkit/lib.rs
1//! # tokkit
2//!
3//! [](https://crates.io/crates/tokkit)
4//! [](https://docs.rs/tokkit)
5//! [](https://crates.io/crates/tokkit)
6//! [](https://travis-ci.org/chridou/tokkit)
7//! [](https://github.com/chridou/tokkit/blob/master/LICENSE-MIT)
8//! [](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}