Initial commit
This commit is contained in:
commit
7585a7f0f9
|
@ -0,0 +1,12 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
|
@ -0,0 +1,2 @@
|
|||
target
|
||||
Cargo.lock
|
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "jwt"
|
||||
version = "0.1.0"
|
||||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
description = "Create and parse JWT."
|
||||
keywords = ["jwt", "web", "api", "token"]
|
||||
|
||||
[dependencies]
|
||||
rustc-serialize = "0.3"
|
||||
clippy = {version = "0.0.22", optional = true}
|
||||
|
||||
[features]
|
||||
default = []
|
||||
dev = ["clippy"]
|
|
@ -0,0 +1,6 @@
|
|||
# JWT
|
||||
|
||||
```
|
||||
JWT::encode(payload, secret, algo) -> Result<String>
|
||||
JWT::decode(token, secret, algo) -> Result<Claims>
|
||||
```
|
|
@ -0,0 +1,101 @@
|
|||
use std::collections::BTreeMap;
|
||||
use rustc_serialize::json::{self, Json, ToJson};
|
||||
use rustc_serialize::base64::{self, ToBase64, FromBase64};
|
||||
|
||||
use errors::Error;
|
||||
|
||||
macro_rules! add_registered_claims {
|
||||
($map: expr, $key: expr, $reg: expr) => {
|
||||
if let Some(val) = $reg {
|
||||
$map.insert($key, val.to_json());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, RustcEncodable, RustcDecodable)]
|
||||
struct RegisteredClaims {
|
||||
iss: Option<String>,
|
||||
sub: Option<String>,
|
||||
aud: Option<String>,
|
||||
exp: Option<u64>,
|
||||
nbf: Option<u64>,
|
||||
iat: Option<u64>,
|
||||
jti: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Claims {
|
||||
registered: RegisteredClaims,
|
||||
private: BTreeMap<String, Json>,
|
||||
}
|
||||
|
||||
impl Claims {
|
||||
pub fn new() -> Claims {
|
||||
Claims {
|
||||
registered: Default::default(),
|
||||
private: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_base64(self) -> Result<String, Error> {
|
||||
let mut map: BTreeMap<String, Json> = BTreeMap::new();
|
||||
// just encoding the struct would give null values in the resulting json
|
||||
// like {"iss": null}, which we don't want
|
||||
add_registered_claims!(map, "iss".to_owned(), self.registered.iss);
|
||||
add_registered_claims!(map, "sub".to_owned(), self.registered.sub);
|
||||
add_registered_claims!(map, "aud".to_owned(), self.registered.aud);
|
||||
add_registered_claims!(map, "exp".to_owned(), self.registered.exp);
|
||||
add_registered_claims!(map, "nbf".to_owned(), self.registered.nbf);
|
||||
add_registered_claims!(map, "iat".to_owned(), self.registered.iat);
|
||||
add_registered_claims!(map, "jti".to_owned(), self.registered.jti);
|
||||
|
||||
map.extend(self.private);
|
||||
let encoded = try!(json::encode(&map));
|
||||
Ok(encoded.as_bytes().to_base64(base64::STANDARD))
|
||||
}
|
||||
|
||||
fn from_base64(encoded String) -> Result<Claims, Error> {
|
||||
|
||||
}
|
||||
|
||||
pub fn add<T: ToJson>(&mut self, key: String, value: T) {
|
||||
self.private.insert(key, value.to_json());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use claims::Claims;
|
||||
use rustc_serialize::json::{ToJson};
|
||||
|
||||
#[test]
|
||||
fn to_base64_no_null_values() {
|
||||
let mut claims = Claims::new();
|
||||
claims.registered.iss = Some("JWT".to_owned());
|
||||
let result = claims.to_base64().unwrap();
|
||||
let expected = "eyJpc3MiOiJKV1QifQ==";
|
||||
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_base64_custom_claims() {
|
||||
let mut claims = Claims::new();
|
||||
claims.add::<String>("group".to_owned(), "zombie".to_owned());
|
||||
let result = claims.to_base64().unwrap();
|
||||
let expected = "eyJncm91cCI6InpvbWJpZSJ9";
|
||||
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_base64_registered_and_customs() {
|
||||
let mut claims = Claims::new();
|
||||
claims.registered.iss = Some("JWT".to_owned());
|
||||
claims.add::<String>("group".to_owned(), "zombie".to_owned());
|
||||
let result = claims.to_base64().unwrap();
|
||||
let expected = "eyJncm91cCI6InpvbWJpZSIsImlzcyI6IkpXVCJ9";
|
||||
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
use std::string;
|
||||
use rustc_serialize::{json, base64};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
EncodeJSON(json::EncoderError),
|
||||
DecodeBase64(base64::FromBase64Error),
|
||||
DecodeJSON(json::DecoderError),
|
||||
Utf8(string::FromUtf8Error),
|
||||
}
|
||||
|
||||
macro_rules! impl_from_error {
|
||||
($f: ty, $e: expr) => {
|
||||
impl From<$f> for Error {
|
||||
fn from(f: $f) -> Error { $e(f) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_error!(json::EncoderError, Error::EncodeJSON);
|
||||
impl_from_error!(base64::FromBase64Error, Error::DecodeBase64);
|
||||
impl_from_error!(json::DecoderError, Error::DecodeJSON);
|
||||
impl_from_error!(string::FromUtf8Error, Error::Utf8);
|
|
@ -0,0 +1,58 @@
|
|||
use rustc_serialize::json;
|
||||
use rustc_serialize::base64::{self, ToBase64, FromBase64};
|
||||
|
||||
use errors::Error;
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq, RustcEncodable, RustcDecodable)]
|
||||
pub struct Header {
|
||||
typ: Option<String>,
|
||||
alg: String,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn new(algorithm: String) -> Header {
|
||||
Header {
|
||||
typ: Some("JWT".to_owned()),
|
||||
alg: algorithm,
|
||||
}
|
||||
}
|
||||
pub fn to_base64(&self) -> Result<String, Error> {
|
||||
let encoded = try!(json::encode(&self));
|
||||
Ok(encoded.as_bytes().to_base64(base64::STANDARD))
|
||||
}
|
||||
|
||||
pub fn from_base64(encoded: String) -> Result<Header, Error> {
|
||||
let decoded = try!(encoded.as_bytes().from_base64());
|
||||
let s = try!(String::from_utf8(decoded));
|
||||
Ok(try!(json::decode(&s)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use header::Header;
|
||||
|
||||
#[test]
|
||||
fn to_base64() {
|
||||
let expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9".to_owned();
|
||||
let result = Header::new("HS256".to_owned()).to_base64();
|
||||
|
||||
assert_eq!(expected, result.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_base64() {
|
||||
let encoded = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9".to_owned();
|
||||
let header = Header::from_base64(encoded).unwrap();
|
||||
|
||||
assert_eq!(header.typ.unwrap(), "JWT");
|
||||
assert_eq!(header.alg, "HS256");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
let header = Header::new("HS256".to_owned());
|
||||
assert_eq!(Header::from_base64(header.to_base64().unwrap()).unwrap(), header);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
//! Create and parses JWT (JSON Web Tokens)
|
||||
//!
|
||||
|
||||
// #![deny(
|
||||
// missing_docs,
|
||||
// missing_debug_implementations, missing_copy_implementations,
|
||||
// trivial_casts, trivial_numeric_casts,
|
||||
// unsafe_code,
|
||||
// unstable_features,
|
||||
// unused_import_braces, unused_qualifications
|
||||
// )]
|
||||
|
||||
#![cfg_attr(feature = "dev", allow(unstable_features))]
|
||||
#![cfg_attr(feature = "dev", feature(plugin))]
|
||||
#![cfg_attr(feature = "dev", plugin(clippy))]
|
||||
|
||||
extern crate rustc_serialize;
|
||||
|
||||
pub mod errors;
|
||||
pub mod header;
|
||||
pub mod claims;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Algorithm {
|
||||
HS256
|
||||
}
|
||||
|
||||
impl ToString for Algorithm {
|
||||
fn to_string(&self) -> String {
|
||||
match *self {
|
||||
Algorithm::HS256 => "HS256".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn encode(secret: String, algorithm: Algorithm) -> String {
|
||||
|
||||
// }
|
||||
|
||||
// pub fn decode(token: String, secret: String, algorithm: Algorithm) -> Result<int> {
|
||||
|
||||
// }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
}
|
Loading…
Reference in New Issue