Merge pull request #6 from Keats/header
Add all params of header from RFC
This commit is contained in:
commit
0ea17b5bd9
22
README.md
22
README.md
|
@ -18,19 +18,21 @@ In terms of imports:
|
||||||
extern crate jsonwebtoken as jwt;
|
extern crate jsonwebtoken as jwt;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
|
|
||||||
use jwt::{encode, decode, Algorithm};
|
use jwt::{encode, decode, Header, Algorithm};
|
||||||
```
|
```
|
||||||
|
|
||||||
### Encoding
|
### Encoding
|
||||||
```rust
|
```rust
|
||||||
let token = encode(&my_claims, "secret", Algorithm::HS256);
|
let token = encode(Header::default(), &my_claims, "secret".as_ref()).unwrap();
|
||||||
```
|
```
|
||||||
In that example, `my_claims` is an instance of the Claims struct.
|
In that example, `my_claims` is an instance of the Claims struct.
|
||||||
The struct you are using for your claims should derive `RustcEncodable` and `RustcDecodable`.
|
The struct you are using for your claims should derive `RustcEncodable` and `RustcDecodable`.
|
||||||
|
The default algorithm is HS256. Look at custom headers section to see how to change that.
|
||||||
|
|
||||||
### Decoding
|
### Decoding
|
||||||
```rust
|
```rust
|
||||||
let claims = decode::<Claims>(&token, "secret", Algorithm::HS256);
|
let token = decode::<Claims>(&token, "secret", Algorithm::HS256).unwrap();
|
||||||
|
// token is a struct with 2 params: header and claims
|
||||||
```
|
```
|
||||||
In addition to the normal base64/json decoding errors, `decode` can return two custom errors:
|
In addition to the normal base64/json decoding errors, `decode` can return two custom errors:
|
||||||
|
|
||||||
|
@ -42,12 +44,20 @@ In addition to the normal base64/json decoding errors, `decode` can return two c
|
||||||
Right now, the library only validates the algorithm type used but does not verify claims such as expiration.
|
Right now, the library only validates the algorithm type used but does not verify claims such as expiration.
|
||||||
Feel free to add a `validate` method to your claims struct to handle that.
|
Feel free to add a `validate` method to your claims struct to handle that.
|
||||||
|
|
||||||
|
### Custom headers
|
||||||
|
All the parameters from the RFC are supported but the default header only has `typ` and `alg` set: all the other fields are optional.
|
||||||
|
If you want to set the `kid` parameter for example:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let mut header = Header::default();
|
||||||
|
header.kid = Some("blabla".to_owned());
|
||||||
|
header.alg = Algorithm::HS512;
|
||||||
|
let token = encode(header, &my_claims, "secret".as_ref()).unwrap();
|
||||||
|
```
|
||||||
|
|
||||||
## Algorithms
|
## Algorithms
|
||||||
Right now, only SHA family is supported: SHA256, SHA384 and SHA512.
|
Right now, only SHA family is supported: SHA256, SHA384 and SHA512.
|
||||||
|
|
||||||
## Missing
|
|
||||||
The header is currently not customisable and therefore does not support things like kid right now.
|
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
On my thinkpad 440s for a 2 claims struct using SHA256:
|
On my thinkpad 440s for a 2 claims struct using SHA256:
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ extern crate test;
|
||||||
extern crate jsonwebtoken as jwt;
|
extern crate jsonwebtoken as jwt;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
|
|
||||||
use jwt::{encode, decode, Algorithm};
|
use jwt::{encode, decode, Algorithm, Header};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, RustcEncodable, RustcDecodable)]
|
#[derive(Debug, PartialEq, Clone, RustcEncodable, RustcDecodable)]
|
||||||
struct Claims {
|
struct Claims {
|
||||||
|
@ -18,11 +18,11 @@ fn bench_encode(b: &mut test::Bencher) {
|
||||||
company: "ACME".to_owned()
|
company: "ACME".to_owned()
|
||||||
};
|
};
|
||||||
|
|
||||||
b.iter(|| encode(&claim, "secret", Algorithm::HS256));
|
b.iter(|| encode(Header::default(), &claim, "secret".as_ref()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_decode(b: &mut test::Bencher) {
|
fn bench_decode(b: &mut test::Bencher) {
|
||||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
|
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
|
||||||
b.iter(|| decode::<Claims>(token, "secret", Algorithm::HS256));
|
b.iter(|| decode::<Claims>(token, "secret".as_ref(), Algorithm::HS256));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,47 @@
|
||||||
extern crate jsonwebtoken as jwt;
|
extern crate jsonwebtoken as jwt;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
|
|
||||||
use jwt::{encode, decode, Algorithm};
|
use jwt::{encode, decode, Header, Algorithm};
|
||||||
use jwt::errors::{Error};
|
use jwt::errors::{Error};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, RustcEncodable, RustcDecodable)]
|
#[derive(Debug, RustcEncodable, RustcDecodable)]
|
||||||
struct Claims {
|
struct Claims {
|
||||||
sub: String,
|
sub: String,
|
||||||
company: String
|
company: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Example validation implementation
|
||||||
|
impl Claims {
|
||||||
|
fn is_valid(self) -> bool {
|
||||||
|
if self.company != "ACME".to_owned() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// expiration etc
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let my_claims = Claims {
|
let my_claims = Claims {
|
||||||
sub: "b@b.com".to_owned(),
|
sub: "b@b.com".to_owned(),
|
||||||
company: "ACME".to_owned()
|
company: "ACME".to_owned()
|
||||||
};
|
};
|
||||||
let key = "secret";
|
let key = "secret";
|
||||||
let token = match encode(&my_claims, key, Algorithm::HS256) {
|
let token = match encode(Header::default(), &my_claims, key.as_ref()) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(_) => panic!() // in practice you would return the error
|
Err(_) => panic!() // in practice you would return the error
|
||||||
};
|
};
|
||||||
|
|
||||||
let claims = match decode::<Claims>(&token, key.as_ref(), Algorithm::HS256) {
|
let token_data = match decode::<Claims>(&token, key.as_ref(), Algorithm::HS256) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
Error::InvalidToken => panic!(), // Example on how to handle a specific error
|
Error::InvalidToken => panic!(), // Example on how to handle a specific error
|
||||||
_ => panic!()
|
_ => panic!()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
println!("{:?}", token_data.claims);
|
||||||
|
println!("{:?}", token_data.header);
|
||||||
|
println!("{:?}", token_data.claims.is_valid());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
extern crate jsonwebtoken as jwt;
|
||||||
|
extern crate rustc_serialize;
|
||||||
|
|
||||||
|
use jwt::{encode, decode, Header, Algorithm};
|
||||||
|
use jwt::errors::{Error};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, RustcEncodable, RustcDecodable)]
|
||||||
|
struct Claims {
|
||||||
|
sub: String,
|
||||||
|
company: String
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let my_claims = Claims {
|
||||||
|
sub: "b@b.com".to_owned(),
|
||||||
|
company: "ACME".to_owned()
|
||||||
|
};
|
||||||
|
let key = "secret";
|
||||||
|
|
||||||
|
let mut header = Header::default();
|
||||||
|
header.kid = Some("signing_key".to_owned());
|
||||||
|
header.alg = Algorithm::HS512;
|
||||||
|
|
||||||
|
let token = match encode(header, &my_claims, key.as_ref()) {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(_) => panic!() // in practice you would return the error
|
||||||
|
};
|
||||||
|
|
||||||
|
let token_data = match decode::<Claims>(&token, key.as_ref(), Algorithm::HS512) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(err) => match err {
|
||||||
|
Error::InvalidToken => panic!(), // Example on how to handle a specific error
|
||||||
|
_ => panic!()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("{:?}", token_data.claims);
|
||||||
|
println!("{:?}", token_data.header);
|
||||||
|
}
|
154
src/lib.rs
154
src/lib.rs
|
@ -19,7 +19,7 @@ use crypto::util::fixed_time_eq;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
use errors::Error;
|
use errors::Error;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone, RustcDecodable, RustcEncodable)]
|
||||||
/// The algorithms supported for signing/verifying
|
/// The algorithms supported for signing/verifying
|
||||||
pub enum Algorithm {
|
pub enum Algorithm {
|
||||||
HS256,
|
HS256,
|
||||||
|
@ -51,46 +51,42 @@ impl<T> Part for T where T: Encodable + Decodable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, RustcDecodable, RustcEncodable)]
|
||||||
/// A basic JWT header part, the alg is automatically filled for use
|
/// A basic JWT header part, the alg defaults to HS256 and typ is automatically
|
||||||
/// It's missing things like the kid but that's for later
|
/// set to `JWT`. All the other fields are optional
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
typ: &'static str,
|
typ: String,
|
||||||
alg: Algorithm,
|
pub alg: Algorithm,
|
||||||
|
pub jku: Option<String>,
|
||||||
|
pub kid: Option<String>,
|
||||||
|
pub x5u: Option<String>,
|
||||||
|
pub x5t: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Header {
|
impl Header {
|
||||||
pub fn new(algorithm: Algorithm) -> Header {
|
pub fn new(algorithm: Algorithm) -> Header {
|
||||||
Header {
|
Header {
|
||||||
typ: "JWT",
|
typ: "JWT".to_owned(),
|
||||||
alg: algorithm,
|
alg: algorithm,
|
||||||
|
jku: None,
|
||||||
|
kid: None,
|
||||||
|
x5u: None,
|
||||||
|
x5t: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Part for Header {
|
impl Default for Header {
|
||||||
type Encoded = &'static str;
|
fn default() -> Header {
|
||||||
|
Header::new(Algorithm::HS256)
|
||||||
fn from_base64<B: AsRef<[u8]>>(encoded: B) -> Result<Self, Error> where Self: Sized {
|
|
||||||
let algoritm = match encoded.as_ref() {
|
|
||||||
b"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" => { Algorithm::HS256 },
|
|
||||||
b"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9" => { Algorithm::HS384 },
|
|
||||||
b"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9" => { Algorithm::HS512 },
|
|
||||||
_ => return Err(Error::InvalidToken)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Header::new(algoritm))
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn to_base64(&self) -> Result<Self::Encoded, Error> {
|
#[derive(Debug)]
|
||||||
let encoded = match self.alg {
|
/// The return type of a successful call to decode(...)
|
||||||
Algorithm::HS256 => { "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" },
|
pub struct TokenData<T: Part> {
|
||||||
Algorithm::HS384 => { "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9" },
|
pub header: Header,
|
||||||
Algorithm::HS512 => { "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9" },
|
pub claims: T
|
||||||
};
|
|
||||||
|
|
||||||
Ok(encoded)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take the payload of a JWT and sign it using the algorithm given.
|
/// Take the payload of a JWT and sign it using the algorithm given.
|
||||||
|
@ -114,38 +110,32 @@ fn verify(signature: &str, data: &str, secret: &[u8], algorithm: Algorithm) -> b
|
||||||
fixed_time_eq(signature.as_ref(), sign(data, secret, algorithm).as_ref())
|
fixed_time_eq(signature.as_ref(), sign(data, secret, algorithm).as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the claims passed and sign the payload using the algorithm and the secret
|
/// Encode the claims passed and sign the payload using the algorithm from the header and the secret
|
||||||
pub fn encode<T: Part, B: AsRef<[u8]>>(claims: &T, secret: B, algorithm: Algorithm) -> Result<String, Error> {
|
pub fn encode<T: Part>(header: Header, claims: &T, secret: &[u8]) -> Result<String, Error> {
|
||||||
let encoded_header = try!(Header::new(algorithm).to_base64());
|
let encoded_header = try!(header.to_base64());
|
||||||
let encoded_claims = try!(claims.to_base64());
|
let encoded_claims = try!(claims.to_base64());
|
||||||
// seems to be a tiny bit faster than format!("{}.{}", x, y)
|
// seems to be a tiny bit faster than format!("{}.{}", x, y)
|
||||||
let payload = [encoded_header, encoded_claims.as_ref()].join(".");
|
let payload = [encoded_header.as_ref(), encoded_claims.as_ref()].join(".");
|
||||||
let signature = sign(&*payload, secret.as_ref(), algorithm);
|
let signature = sign(&*payload, secret.as_ref(), header.alg);
|
||||||
|
|
||||||
Ok([payload, signature].join("."))
|
Ok([payload, signature].join("."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used in decode: takes the result of a rsplit and ensure we only get 2 parts
|
||||||
|
/// Errors if we don't
|
||||||
|
macro_rules! expect_two {
|
||||||
|
($iter:expr) => {{
|
||||||
|
let mut i = $iter; // evaluate the expr
|
||||||
|
match (i.next(), i.next(), i.next()) {
|
||||||
|
(Some(first), Some(second), None) => (first, second),
|
||||||
|
_ => return Err(Error::InvalidToken)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
/// Decode a token into a Claims struct
|
/// Decode a token into a Claims struct
|
||||||
/// If the token or its signature is invalid, it will return an error
|
/// If the token or its signature is invalid, it will return an error
|
||||||
pub fn decode<T: Part>(token: &str, secret: &[u8], algorithm: Algorithm) -> Result<T, Error> {
|
pub fn decode<T: Part>(token: &str, secret: &[u8], algorithm: Algorithm) -> Result<TokenData<T>, Error> {
|
||||||
// We don't use AsRef<[u8]> for `secret` because it would require changing this:
|
|
||||||
//
|
|
||||||
// decode::<MyStruct>(...)
|
|
||||||
//
|
|
||||||
// to:
|
|
||||||
//
|
|
||||||
// decode::<MyStruct, _>(...)
|
|
||||||
|
|
||||||
macro_rules! expect_two {
|
|
||||||
($iter:expr) => {{
|
|
||||||
let mut i = $iter; // evaluate the expr
|
|
||||||
match (i.next(), i.next(), i.next()) {
|
|
||||||
(Some(first), Some(second), None) => (first, second),
|
|
||||||
_ => return Err(Error::InvalidToken)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (signature, payload) = expect_two!(token.rsplitn(2, '.'));
|
let (signature, payload) = expect_two!(token.rsplitn(2, '.'));
|
||||||
|
|
||||||
let is_valid = verify(
|
let is_valid = verify(
|
||||||
|
@ -165,13 +155,14 @@ pub fn decode<T: Part>(token: &str, secret: &[u8], algorithm: Algorithm) -> Resu
|
||||||
if header.alg != algorithm {
|
if header.alg != algorithm {
|
||||||
return Err(Error::WrongAlgorithmHeader);
|
return Err(Error::WrongAlgorithmHeader);
|
||||||
}
|
}
|
||||||
|
let decoded_claims = try!(T::from_base64(claims));
|
||||||
|
|
||||||
T::from_base64(claims)
|
Ok(TokenData { header: header, claims: decoded_claims})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{encode, decode, Algorithm, Header, Part, sign, verify};
|
use super::{encode, decode, Algorithm, Header, sign, verify};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, RustcEncodable, RustcDecodable)]
|
#[derive(Debug, PartialEq, Clone, RustcEncodable, RustcDecodable)]
|
||||||
struct Claims {
|
struct Claims {
|
||||||
|
@ -179,29 +170,6 @@ mod tests {
|
||||||
company: String
|
company: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn to_base64() {
|
|
||||||
let expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9".to_owned();
|
|
||||||
let result = Header::new(Algorithm::HS256).to_base64();
|
|
||||||
|
|
||||||
assert_eq!(expected, result.unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn from_base64() {
|
|
||||||
let encoded = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9";
|
|
||||||
let header = Header::from_base64(encoded).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(header.typ, "JWT");
|
|
||||||
assert_eq!(header.alg, Algorithm::HS256);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn round_trip_base64() {
|
|
||||||
let header = Header::new(Algorithm::HS256);
|
|
||||||
assert_eq!(Header::from_base64(header.to_base64().unwrap()).unwrap(), header);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sign_hs256() {
|
fn sign_hs256() {
|
||||||
let result = sign("hello world", b"secret", Algorithm::HS256);
|
let result = sign("hello world", b"secret", Algorithm::HS256);
|
||||||
|
@ -216,15 +184,31 @@ mod tests {
|
||||||
assert!(valid);
|
assert!(valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encode_with_custom_header() {
|
||||||
|
// TODO: test decode value
|
||||||
|
let my_claims = Claims {
|
||||||
|
sub: "b@b.com".to_owned(),
|
||||||
|
company: "ACME".to_owned()
|
||||||
|
};
|
||||||
|
let mut header = Header::default();
|
||||||
|
header.kid = Some("kid".to_owned());
|
||||||
|
let token = encode(header, &my_claims, "secret".as_ref()).unwrap();
|
||||||
|
let token_data = decode::<Claims>(&token, "secret".as_ref(), Algorithm::HS256).unwrap();
|
||||||
|
assert_eq!(my_claims, token_data.claims);
|
||||||
|
assert_eq!("kid", token_data.header.kid.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn round_trip_claim() {
|
fn round_trip_claim() {
|
||||||
let my_claims = Claims {
|
let my_claims = Claims {
|
||||||
sub: "b@b.com".to_owned(),
|
sub: "b@b.com".to_owned(),
|
||||||
company: "ACME".to_owned()
|
company: "ACME".to_owned()
|
||||||
};
|
};
|
||||||
let token = encode(&my_claims, "secret", Algorithm::HS256).unwrap();
|
let token = encode(Header::default(), &my_claims, "secret".as_ref()).unwrap();
|
||||||
let claims = decode::<Claims>(&token, "secret".as_ref(), Algorithm::HS256).unwrap();
|
let token_data = decode::<Claims>(&token, "secret".as_ref(), Algorithm::HS256).unwrap();
|
||||||
assert_eq!(my_claims, claims);
|
assert_eq!(my_claims, token_data.claims);
|
||||||
|
assert!(token_data.header.kid.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -243,11 +227,17 @@ mod tests {
|
||||||
claims.unwrap();
|
claims.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decode_token_with_bytes_secret() {
|
fn decode_token_with_bytes_secret() {
|
||||||
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiY29tcGFueSI6Ikdvb2dvbCJ9.27QxgG96vpX4akKNpD1YdRGHE3_u2X35wR3EHA2eCrs";
|
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiY29tcGFueSI6Ikdvb2dvbCJ9.27QxgG96vpX4akKNpD1YdRGHE3_u2X35wR3EHA2eCrs";
|
||||||
let claims = decode::<Claims>(token, b"\x01\x02\x03", Algorithm::HS256);
|
let claims = decode::<Claims>(token, b"\x01\x02\x03", Algorithm::HS256);
|
||||||
claims.unwrap();
|
assert!(claims.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decode_token_with_shuffled_header_fields() {
|
||||||
|
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjb21wYW55IjoiMTIzNDU2Nzg5MCIsInN1YiI6IkpvaG4gRG9lIn0.SEIZ4Jg46VGhquuwPYDLY5qHF8AkQczF14aXM3a2c28";
|
||||||
|
let claims = decode::<Claims>(token, "secret".as_ref(), Algorithm::HS256);
|
||||||
|
assert!(claims.is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue