Add some docs

This commit is contained in:
Vincent Prouillet 2017-04-13 10:08:07 +09:00
parent 50d676865f
commit 35fd9d63cc
6 changed files with 101 additions and 12 deletions

View File

@ -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<Str
}
}
/// Encode the claims passed and sign the payload using the algorithm from the header and the key
/// Encode the header and claims given and sign the payload using the algorithm from the header and the key
pub fn encode<T: Serialize>(header: &Header, claims: &T, key: &[u8]) -> Result<String> {
let encoded_header = to_jwt_part(&header)?;
let encoded_claims = to_jwt_part(&claims)?;
@ -82,10 +88,11 @@ pub fn encode<T: Serialize>(header: &Header, claims: &T, key: &[u8]) -> Result<S
Ok([signing_input, signature].join("."))
}
/// Compares the signature given with a re-computed signature for HMAC or using the public key (`key`)
/// Compares the signature given with a re-computed signature for HMAC or using the public key
/// for RSA
///
/// `signature` is the signature part of a jwt (text after the second '.')
///
/// `signing_input` is base64(header) + "." + base64(claims)
pub fn verify(signature: &str, signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<bool> {
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<T: Deserialize>(token: &str, key: &[u8], algorithm: Algorithm, validation: Validation) -> Result<TokenData<T>> {
let (signature, signing_input) = expect_two!(token.rsplitn(2, '.'));

View File

@ -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"];
}
}

View File

@ -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<String>,
/// 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<String>,
/// 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<String>,
/// 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<String>,
/// 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<String>,
}
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)
}

View File

@ -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;

View File

@ -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<T: Deserialize> {
pub header: Header,

View File

@ -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<String> 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<Value>,
/// 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<String>,
/// 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<String>,
}
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<T: Serialize>(&mut self, audience: &T) {
self.aud = Some(to_value(audience).unwrap());
}