1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
//! # tokkit
//!
//! [![crates.io](https://img.shields.io/crates/v/tokkit.svg)](https://crates.io/crates/tokkit)
//! [![docs.rs](https://docs.rs/tokkit/badge.svg)](https://docs.rs/tokkit)
//! [![downloads](https://img.shields.io/crates/d/tokkit.svg)](https://crates.io/crates/tokkit)
//! [![build Status](https://travis-ci.org/chridou/tokkit.svg?branch=master)](https://travis-ci.org/chridou/tokkit)
//! [![license-mit](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/chridou/tokkit/blob/master/LICENSE-MIT)
//! [![license-apache](http://img.shields.io/badge/license-APACHE-blue.svg)](https://github.com/chridou/tokkit/blob/master/LICENSE-APACHE)
//!
//! `tokkit` is a simple(even simplistic) **tok**en tool**kit** for OAUTH2 token
//! introspection
//!
//! ## Adding tokkit to your project
//!
//! tokkit is available on [crates.io](https://crates.io/crates/tokkit).
//!
//! ## Documentation
//!
//! The documentation is available [online](https://docs.rs/tokkit).
//!
//! ## Features
//!
//! * `async`: Adds a `hyper` based async client.
//! See also `TokenInfoServiceClientBuilder`
//! * `metrix`: Add support for the [metrix](https://crates.io/crates/metrix)
//! crate(async client only)
//! See also `TokenInfoServiceClientBuilder`
//!
//! ### Verify Access Tokens
//!
//! `tokkit` contains a module `token_info` for protected resources to verify
//! access tokens.
//!
//! ```rust,no_run
//! use tokkit::client::*;
//! use tokkit::*;
//!
//! let builder = TokenInfoServiceClientBuilder::google_v3();
//!
//! let service = builder.build().unwrap();
//!
//! let token = AccessToken::new("<token>");
//!
//! let tokeninfo = service.introspect(&token).unwrap();
//! ```
//!
//! ## Recent changes
//!
//! * 0.15.3
//!    * Use reqwest 0.9
//! * 0.15.2
//!    * Async Client has default https connector
//! * 0.15.1
//!    * Async Client can be created with a given Executor
//! * 0.15.0
//!    * updated metrix
//! * 0.14.0
//!    * Add a client that takes an HttpClient as a parameter
//! * 0.13.0
//!    * parser does not need the http client in an Arc
//!
//! ## License
//!
//! tokkit is primarily distributed under the terms of
//! both the MIT license and the Apache License (Version 2.0).
//!
//! Copyright (c) 2017 Christian Douven
//! Token verification for protected resources on resource servers.
//!
//! See [OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662)
//! and
//! [Roles](https://tools.ietf.org/html/rfc6749#section-1.1)
#[macro_use]
extern crate log;

extern crate backoff;
#[macro_use]
extern crate failure;
extern crate json;
extern crate reqwest;
extern crate url;

#[cfg(feature = "async")]
extern crate futures;
#[cfg(feature = "async")]
extern crate http;
#[cfg(feature = "async")]
extern crate hyper;
#[cfg(feature = "async")]
extern crate hyper_tls;
#[cfg(feature = "metrix")]
extern crate metrix;
#[cfg(feature = "async")]
extern crate tokio_retry;

use std::fmt;

#[cfg(feature = "async")]
pub mod async_client;
pub mod client;
mod error;
pub mod metrics;
pub mod parsers;
pub mod token_manager;

pub use error::{TokenInfoError, TokenInfoErrorKind, TokenInfoResult};

/// An access token
///
/// See [RFC6749](https://tools.ietf.org/html/rfc6749#section-1.4)
#[derive(Clone)]
pub struct AccessToken(pub String);

impl AccessToken {
    /// Creates a new `AccessToken`
    pub fn new<T: Into<String>>(token: T) -> Self {
        AccessToken(token.into())
    }
}

impl fmt::Display for AccessToken {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<secret-access-token>")
    }
}

impl fmt::Debug for AccessToken {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "AccessToken(<secret>)")
    }
}

/// An access token scope
///
/// See [RFC6749](https://tools.ietf.org/html/rfc6749#page-23)
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
pub struct Scope(pub String);

impl Scope {
    /// Creates a new `Scope`
    pub fn new<T: Into<String>>(scope: T) -> Scope {
        Scope(scope.into())
    }
}

impl fmt::Display for Scope {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Gives a `TokenInfo` for an `AccessToken`.
///
/// See [OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662)
pub trait TokenInfoService {
    /// Gives a `TokenInfo` for an `AccessToken`.
    fn introspect(&self, token: &AccessToken) -> TokenInfoResult<TokenInfo>;
}

/// A `Result` where the failure is always an `InitializationError`
pub type InitializationResult<T> = ::std::result::Result<T, InitializationError>;

/// An error to be returned if the initialization of a component
/// or else fails.
#[derive(Debug, Fail)]
#[fail(display = "{}", _0)]
pub struct InitializationError(pub String);

/// An id that uniquely identifies the owner of a protected resource
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
pub struct UserId(pub String);

impl UserId {
    pub fn new<T: Into<String>>(uid: T) -> UserId {
        UserId(uid.into())
    }
}

impl fmt::Display for UserId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Information on an `AccessToken` returned by a `TokenInfoService`.
///
/// See [OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662)
#[derive(Debug, PartialEq)]
pub struct TokenInfo {
    /// REQUIRED.  Boolean indicator of whether or not the presented token
    /// is currently active.  The specifics of a token's "active" state
    /// will vary depending on the implementation of the authorization
    /// server and the information it keeps about its tokens, but a "true"
    /// value return for the "active" property will generally indicate
    /// that a given token has been issued by this authorization server,
    /// has not been revoked by the resource owner, and is within its
    /// given time window of validity (e.g., after its issuance time and
    /// before its expiration time).
    /// See [Section 4](https://tools.ietf.org/html/rfc7662#section-4)
    /// for information on implementation of such checks.
    pub active: bool,
    /// OPTIONAL.  Human-readable identifier for the resource owner who
    /// authorized this token.
    ///
    /// Remark: This is usually not a human readable id but a custom field
    /// since we are in the realm of S2S authorization.
    pub user_id: Option<UserId>,
    /// OPTIONAL.  A JSON string containing a space-separated list of
    /// scopes associated with this token, in the format described in
    /// [Section 3.3](https://tools.ietf.org/html/rfc7662#section-5.1)
    /// of OAuth 2.0 [RFC6749](https://tools.ietf.org/html/rfc6749).
    pub scope: Vec<Scope>,
    /// OPTIONAL.  Integer timestamp, measured in the number of seconds
    /// since January 1 1970 UTC, indicating when this token will expire,
    /// as defined in JWT [RFC7519](https://tools.ietf.org/html/rfc7519).
    ///
    /// Remark: Contains the number of seconds until the token expires.
    /// This seems to be used by most introspection services.
    pub expires_in_seconds: Option<u64>,
}

impl TokenInfo {
    /// Use for authorization. Checks whether this `TokenInfo` has the given
    /// `Scope`.
    pub fn has_scope(&self, scope: &Scope) -> bool {
        self.scope.iter().find(|&s| s == scope).is_some()
    }

    /// Use for authorization. Checks whether this `TokenInfo` has all of the
    /// given `Scopes`.
    pub fn has_scopes(&self, scopes: &[Scope]) -> bool {
        scopes.iter().all(|scope| self.has_scope(scope))
    }

    /// If the `TokenInfo` does not have the scope this method will fail.
    pub fn must_have_scope(&self, scope: &Scope) -> ::std::result::Result<(), NotAuthorized> {
        if self.has_scope(scope) {
            Ok(())
        } else {
            Err(NotAuthorized(format!(
                "Required scope '{}' not present.",
                scope
            )))
        }
    }
}

/// There is no authorization for the requested resource
#[derive(Debug, Fail)]
pub struct NotAuthorized(pub String);

impl NotAuthorized {
    pub fn new<T: Into<String>>(msg: T) -> NotAuthorized {
        NotAuthorized(msg.into())
    }
}

impl fmt::Display for NotAuthorized {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Not authorized: {}", self.0)
    }
}