twist-jwt 0.3.1

An implementation of RFC7519 JSON Web Token (JWT)
//! [JSON Web Token][jwt] (JWT) implementation for twist.
//!
//! # Examples
//! ## Encode/Decode the sample token from jwt.io
//! ```
//! # extern crate twist_jwt;
//! # extern crate serde;
//! # #[macro_use]
//! # extern crate serde_derive;
//! # extern crate serde_json;
//! #
//! # use twist_jwt::{Algorithm, decode, encode, Header};
//! #
//! # pub fn main() {
//! #[derive(Clone, Deserialize, PartialEq, Serialize)]
//! pub struct Claims {
//!     pub sub: String,
//!     pub name: String,
//!     pub admin: bool,
//! }
//!
//! // Our super 'secret'
//! let secret = "secret".as_bytes();
//!
//! // Sample Token from jwt.io.
//! let actual = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.\
//!     eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.\
//!     TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
//!
//! // Default JOSE Header (alg: HS256, typ: 'JWT').
//! let mut header: Header = Default::default();
//!
//! // The jwt.io sample claims.  This can really be anything as long as its
//! // 'Clone + Deserialize + Serialize'.
//! let claims = Claims {
//!     sub: "1234567890".to_string(),
//!     name: "John Doe".to_string(),
//!     admin: true,
//! };
//!
//! // Encode the header and claims with the given secret, and ensure it's the same as the sample
//! // token.
//! let mut token = String::new();
//! match encode::jwt(&header, &claims, &secret) {
//!     Ok(tok) => {
//!         assert!(tok == actual);
//!         token = tok;
//!     },
//!     Err(_) => assert!(false),
//! }
//!
//! // Now decode the token from above into it's parts.
//! match decode::jwt::<Claims>(&token, &secret, Algorithm::HS256) {
//!     Ok(token_data) => {
//!         assert!(token_data.header() == header);
//!         assert!(token_data.claims() == claims);
//!     }
//!     Err(_) => assert!(false),
//! }
//! # }
//! ```
//!
//! ## Encode/Decode/Verify with a set of registered claims.
//! ```
//! # extern crate chrono;
//! # extern crate twist_jwt;
//! # extern crate serde;
//! # #[macro_use]
//! # extern crate serde_derive;
//! # extern crate serde_json;
//! #
//! # use chrono::{DateTime, Duration, UTC};
//! # use twist_jwt::{Algorithm, decode, encode, Header, verify};
//! #
//! # pub fn main() {
//! #[derive(Clone, Deserialize, PartialEq, Serialize)]
//! pub struct Claims {
//!     pub iss: String,
//!     pub sub: String,
//!     pub aud: String,
//!     pub exp: DateTime<UTC>,
//!     pub nbf: DateTime<UTC>,
//!     pub iat: DateTime<UTC>,
//!     pub jti: String,
//! }
//!
//! impl Default for Claims {
//!     fn default() -> Claims {
//!         Claims {
//!             iss: String::new(),
//!             sub: String::new(),
//!             aud: String::new(),
//!             exp: UTC::now(),
//!             nbf: UTC::now(),
//!             iat: UTC::now(),
//!             jti: String::new(),
//!         }
//!     }
//! }
//!
//! impl verify::HasRegistered for Claims {
//!     fn iss(&self) -> Option<String> {
//!         Some(self.iss.clone())
//!     }
//!
//!     fn sub(&self) -> Option<String> {
//!         Some(self.sub.clone())
//!     }
//!
//!     fn aud(&self) -> Option<String> {
//!         Some(self.aud.clone())
//!     }
//!
//!     fn exp(&self) -> Option<DateTime<UTC>> {
//!         Some(self.exp.clone())
//!     }
//!
//!     fn nbf(&self) -> Option<DateTime<UTC>> {
//!         Some(self.nbf.clone())
//!     }
//!
//!     fn iat(&self) -> Option<DateTime<UTC>> {
//!         Some(self.iat.clone())
//!     }
//!
//!     fn jti(&self) -> Option<String> {
//!         Some(self.jti.clone())
//!     }
//! }
//!
//! // Our super 'secret'
//! let secret = "secret".as_bytes();
//!
//! // Default JOSE Header (alg: HS256, typ: 'JWT').
//! let header: Header = Default::default();
//!
//! // Our time based claims.
//! let now: DateTime<UTC> = UTC::now();
//! let exp = now.checked_add_signed(Duration::minutes(10)).expect("invalid exp");
//! let nbf = now.checked_sub_signed(Duration::seconds(10)).expect("invalid nbf");
//!
//! // The jwt.io sample claims.  This can really be anything as long as its
//! // 'Clone + Deserialize + Serialize'.
//! let claims = Claims {
//!     iss: "me".to_string(),
//!     sub: "me".to_string(),
//!     aud: "you".to_string(),
//!     exp: exp,
//!     nbf: nbf,
//!     iat: now,
//!     jti: "my unique id".to_string(),
//! };
//!
//! // Encode the header and claims with the given secret.
//! let mut token = String::new();
//! match encode::jwt(&header, &claims, &secret) {
//!     Ok(tok) => token = tok,
//!     Err(_) => assert!(false),
//! }
//!
//! // Now decode the token from above into it's parts.
//! let mut unverified: Claims = Default::default();
//!
//! match decode::jwt::<Claims>(&token, &secret, Algorithm::HS256) {
//!     Ok(token_data) => {
//!         assert!(token_data.header() == header);
//!         assert!(token_data.claims() == claims);
//!         unverified = token_data.claims();
//!     }
//!     Err(_) => assert!(false),
//! }
//!
//! // Verify the claims
//! assert!(verify::claims(&unverified, &claims, verify::ALL));
//! // Adjust the unverified.
//! unverified.iss = "notme".to_string();
//! // Verify should fail.
//! assert!(!verify::claims(&unverified, &claims, verify::ALL));
//! # }
//! ```
//!
//! [jwt]: https://tools.ietf.org/html/rfc7519
#![deny(missing_docs)]
#![cfg_attr(feature = "cargo-clippy", allow(unseparated_literal_suffix))]
extern crate base64;
extern crate byteorder;
extern crate chrono;
extern crate ring;
extern crate serde;
extern crate serde_json;
extern crate untrusted;

#[macro_use]
extern crate bitflags;
#[macro_use]
mod macros;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate slog;
#[macro_use]
extern crate twist;

mod client;
mod error;
mod server;
mod sign;
mod util;

pub mod encode;
pub mod decode;
pub mod verify;

use error::TwistJwt;
use std::fmt;

pub use server::Jwt as ServerJwt;
pub use client::Jwt as ClientJwt;

/// This extension needs to use the `rsv2` bit in a websocket frame.
const RSV3: u8 = 1;
/// The `Sec-WebSocket-Extensions` header prefix.
const SWE: &'static str = "Sec-WebSocket-Extensions: ";
/// The `permessage-jwt` header value.
const PMJWT: &'static str = "permessage-jwt";

/// Supported [JWT signing algorithms](https://tools.ietf.org/html/rfc7518#section-3.1).
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum Algorithm {
    /// ECDSA using P-256 and SHA-256
    // ES256,
    /// ECDSA using P-384 and SHA-384
    // ES384,
    /// ECDSA using P-512 and SHA-512
    // ES512,
    /// HMAC using SHA-256
    HS256,
    /// HMAC using SHA-384
    HS384,
    /// HMAC using SHA-512
    HS512,
    /// RSASSA-PSS using SHA-256 and MGF1 with SHA-256
    PS256,
    /// RSASSA-PSS using SHA-384 and MGF1 with SHA-384
    PS384,
    /// RSASSA-PSS using SHA-512 and MGF1 with SHA-512
    PS512,
    /// RSASSA-PKCS1-v1_5 using SHA-256
    RS256,
    /// RSASSA-PKCS1-v1_5 using SHA-384
    RS384,
    /// RSASSA-PKCS1-v1_5 using SHA-512
    RS512,
}

impl Default for Algorithm {
    fn default() -> Algorithm {
        Algorithm::HS256
    }
}

impl fmt::Display for Algorithm {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Algorithm::HS256 => write!(f, "HS256"),
            Algorithm::HS384 => write!(f, "HS384"),
            Algorithm::HS512 => write!(f, "HS512"),
            Algorithm::PS256 => write!(f, "PS256"),
            Algorithm::PS384 => write!(f, "PS384"),
            Algorithm::PS512 => write!(f, "PS512"),
            Algorithm::RS256 => write!(f, "RS256"),
            Algorithm::RS384 => write!(f, "RS384"),
            Algorithm::RS512 => write!(f, "RS512"),
        }
    }
}

/// The [JOSE Header](https://tools.ietf.org/html/rfc7515#section-4) portion of the JWT.
///
/// The `alg` defaults to `HS256` and `typ` is automatically  set to `JWT`. All the other fields are
/// optional
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Header {
    /// Identifies the cryptographic algorithm used to secure the JWS.
    alg: Algorithm,
    /// Always set to 'JWT'
    typ: String,
    /// JWK Set Url: refers to a resource for a set of JSON-encoded public keys, one of which
    /// corresponds to the key used to digitally sign the JWS.
    #[serde(skip_serializing_if = "Option::is_none")]
    jku: Option<String>,
    /// JSON Web Key: the public key that corresponds to the key used to digitally sign the JWS.
    #[serde(skip_serializing_if = "Option::is_none")]
    jwk: Option<String>,
    /// Key ID: a hint indicating which key was used to secure the JWS.
    #[serde(skip_serializing_if = "Option::is_none")]
    kid: Option<String>,
    /// X.509 URL: refers to a resource for the X.509 public key certificate or certificate chain
    /// corresponding to the key used to digitally sign the JWS.
    #[serde(skip_serializing_if = "Option::is_none")]
    x5u: Option<String>,
    /// X.509 Cert Chain: contains the X.509 public key certificate or certificate chain
    /// corresponding to the key used to digitally sign the JWS.
    #[serde(skip_serializing_if = "Option::is_none")]
    x5c: Option<String>,
    /// X.509 Cert SHA-1 Thumbprint: base64url-encoded SHA-1 thumbprint (a.k.a. digest) of the DER
    /// encoding of the X.509 certificate corresponding to the key used to digitally sign the JWS.
    #[serde(skip_serializing_if = "Option::is_none")]
    x5t: Option<String>,
    /// X.509 Cert SHA-256 Thumbprint: base64url-encoded SHA-256 thumbprint (a.k.a. digest) of the
    /// DER encoding of the X.509 certificate corresponding to the key used to digitally sign the
    /// JWS.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename="x5t#S256")]
    x5ts256: Option<String>,
    /// Content Type
    #[serde(skip_serializing_if = "Option::is_none")]
    cty: Option<String>,
    /// Critical
    #[serde(skip_serializing_if = "Option::is_none")]
    crit: Option<Vec<String>>,
}

impl Header {
    /// Get the `alg` value.
    pub fn alg(&self) -> Algorithm {
        self.alg
    }

    /// Set the `alg` value.
    pub fn set_alg(&mut self, alg: Algorithm) -> &mut Header {
        self.alg = alg;
        self
    }

    /// Get the `typ` value.
    pub fn typ(&self) -> &str {
        &self.typ
    }

    /// Get the `jku` value.
    pub fn jku(&self) -> &str {
        if let Some(ref jku) = self.jku {
            jku
        } else {
            ""
        }
    }

    /// Set the `jku` value.
    pub fn set_jku(&mut self, jku: &str) -> &mut Header {
        self.jku = Some(jku.to_string());
        self
    }

    /// Get the `jwk` value.
    pub fn jwk(&self) -> &str {
        if let Some(ref jwk) = self.jwk {
            jwk
        } else {
            ""
        }
    }

    /// Set the `jwk` value.
    pub fn set_jwk(&mut self, jwk: &str) -> &mut Header {
        self.jwk = Some(jwk.to_string());
        self
    }

    /// Get the `kid` value.
    pub fn kid(&self) -> &str {
        if let Some(ref kid) = self.kid {
            kid
        } else {
            ""
        }
    }

    /// Set the `kid` value.
    pub fn set_kid(&mut self, kid: &str) -> &mut Header {
        self.kid = Some(kid.to_string());
        self
    }

    /// Get the `x5u` value.
    pub fn x5u(&self) -> &str {
        if let Some(ref x5u) = self.x5u {
            x5u
        } else {
            ""
        }
    }

    /// Set the `x5u` value.
    pub fn set_x5u(&mut self, x5u: &str) -> &mut Header {
        self.x5u = Some(x5u.to_string());
        self
    }

    /// Get the `x5c` value.
    pub fn x5c(&self) -> &str {
        if let Some(ref x5c) = self.x5c {
            x5c
        } else {
            ""
        }
    }

    /// Set the `x5c` value.
    pub fn set_x5c(&mut self, x5c: &str) -> &mut Header {
        self.x5c = Some(x5c.to_string());
        self
    }

    /// Get the `x5t` value.
    pub fn x5t(&self) -> &str {
        if let Some(ref x5t) = self.x5c {
            x5t
        } else {
            ""
        }
    }

    /// Set the `x5t` value.
    pub fn set_x5t(&mut self, x5t: &str) -> &mut Header {
        self.x5t = Some(x5t.to_string());
        self
    }

    /// Get the `x5ts256` value.
    pub fn x5ts256(&self) -> &str {
        if let Some(ref x5ts256) = self.x5ts256 {
            x5ts256
        } else {
            ""
        }
    }

    /// Set the `x5ts256` value.
    pub fn set_x5ts256(&mut self, x5ts256: &str) -> &mut Header {
        self.x5ts256 = Some(x5ts256.to_string());
        self
    }

    /// Get the `cty` value.
    pub fn cty(&self) -> &str {
        if let Some(ref cty) = self.cty {
            cty
        } else {
            ""
        }
    }

    /// Set the `cty` value.
    pub fn set_cty(&mut self, cty: &str) -> &mut Header {
        self.cty = Some(cty.to_string());
        self
    }

    /// Get the `crit` value.
    pub fn crit(&self) -> Vec<String> {
        if let Some(ref crit) = self.crit {
            crit.clone()
        } else {
            Vec::new()
        }
    }

    /// Set the `crit` value.
    pub fn set_crit(&mut self, crit: Vec<String>) -> &mut Header {
        self.crit = Some(crit);
        self
    }
}

impl Default for Header {
    fn default() -> Header {
        Header {
            alg: Algorithm::HS256,
            typ: "JWT".to_string(),
            jku: None,
            jwk: None,
            kid: None,
            x5u: None,
            x5c: None,
            x5t: None,
            x5ts256: None,
            cty: None,
            crit: None,
        }
    }
}

/// The decoded data contained in a JWT.
pub struct TokenData<T> {
    /// The `Header` data.
    header: Header,
    /// The claims.
    claims: T,
}

impl<T> TokenData<T> {
    /// Get the `header` value.
    pub fn header(&self) -> Header {
        self.header.clone()
    }

    /// Get the `claims' value.
    pub fn claims(&self) -> T
        where T: Clone
    {
        self.claims.clone()
    }
}

/// A `Result` of `Ok(T)` or `Err(TwistJwt)`.
pub type TwistJwtResult<T> = Result<T, TwistJwt>;

#[cfg(test)]
mod test {
    pub const HS256: &'static str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\
    .eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9\
    .TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
    pub const HS384: &'static str = "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.\
    DtVnCyiYCsCbg8gUP-579IC2GJ7P3CtFw6nfTTPw-0lZUzqgWAo9QIQElyxOpoRm";
    pub const HS512: &'static str = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.\
    YI0rUGDq5XdRw8vW2sDLRNFMN8Waol03iSFH8I4iLzuYK7FKHaQYWzPt0BJFGrAmKJ6SjY0mJIMZqNQJFVpkuw";
    pub const PS256_2048: &'static str = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9. ";
    pub const PS256_4096: &'static str = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9. ";
    pub const PS384_2048: &'static str = "eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9. ";
    pub const PS384_4096: &'static str = "eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9. ";
    pub const PS512_2048: &'static str = "eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9. ";
    pub const PS512_4096: &'static str = "eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9. ";
    pub const RS256_2048: &'static str = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.\
    dzhXIZsJRP0_YzFkgHm_G4r-XwIyvffdUe6fmZiaPMnbDm0H_FQtNyLkMCnDJob2qbGxbfEQLLF7O4-Xrh0mUDFn7XlLfL\
    d_22ZVemaqG0B9tPOVAFMxknjOFWSRpG35Lqvw0F4FySHUcK27nIi4nuHA2SvulvdQMtQbXNNw3umdiA32cIQYfr13WNOMC\
    68cuDIlHn0OnQQWeRlYPHQZj53U-v57WczBVGUvK5UaZEgORJrh8mBV8KznpDvDXhKsJ8BJWom8Yp-r4gD0DNcfQnCEkk72\
    WDWzRFIT2Q48lFbTBqkUgS4SCQ66zW7kz4DUFmVvfz39WrCBTkYqS81F7w";
    pub const RS256_4096: &'static str = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.\
    KncNhDRnGTTFQNyNnjXWLz95JGq6Zfj0fAYKc0tPqcj7Z5tYTdxXo8MpIGRhJf5OTRt6_aL2jPy4FCPcvaEnB2Bp4ohS07p\
    724KnEkZF4wj0WCU7y6FVSbim-k3-UCjAPTPDzRxHC4fL9as4-bVOFs6RFTJPVKyj_uDrC0Tk43ueZUX3J3sFhEJyC4OZ9j\
    MVy8tT7cU7R2-qM-hBXDO-fMgm3Ijmpa4q34VsL8GDMtLpT2CyoDxd-n92024VWhrY3drMCJD3gAy_O6UgBHVr_dfNF8att\
    or2VNBzqfwjLJ6B_HWhrrGjlzDYHqAZBmXMq-SJtPuUu6VYku2H2tnvs1MOUkBWFICLlAvkyLAy6gB-QBoSZgJdlhIYarUa\
    kJ8rKXs4Wjo98N47MAei7sM2RzrL7zMz0fIPqQhL5Y49rQW_yV2BCg7hUCwAcH6wzhgqAEXdgJigacPMCHNfbaqmJSc01JE\
    Uzi0dWX5dH8wLAQG79-Ue0sXPelM-pemvSgKUr-FLrG3j2-0r8-oGpqblPN-5SQBScNEIwonozZFuDMn5nSaaWLvxDWV9C8\
    T9TfcTh4A8ZIXpt2A0UsjXS1fWYtOSMIwkS_j3ILCL__Wj5iABls4nPFQOGOd_LTXEjiS4obyBaLPtGCT-s1qA13-1-miA8\
    wshJaY60M-bKPyNJyM";
    pub const RS384_2048: &'static str = "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.\
    YUrcB6d8nanRt1VS6YQUzjtLPxCJeqJPkk2DEtINrweltrjD-kThdhOPGtiCWSE9XIkD7TtX9X01ucdsKZlUW31aPmPa08p\
    CZK0eJ9u0r10OEZz4pyGn9xCPFgRU4di7OKQmnbMu9ufnFig5dZNh8DLyn1YHWecYFv4kAaaWiF7x8ygUkbUxA8ugXHY2g9\
    qC5Oit8BHbe4FgNOeszvTBGEIiH9T1c7OepwSeGKXqb98n3zWAox_MPOTOvYGzljx_3HutHkmXiC2d3rgr8fiS9XDpqbv-l\
    kMX8eBXOeKjjzsWB7qcF4ZK53btMwzhy6sHkNe6wFFqSCCe7yDzh-ugbA";
    pub const RS384_4096: &'static str = "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.\
    lsfKu9H1rPtc1gmPfFUTj9LjuxT_apUlk1MuoYMwerMPnBzjblAKZsgmC-S6inMblkFiFhriZ_5sRNXtZB0etKCM4Ea53YF\
    5J8H5_7JBAtwv6OL0hZTxV4RvGMuVoaTJAmDD7S1Y6mdrGjWpuyIoOuAQ0eJh9RiWfMgX7eqeuPRd3Ti09pcxgh_zdbPcsR\
    BybSrVgMQBfcyOMyzJq6irG4IS97p-ka6BIqJ7Lw1pgtV2USs7fbyoEkaEriXQzX7pppjce0lZVWbZ0uoiJXi2wypK3nNK6\
    kd7bmphJ7B2pieU74c5hCZ75uoAm26lOX40XT86FAz0XS7Go3CFJK2PbWdte6Tesc-Xk1ghktG2x5tJy0tkMiwYX955iro4\
    sl44ZT0cXhWEYdxw5yyJFoboeg1Uk5lmrEMB9UrwWvxpC0C7JOy95tojjpImUJgU62U7UebZn-XrNOduAOYlMXJiJ4a7vwN\
    blWIoTkgWuIlTrNy2SzTf8X5gvb67Nc9DjV0nJRfhuNnd_IcwVfG61Vdb13FekG4TsVWbkItRG2l6_MPOQDYXF91hg7xWAm\
    SK2WJazK4FI9G1_L4Np9LjSvbQ_AFTKffcFPyWdJiJef0vH6vk7P0W0Ea-T8RCQS8_NuA0Cx12zhICXPUyw4Rj3SySTVbwh\
    aPjDrB0cgtNXlWYOWY";
    pub const RS512_2048: &'static str = "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.\
    G3HMiEggAaMlswaWMcWp71-fFJ_8FFbBvyx_AMLolbnnbbYXyT9T1y_481OVOYRx95xhZY_iXHlrIdb4Dfu3jqbp59J67ti\
    LcAZF6xzNz5_bWTeXAMRhkF-RsnQBew-Dmy5tPgLhwERnXWNNWXCtlxi7JLm-ZAbcQwDmpvuWZj4Wwsc197uTfftS_tLMhX\
    M2nAaQgie57sN59mm-YxQrGwHhroA2GPB9UzJ_orntbVRG2PcDQeNhnDzckSsn2TsrvLMne-KUzYmt8yoFd0DikQZEhGlaV\
    OLtvoFSnEKU254YdvS7SKE7l1HkY_irfWDBxpDwFYyXg_gxan5Wwozatg";
    pub const RS512_4096: &'static str = "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.\
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.\
    S5ccfJeLu5sBWjYwBsw0Ccb16vEyDEFXn-drBRp8k6aoA1U5Lp2CUuZWKzNiHb66OSjQb-o-Vdd5FBODxZevJa2pn4KCO0Z\
    0PD00c2aqT0-1Ail0DHgHZL4xqZOOJ4ysvaX3fUzzT-tAEaywkfFQS6VKOBkToiFFgMlKZUowxbpEqJrFFYSkrEIVvGSgYF\
    tNBysylEUk82Q8Go5WY7MVzd-knhcvuz6Ko3sesvDFQEiIqrEYoll5oQERJ-a4FlLfOsi0FlkG6-96BdgMyG_gjKnL74wUP\
    cAhWkylqP9a0y3eLmazPezudaTTqf3CzfuC1I9nopBcweXeVWKJcz1lkdDdMj8Ht1_VbWzxzRQQdgo4QIlWGJSuieOBJLHf\
    2mH2aJqDcggez5OUnU1YUGIntfs3ce4-leYb77K_lVPI6BpzF6CSIJKadOxnx2aUkz54WlL7f471JcVAEpTYbihqAu-rhIY\
    YjLASRKb0Zq5wWYqjQYW1KWZ8-p97zlDqYQe4cZNHeIRYqTszdBR8oP8fkl-C8QNk7YDRZ7F1OiDUgUPgqy_TBmm6cjvHiq\
    00qqzGcgF-a3nmq25ZMPi0-bEYh4qxsqK1k9JNBqj5DO8fQ1ftdEbgNfo6SwB-XZaM4zxREoZwHIe6D1sFLHcCyt1rvSmZk\
    41TOUnU0WIBOK98qu8";
    pub const ISS_ONLY: &'static str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.\
    eyJpc3MiOiJlbGxtYWsuaW8ifQ.\
    6OOeJ0wqExhY-_UqHJfeMPDlzMFoZLm2-ce8YnQmvyQ";

    #[derive(Clone, Deserialize, Serialize)]
    pub struct Claims {
        pub sub: String,
        pub name: String,
        pub admin: bool,
    }
}