openid 0.23.0

OpenID Connect & Discovery client library using async / await.
Documentation
use base64::{
    Engine as _, alphabet,
    engine::{GeneralPurpose, GeneralPurposeConfig},
};
use biscuit::SingleOrMultiple;
use url::Url;

use crate::Userinfo;

const ANYPAD: GeneralPurposeConfig = GeneralPurposeConfig::new()
    .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent);
const URL_SAFE_ANYPAD: GeneralPurpose = GeneralPurpose::new(&alphabet::URL_SAFE, ANYPAD);

/// The primary extension that OpenID Connect makes to OAuth 2.0 to enable
/// End-Users to be Authenticated is the ID Token data structure. The ID Token
/// is a security token that contains Claims about the Authentication of an
/// End-User by an Authorization Server when using a Client, and potentially
/// other requested Claims. The ID Token is represented as a JSON Web Token
/// (JWT) [JWT].
pub trait Claims {
    /// Issuer Identifier for the Issuer of the response. The iss value is a
    /// case sensitive URL using the https scheme that contains scheme, host,
    /// and optionally, port number and path components and no query or fragment
    /// components.
    fn iss(&self) -> &Url;
    /// Subject Identifier. A locally unique and never reassigned identifier
    /// within the Issuer for the End-User, which is intended to be consumed by
    /// the Client, e.g., 24400320 or AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4.
    /// It MUST NOT exceed 255 ASCII characters in length. The sub value is a
    /// case sensitive string.
    fn sub(&self) -> &str;
    /// Audience(s) that this ID Token is intended for. It MUST contain the
    /// OAuth 2.0 client_id of the Relying Party as an audience value. It MAY
    /// also contain identifiers for other audiences. In the general case, the
    /// aud value is an array of case sensitive strings. In the common special
    /// case when there is one audience, the aud value MAY be a single case
    /// sensitive string.
    fn aud(&self) -> &SingleOrMultiple<String>;
    /// Expiration time on or after which the ID Token MUST NOT be accepted for
    /// processing. The processing of this parameter requires that the current
    /// date/time MUST be before the expiration date/time listed in the value.
    /// Implementers MAY provide for some small leeway, usually no more than a
    /// few minutes, to account for clock skew. Its value is a JSON number
    /// representing the number of seconds from 1970-01-01T0:0:0Z as measured in
    /// UTC until the date/time. See RFC 3339 [RFC3339] for details regarding
    /// date/times in general and UTC in particular.
    fn exp(&self) -> i64;
    /// Time at which the JWT was issued. Its value is a JSON number
    /// representing the number of seconds from 1970-01-01T0:0:0Z as measured in
    /// UTC until the date/time.
    fn iat(&self) -> i64;
    /// Time when the End-User authentication occurred. Its value is a JSON
    /// number representing the number of seconds from 1970-01-01T0:0:0Z as
    /// measured in UTC until the date/time. When a max_age request is made or
    /// when auth_time is requested as an Essential Claim, then this Claim is
    /// REQUIRED; otherwise, its inclusion is OPTIONAL. (The auth_time Claim
    /// semantically corresponds to the OpenID 2.0 PAPE [OpenID.PAPE] auth_time
    /// response parameter.)
    fn auth_time(&self) -> Option<i64>;
    /// String value used to associate a Client session with an ID Token, and to
    /// mitigate replay attacks. The value is passed through unmodified from the
    /// Authentication Request to the ID Token. If present in the ID Token,
    /// Clients MUST verify that the nonce Claim Value is equal to the value of
    /// the nonce parameter sent in the Authentication Request. If present in
    /// the Authentication Request, Authorization Servers MUST include a nonce
    /// Claim in the ID Token with the Claim Value being the nonce value sent in
    /// the Authentication Request. Authorization Servers SHOULD perform no
    /// other processing on nonce values used. The nonce value is a case
    /// sensitive string.
    fn nonce(&self) -> Option<&String>;
    /// Access Token hash value. Its value is the base64url encoding of the
    /// left-most half of the hash of the octets of the ASCII representation of
    /// the access_token value, where the hash algorithm used is the hash
    /// algorithm used in the alg Header Parameter of the ID Token's JOSE
    /// Header. For instance, if the alg is RS256, hash the access_token value
    /// with SHA-256, then take the left-most 128 bits and base64url encode
    /// them. The at_hash value is a case sensitive string.
    fn at_hash(&self) -> Option<&String>;
    /// Code hash value. Its value is the base64url encoding of the left-most
    /// half of the hash of the octets of the ASCII representation of the code
    /// value, where the hash algorithm used is the hash algorithm used in the
    /// alg Header Parameter of the ID Token's JOSE Header. For instance, if the
    /// alg is HS512, hash the code value with SHA-512, then take the left-most
    /// 256 bits and base64url encode them. The c_hash value is a case sensitive
    /// string. If the ID Token is issued from the Authorization Endpoint
    /// with a code, which is the case for the response_type values code
    /// id_token and code id_token token, this is REQUIRED; otherwise, its
    /// inclusion is OPTIONAL.
    fn c_hash(&self) -> Option<&String>;
    /// Authentication Context Class Reference. String specifying an
    /// Authentication Context Class Reference value that identifies the
    /// Authentication Context Class that the authentication performed
    /// satisfied. The value "0" indicates the End-User authentication did not
    /// meet the requirements of ISO/IEC 29115 [ISO29115] level 1.
    /// Authentication using a long-lived browser cookie, for instance, is one
    /// example where the use of "level 0" is appropriate. Authentications with
    /// level 0 SHOULD NOT be used to authorize access to any resource of any
    /// monetary value. (This corresponds to the OpenID 2.0 PAPE [OpenID.PAPE]
    /// nist_auth_level 0.) An absolute URI or an RFC 6711 [RFC6711] registered
    /// name SHOULD be used as the acr value; registered names MUST NOT be used
    /// with a different meaning than that which is registered. Parties using
    /// this claim will need to agree upon the meanings of the values used,
    /// which may be context-specific. The acr value is a case sensitive string.
    fn acr(&self) -> Option<&String>;
    /// Authentication Methods References. JSON array of strings that are
    /// identifiers for authentication methods used in the authentication. For
    /// instance, values might indicate that both password and OTP
    /// authentication methods were used. The definition of particular values to
    /// be used in the amr Claim is beyond the scope of this specification.
    /// Parties using this claim will need to agree upon the meanings of the
    /// values used, which may be context-specific. The amr value is an array of
    /// case sensitive strings.
    fn amr(&self) -> Option<&Vec<String>>;
    /// Authorized party - the party to which the ID Token was issued. If
    /// present, it MUST contain the OAuth 2.0 Client ID of this party. This
    /// Claim is only needed when the ID Token has a single audience value and
    /// that audience is different than the authorized party. It MAY be included
    /// even when the authorized party is the same as the sole audience. The azp
    /// value is a case sensitive string containing a StringOrURI value.
    fn azp(&self) -> Option<&String>;

    /// The userinfo method returns a reference to the Userinfo struct, which
    /// contains information about the user who issued the ID Token.
    fn userinfo(&self) -> &Userinfo;

    /// 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
    fn at_hash_to_vec(&self) -> Option<Vec<u8>> {
        URL_SAFE_ANYPAD.decode(self.at_hash()?).ok()
    }
    /// Decodes c_hash. Returns None if it doesn't exist or something goes
    /// wrong.
    ///
    /// See [spec 3.3.2.11](https://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken)
    ///
    /// The returned Vec is the first 128 bits of the code hash using alg's hash
    /// alg
    fn c_hash_to_vec(&self) -> Option<Vec<u8>> {
        URL_SAFE_ANYPAD.decode(self.c_hash()?).ok()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn decode_at_hash() {
        let x = URL_SAFE_ANYPAD.decode("zglPCMCEP7ilF3LP_NExow");
        let y = URL_SAFE_ANYPAD.decode("zglPCMCEP7ilF3LP_NExow==");
        assert!(x.is_ok());
        assert!(y.is_ok());
        assert_eq!(x, y);
    }
}