diff --git a/src/crypto.rs b/src/crypto.rs index cd141ed..b5415a8 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -17,12 +17,18 @@ use validation::{Validation, validate}; /// The algorithms supported for signing/verifying #[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] pub enum Algorithm { + /// HMAC using SHA-256 HS256, + /// HMAC using SHA-384 HS384, + /// HMAC using SHA-512 HS512, + /// RSASSA-PKCS1-v1_5 using SHA-256 RS256, + /// RSASSA-PKCS1-v1_5 using SHA-384 RS384, + /// RSASSA-PKCS1-v1_5 using SHA-512 RS512, } @@ -72,7 +78,7 @@ pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result(header: &Header, claims: &T, key: &[u8]) -> Result { let encoded_header = to_jwt_part(&header)?; let encoded_claims = to_jwt_part(&claims)?; @@ -82,10 +88,11 @@ pub fn encode(header: &Header, claims: &T, key: &[u8]) -> Result Result { match algorithm { @@ -131,9 +138,9 @@ macro_rules! expect_two { }} } -/// Decode a token into a struct containing Claims and Header +/// Decode a token into a struct containing 2 fields: `claims` and `header`. /// -/// If the token or its signature is invalid, it will return an error +/// If the token or its signature is invalid or the claims fail validation, it will return an error. pub fn decode(token: &str, key: &[u8], algorithm: Algorithm, validation: Validation) -> Result> { let (signature, signing_input) = expect_two!(token.rsplitn(2, '.')); diff --git a/src/errors.rs b/src/errors.rs index c243760..729ab51 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -60,9 +60,9 @@ error_chain! { } foreign_links { - Unspecified(ring::error::Unspecified); - Base64(base64::Base64Error); - Json(serde_json::Error); - Utf8(::std::string::FromUtf8Error); + Unspecified(ring::error::Unspecified) #[doc = "An error happened while signing/verifying a token with RSA"]; + Base64(base64::Base64Error) #[doc = "An error happened while decoding some base64 text"]; + Json(serde_json::Error) #[doc = "An error happened while serializing/deserializing JSON"]; + Utf8(::std::string::FromUtf8Error) #[doc = "An error happened while trying to convert the result of base64 decoding to a String"]; } } diff --git a/src/header.rs b/src/header.rs index 0bd7703..591074d 100644 --- a/src/header.rs +++ b/src/header.rs @@ -2,35 +2,61 @@ use crypto::Algorithm; /// A basic JWT header, the alg defaults to HS256 and typ is automatically -/// set to `JWT`. All the other fields are optional +/// set to `JWT`. All the other fields are optional. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Header { + /// The type of JWS: it can only be "JWT" here + /// + /// Defined in [RFC7515#4.1.9](https://tools.ietf.org/html/rfc7515#section-4.1.9). typ: String, + /// The algorithm used + /// + /// Defined in [RFC7515#4.1.1](https://tools.ietf.org/html/rfc7515#section-4.1.1). pub alg: Algorithm, + /// Content type + /// + /// Defined in [RFC7519#5.2](https://tools.ietf.org/html/rfc7519#section-5.2). + #[serde(skip_serializing_if = "Option::is_none")] + pub cty: Option, + /// JSON Key URL + /// + /// Defined in [RFC7515#4.1.2](https://tools.ietf.org/html/rfc7515#section-4.1.2). #[serde(skip_serializing_if = "Option::is_none")] pub jku: Option, + /// Key ID + /// + /// Defined in [RFC7515#4.1.4](https://tools.ietf.org/html/rfc7515#section-4.1.4). #[serde(skip_serializing_if = "Option::is_none")] pub kid: Option, + /// X.509 URL + /// + /// Defined in [RFC7515#4.1.5](https://tools.ietf.org/html/rfc7515#section-4.1.5). #[serde(skip_serializing_if = "Option::is_none")] pub x5u: Option, + /// X.509 certificate thumbprint + /// + /// Defined in [RFC7515#4.1.7](https://tools.ietf.org/html/rfc7515#section-4.1.7). #[serde(skip_serializing_if = "Option::is_none")] pub x5t: Option, } impl Header { + /// Returns a JWT header with the algorithm given pub fn new(algorithm: Algorithm) -> Header { Header { typ: "JWT".to_string(), alg: algorithm, + cty: None, jku: None, kid: None, x5u: None, - x5t: None + x5t: None, } } } impl Default for Header { + /// Returns a JWT header using HS256 fn default() -> Header { Header::new(Algorithm::HS256) } diff --git a/src/lib.rs b/src/lib.rs index add2702..1a2567d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ //! Create and parses JWT (JSON Web Tokens) //! #![recursion_limit = "300"] +#![deny(missing_docs)] #[macro_use] extern crate error_chain; @@ -13,6 +14,7 @@ extern crate ring; extern crate untrusted; extern crate chrono; +/// All the errors, generated using error-chain pub mod errors; mod header; mod crypto; diff --git a/src/serialization.rs b/src/serialization.rs index 41df835..fe7529e 100644 --- a/src/serialization.rs +++ b/src/serialization.rs @@ -8,7 +8,7 @@ use errors::{Result}; use header::Header; -/// The return type of a successful call to decode(...) +/// The return type of a successful call to decode #[derive(Debug)] pub struct TokenData { pub header: Header, diff --git a/src/validation.rs b/src/validation.rs index 5a8a67c..4a2cf4e 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -6,20 +6,74 @@ use serde_json::map::Map; use errors::{Result, ErrorKind}; +/// Contains the various validations that are applied after decoding a token. +/// +/// All time validation happen on UTC timestamps. +/// ```rust +/// use jsonwebtoken::Validation; +/// +/// // Default value +/// let validation = Validation::default(); +/// // Changing one parameter +/// let mut validation = Validation {leeway: 1000 * 60, ..Default::default()}; +/// // Setting audience +/// let mut validation = Validation::default(); +/// validation.set_audience(&"Me"); // string +/// validation.set_audience(&["Me", "You"]); // array of strings +/// ``` #[derive(Debug, Clone, PartialEq)] pub struct Validation { + /// Add some leeway (in ms) to the `exp`, `iat` and `nbf` validation to + /// account for clock skew. + /// + /// Defaults to `0`. pub leeway: i64, + /// Whether to actually validate the signature of the token. + /// + /// WARNING: only set that to false if you know what you are doing. + /// + /// Defaults to `true`. pub validate_signature: bool, + /// Whether to validate the `exp` field. + /// + /// It will return an error if the time in the `exp` field is past. + /// + /// Defaults to `true`. pub validate_exp: bool, + /// Whether to validate the `iat` field. + /// + /// It will return an error if the time in the `iat` field is in the future. + /// + /// Defaults to `true`. pub validate_iat: bool, + /// Whether to validate the `nbf` field. + /// + /// It will return an error if the current timestamp is before the time in the `nbf` field. + /// + /// Defaults to `true`. pub validate_nbf: bool, - + /// If it contains a value, the validation will check that the `aud` field is the same as the + /// one provided and will error otherwise. + /// Since `aud` can be either a String or a Vec in the JWT spec, you will need to use + /// the [set_audience](struct.Validation.html#method.set_audience) method to set it. + /// + /// Default to `None`. pub aud: Option, + /// If it contains a value, the validation will check that the `iss` field is the same as the + /// one provided and will error otherwise. + /// + /// Default to None pub iss: Option, + /// If it contains a value, the validation will check that the `sub` field is the same as the + /// one provided and will error otherwise. + /// + /// Default to `None`. pub sub: Option, } impl Validation { + /// Since `aud` can be either a String or an array of String in the JWT spec, this method will take + /// care of serializing the value. pub fn set_audience(&mut self, audience: &T) { self.aud = Some(to_value(audience).unwrap()); }