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
use base64;
use biscuit::{CompactJson, Empty, SingleOrMultiple};
use inth_oauth2::client::response::{FromResponse, ParseError};
use inth_oauth2::token::{self, Bearer, Expiring};
use reqwest::Url;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use url_serde;

pub use biscuit::jws::Compact as Jws;

type IdToken = Jws<Claims, Empty>;

/// ID Token contents. [See spec.](https://openid.net/specs/openid-connect-basic-1_0.html#IDToken)
#[derive(Deserialize, Serialize)]
pub struct Claims {
    #[serde(with = "url_serde")]
    pub iss: Url,
    // Max 255 ASCII chars
    // Can't deserialize a [u8; 255]
    pub sub: String,
    // Either an array of audiences, or just the client_id
    pub aud: SingleOrMultiple<String>,
    // Not perfectly accurate for what time values we can get back...
    // By spec, this is an arbitrarilly large number. In practice, an
    // i64 unix time is up to 293 billion years from 1970.
    //
    // Make sure this cannot silently underflow, see:
    // https://github.com/serde-rs/json/blob/8e01f44f479b3ea96b299efc0da9131e7aff35dc/src/de.rs#L341
    pub exp: i64,
    pub iat: i64,
    // required for max_age request
    #[serde(default)]
    pub auth_time: Option<i64>,
    #[serde(default)]
    pub nonce: Option<String>,
    // base64 encoded, need to decode it!
    #[serde(default)]
    at_hash: Option<String>,
    #[serde(default)]
    pub acr: Option<String>,
    #[serde(default)]
    pub amr: Option<Vec<String>>,
    // If exists, must be client_id
    #[serde(default)]
    pub azp: Option<String>,
}

impl Claims {
    /// Decodes at_hash. Returns None if it doesn't exist or something goes wrong.
    ///
    /// See [spec 3.1.3.6](https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken)
    ///
    /// The returned Vec is the first 128 bits of the access token hash using alg's hash alg
    pub fn at_hash(&self) -> Option<Vec<u8>> {
        if let Some(ref hash) = self.at_hash {
            return base64::decode_config(hash.as_str(), base64::URL_SAFE).ok();
        }
        None
    }
}

// THIS IS CRAZY VOODOO WITCHCRAFT MAGIC
impl CompactJson for Claims {}

/// An OpenID Connect token. This is the only token allowed by spec.
/// Has an access_token for bearer, and the id_token for authentication.
/// Wraps an oauth bearer token.
#[derive(Serialize, Deserialize)]
pub struct Token {
    bearer: Bearer<Expiring>,
    pub id_token: IdToken,
}

impl Token {
    // Takes a json response object and parses out the id token
    // TODO Support extracting a jwe token according to spec. Right now we only support jws tokens.
    fn id_token(json: &Value) -> Result<IdToken, ParseError> {
        let obj = json.as_object().ok_or(ParseError::ExpectedType("object"))?;
        let token = obj
            .get("id_token")
            .and_then(Value::as_str)
            .ok_or(ParseError::ExpectedFieldType("id_token", "string"))?;
        Ok(Jws::new_encoded(token))
    }
}

impl token::Token<Expiring> for Token {
    fn access_token(&self) -> &str {
        self.bearer.access_token()
    }
    fn scope(&self) -> Option<&str> {
        self.bearer.scope()
    }
    fn lifetime(&self) -> &Expiring {
        self.bearer.lifetime()
    }
}

impl FromResponse for Token {
    fn from_response(json: &Value) -> Result<Self, ParseError> {
        let bearer = Bearer::from_response(json)?;
        let id_token = Self::id_token(json)?;
        Ok(Self { bearer, id_token })
    }

    fn from_response_inherit(json: &Value, prev: &Self) -> Result<Self, ParseError> {
        let bearer = Bearer::from_response_inherit(json, &prev.bearer)?;
        let id_token = Self::id_token(json)?;
        Ok(Self { bearer, id_token })
    }
}