Update docs
This commit is contained in:
parent
8e4757cb1d
commit
51dacd9bc4
116
README.md
116
README.md
|
@ -9,8 +9,7 @@ Add the following to Cargo.toml:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
jsonwebtoken = "7"
|
jsonwebtoken = "7"
|
||||||
serde_derive = "1"
|
serde = {version = "1.0", features = ["derive"] }
|
||||||
serde = "1"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
@ -18,11 +17,8 @@ Complete examples are available in the examples directory: a basic one and one w
|
||||||
|
|
||||||
In terms of imports and structs:
|
In terms of imports and structs:
|
||||||
```rust
|
```rust
|
||||||
extern crate jsonwebtoken as jwt;
|
use serde::{Serialize, Deserialize};
|
||||||
#[macro_use]
|
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation};
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
use jwt::{encode, decode, Header, Algorithm, Validation};
|
|
||||||
|
|
||||||
/// Our claims struct, it needs to derive `Serialize` and/or `Deserialize`
|
/// Our claims struct, it needs to derive `Serialize` and/or `Deserialize`
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
@ -33,7 +29,7 @@ struct Claims {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Encoding
|
### Header
|
||||||
The default algorithm is HS256.
|
The default algorithm is HS256.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -45,38 +41,84 @@ All the parameters from the RFC are supported but the default header only has `t
|
||||||
If you want to set the `kid` parameter or change the algorithm for example:
|
If you want to set the `kid` parameter or change the algorithm for example:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let mut header = Header::default();
|
let mut header = Header::new(Algorithm::HS512);
|
||||||
header.kid = Some("blabla".to_owned());
|
header.kid = Some("blabla".to_owned());
|
||||||
header.alg = Algorithm::HS512;
|
|
||||||
let token = encode(&header, &my_claims, "secret".as_ref())?;
|
let token = encode(&header, &my_claims, "secret".as_ref())?;
|
||||||
```
|
```
|
||||||
Look at `examples/custom_header.rs` for a full working example.
|
Look at `examples/custom_header.rs` for a full working example.
|
||||||
|
|
||||||
|
### Encoding
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// HS256
|
||||||
|
let token = encode(&Header::default(), &my_claims, "secret".as_ref())?;
|
||||||
|
// RSA
|
||||||
|
let token = encode(&Header::new(Algorithm::RS256), &my_claims, include_str!("privkey.pem"))?;
|
||||||
|
```
|
||||||
|
Encoding a JWT takes 3 parameters:
|
||||||
|
|
||||||
|
- a header: the `Header` struct
|
||||||
|
- some claims: your own struct
|
||||||
|
- a key
|
||||||
|
|
||||||
|
When using HS256, HS2384 or HS512, the key is always a shared secret like in the example above. When using
|
||||||
|
RSA/EC, the key should always be the content of the private key in the PEM format.
|
||||||
|
|
||||||
### Decoding
|
### Decoding
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let token = decode::<Claims>(&token, "secret".as_ref(), &Validation::default())?;
|
let token = decode::<Claims>(&token, "secret".as_ref(), &Validation::default())?;
|
||||||
// token is a struct with 2 params: header and claims
|
// token is a struct with 2 fields: `header` and `claims` and `claims` is your own struct.
|
||||||
```
|
```
|
||||||
`decode` can error for a variety of reasons:
|
`decode` can error for a variety of reasons:
|
||||||
|
|
||||||
- the token or its signature is invalid
|
- the token or its signature is invalid
|
||||||
- error while decoding base64 or the result of decoding base64 is not valid UTF-8
|
- the token had invalid base64
|
||||||
- validation of at least one reserved claim failed
|
- validation of at least one reserved claim failed
|
||||||
|
|
||||||
In some cases, for example if you don't know the algorithm used, you will want to only decode the header:
|
As with encoding, when using HS256, HS2384 or HS512, the key is always a shared secret like in the example above. When using
|
||||||
|
RSA/EC, the key should always be the content of the public key in the PEM format.
|
||||||
|
|
||||||
|
In some cases, for example if you don't know the algorithm used or need to grab the `kid`, you can decode only the header:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let header = decode_header(&token)?;
|
let header = decode_header(&token)?;
|
||||||
```
|
```
|
||||||
|
|
||||||
This does not perform any validation on the token.
|
This does not perform any signature verification/validations on the token so it could have been tampered with.
|
||||||
|
|
||||||
|
You can also decode a token using the public key components of a RSA key in base64 format.
|
||||||
|
The main use-case is for JWK where your public key is a JSON format like so:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kty":"RSA",
|
||||||
|
"e":"AQAB",
|
||||||
|
"kid":"6a7a119f-0876-4f7e-8d0f-bf3ea1391dd8",
|
||||||
|
"n":"yRE6rHuNR0QbHO3H3Kt2pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4l4sggh5_CYYi_cvI-SXVT9kPWSKXxJXBXd_4LkvcPuUakBoAkfh-eiFVMh2VrUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHRyIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG_AtH89BIE9jDBHZ9dLelK9a184zAf8LwoPLxvJb3Il5nncqPcSfKDDodMFBIMc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xqi-yUod-j8MtvIj812dkS4QMiRVN_by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5TdQ"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let token = decode_rsa_components::<Claims>(&token, jwk["n"], jwk["e"], &Validation::new(Algorithm::RS256))?;
|
||||||
|
// token is a struct with 2 fields: `header` and `claims` and `claims` is your own struct.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Convertion .der to .pem
|
||||||
|
|
||||||
|
You can use openssl for that:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl rsa -inform DER -outform PEM -in mykey.der -out mykey.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
#### Validation
|
#### Validation
|
||||||
This library validates automatically the `exp` claim. `nbf` is also validated if present. You can also validate the `sub`, `iss` and `aud` but
|
This library validates automatically the `exp` claim. `nbf` is also validated if present. You can also validate the `sub`, `iss` and `aud` but
|
||||||
those require setting the expected value in the `Validation` struct.
|
those require setting the expected value in the `Validation` struct.
|
||||||
|
|
||||||
Since validating time fields is always a bit tricky due to clock skew,
|
Since validating time fields is always a bit tricky due to clock skew,
|
||||||
you can add some leeway to the `iat`, `exp` and `nbf` validation by setting a `leeway` parameter.
|
you can add some leeway to the `iat`, `exp` and `nbf` validation by setting the `leeway` field.
|
||||||
|
|
||||||
Last but not least, you will need to set the algorithm(s) allowed for this token if you are not using `HS256`.
|
Last but not least, you will need to set the algorithm(s) allowed for this token if you are not using `HS256`.
|
||||||
|
|
||||||
|
@ -112,44 +154,6 @@ This library currently supports the following:
|
||||||
- ES256
|
- ES256
|
||||||
- ES384
|
- ES384
|
||||||
|
|
||||||
### RSA
|
### RSA & ECDSA
|
||||||
`jsonwebtoken` can read DER and PEM encoded keys.
|
By default, the `encode`/`decode` functions takes the PEM format since it is the most common.
|
||||||
|
RSA can also use the public key components modulus/exponent in base64 format for decoding.
|
||||||
#### DER Encoded
|
|
||||||
If you have openssl installed, you can run the following commands to obtain the DER keys from PKCS#1 (ie with `BEGIN RSA PUBLIC KEY`) .pem.
|
|
||||||
If you have a PKCS#8 pem file (ie starting with `BEGIN PUBLIC KEY`), you will need to first convert it to PKCS#1:
|
|
||||||
`openssl rsa -pubin -in <filename> -RSAPublicKey_out -out <filename>`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
// private key
|
|
||||||
$ openssl rsa -in private_rsa_key.pem -outform DER -out private_rsa_key.der
|
|
||||||
// public key
|
|
||||||
$ openssl rsa -in private_rsa_key.der -inform DER -RSAPublicKey_out -outform DER -out public_key.der
|
|
||||||
```
|
|
||||||
|
|
||||||
If you are getting an error with your public key, make sure you get it by using the command above to ensure
|
|
||||||
it is in the right format.
|
|
||||||
|
|
||||||
#### PEM Encoded
|
|
||||||
To generate a PKCS#1 RSA key, run `openssl genrsa -out private_rsa_key_pkcs1.pem 2048`
|
|
||||||
To convert a PKCS#1 RSA key to a PKCS#8 RSA key, run `openssl pkcs8 -topk8 -inform pem -in private_rsa_key_pkcs1.pem -outform pem -nocrypt -out private_rsa_key_pkcs8.pem`
|
|
||||||
|
|
||||||
To use a PEM encoded private / public keys, a pem struct is returned by `decode_pem`.
|
|
||||||
This carries the lifetime of the data inside. Finally to use the key like any other
|
|
||||||
use the `.as_key(alg)` function on the pem struct.
|
|
||||||
```
|
|
||||||
let privkey_pem = decode_pem(pem_string_here).unwrap();
|
|
||||||
let privkey = privkey_pem.as_key(Algorithm::RS256).unwrap();
|
|
||||||
```
|
|
||||||
|
|
||||||
### ECDSA
|
|
||||||
`jsonwebtoken` can read PKCS#8 DER encoded private keys and public keys, as well as PEM encoded keys. Like RSA, to read a PEM key, you must use the pem decoder.
|
|
||||||
|
|
||||||
To generate an EC key, you can do the following.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
// private key
|
|
||||||
openssl ecparam -genkey -name prime256v1 | openssl ec -out private_key.pem
|
|
||||||
// public key
|
|
||||||
openssl ec -in private_key.pem -pubout -out public_key.pem
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
extern crate jsonwebtoken as jwt;
|
|
||||||
extern crate test;
|
extern crate test;
|
||||||
#[macro_use]
|
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
use jwt::{decode, encode, Header, Hmac, Validation};
|
use jsonwebtoken::{decode, encode, Header, Validation};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
struct Claims {
|
struct Claims {
|
||||||
|
@ -16,7 +14,7 @@ struct Claims {
|
||||||
fn bench_encode(b: &mut test::Bencher) {
|
fn bench_encode(b: &mut test::Bencher) {
|
||||||
let claim = Claims { sub: "b@b.com".to_owned(), company: "ACME".to_owned() };
|
let claim = Claims { sub: "b@b.com".to_owned(), company: "ACME".to_owned() };
|
||||||
|
|
||||||
b.iter(|| encode(&Header::default(), &claim, Hmac::from(b"secret")));
|
b.iter(|| encode(&Header::default(), &claim, "secret".as_ref()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::errors::{Error, ErrorKind, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// The algorithms supported for signing/verifying
|
/// The algorithms supported for signing/verifying JWTs
|
||||||
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||||
pub enum Algorithm {
|
pub enum Algorithm {
|
||||||
/// HMAC using SHA-256
|
/// HMAC using SHA-256
|
||||||
|
|
|
@ -2,7 +2,7 @@ use ring::{rand, signature};
|
||||||
|
|
||||||
use crate::algorithms::Algorithm;
|
use crate::algorithms::Algorithm;
|
||||||
use crate::errors::Result;
|
use crate::errors::Result;
|
||||||
use crate::pem_decoder::PemEncodedKey;
|
use crate::pem::decoder::PemEncodedKey;
|
||||||
use crate::serialization::b64_encode;
|
use crate::serialization::b64_encode;
|
||||||
|
|
||||||
/// Only used internally when validating EC, to map from our enum to the Ring EcdsaVerificationAlgorithm structs.
|
/// Only used internally when validating EC, to map from our enum to the Ring EcdsaVerificationAlgorithm structs.
|
||||||
|
|
|
@ -3,7 +3,7 @@ use ring::{hmac, signature};
|
||||||
|
|
||||||
use crate::algorithms::Algorithm;
|
use crate::algorithms::Algorithm;
|
||||||
use crate::errors::Result;
|
use crate::errors::Result;
|
||||||
use crate::pem_decoder::PemEncodedKey;
|
use crate::pem::decoder::PemEncodedKey;
|
||||||
use crate::serialization::{b64_decode, b64_encode};
|
use crate::serialization::{b64_decode, b64_encode};
|
||||||
|
|
||||||
pub(crate) mod ecdsa;
|
pub(crate) mod ecdsa;
|
||||||
|
@ -19,7 +19,8 @@ pub(crate) fn sign_hmac(alg: hmac::Algorithm, key: &[u8], message: &str) -> Resu
|
||||||
/// Take the payload of a JWT, sign it using the algorithm given and return
|
/// Take the payload of a JWT, sign it using the algorithm given and return
|
||||||
/// the base64 url safe encoded of the result.
|
/// the base64 url safe encoded of the result.
|
||||||
///
|
///
|
||||||
/// Only use this function if you want to do something other than JWT.
|
/// If you just want to encode a JWT, use `encode` instead.
|
||||||
|
///
|
||||||
/// `key` is the secret for HMAC and a pem encoded string otherwise
|
/// `key` is the secret for HMAC and a pem encoded string otherwise
|
||||||
pub fn sign(message: &str, key: &[u8], algorithm: Algorithm) -> Result<String> {
|
pub fn sign(message: &str, key: &[u8], algorithm: Algorithm) -> Result<String> {
|
||||||
match algorithm {
|
match algorithm {
|
||||||
|
@ -57,7 +58,7 @@ fn verify_ring(
|
||||||
/// Compares the signature given with a re-computed signature for HMAC or using the public key
|
/// Compares the signature given with a re-computed signature for HMAC or using the public key
|
||||||
/// for RSA/EC.
|
/// for RSA/EC.
|
||||||
///
|
///
|
||||||
/// Only use this function if you want to do something other than JWT.
|
/// If you just want to decode a JWT, use `decode` instead.
|
||||||
///
|
///
|
||||||
/// `signature` is the signature part of a jwt (text after the second '.')
|
/// `signature` is the signature part of a jwt (text after the second '.')
|
||||||
///
|
///
|
||||||
|
@ -109,5 +110,10 @@ pub fn verify_rsa_components(
|
||||||
alg: Algorithm,
|
alg: Algorithm,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
let signature_bytes = b64_decode(signature)?;
|
let signature_bytes = b64_decode(signature)?;
|
||||||
rsa::verify_from_components(rsa::alg_to_rsa_parameters(alg), &signature_bytes, message, components)
|
rsa::verify_from_components(
|
||||||
|
rsa::alg_to_rsa_parameters(alg),
|
||||||
|
&signature_bytes,
|
||||||
|
message,
|
||||||
|
components,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use simple_asn1::BigUint;
|
||||||
|
|
||||||
use crate::algorithms::Algorithm;
|
use crate::algorithms::Algorithm;
|
||||||
use crate::errors::{ErrorKind, Result};
|
use crate::errors::{ErrorKind, Result};
|
||||||
use crate::pem_decoder::PemEncodedKey;
|
use crate::pem::decoder::PemEncodedKey;
|
||||||
use crate::serialization::{b64_decode, b64_encode};
|
use crate::serialization::{b64_decode, b64_encode};
|
||||||
|
|
||||||
/// Only used internally when validating RSA, to map from our enum to the Ring param structs.
|
/// Only used internally when validating RSA, to map from our enum to the Ring param structs.
|
||||||
|
|
|
@ -3,9 +3,18 @@ use serde::de::DeserializeOwned;
|
||||||
use crate::crypto::{verify, verify_rsa_components};
|
use crate::crypto::{verify, verify_rsa_components};
|
||||||
use crate::errors::{new_error, ErrorKind, Result};
|
use crate::errors::{new_error, ErrorKind, Result};
|
||||||
use crate::header::Header;
|
use crate::header::Header;
|
||||||
use crate::serialization::{from_jwt_part_claims, TokenData};
|
use crate::serialization::from_jwt_part_claims;
|
||||||
use crate::validation::{validate, Validation};
|
use crate::validation::{validate, Validation};
|
||||||
|
|
||||||
|
/// The return type of a successful call to [decode](fn.decode.html).
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TokenData<T> {
|
||||||
|
/// The decoded JWT header
|
||||||
|
pub header: Header,
|
||||||
|
/// The decoded JWT claims
|
||||||
|
pub claims: T,
|
||||||
|
}
|
||||||
|
|
||||||
/// Takes the result of a rsplit and ensure we only get 2 parts
|
/// Takes the result of a rsplit and ensure we only get 2 parts
|
||||||
/// Errors if we don't
|
/// Errors if we don't
|
||||||
macro_rules! expect_two {
|
macro_rules! expect_two {
|
||||||
|
@ -54,7 +63,7 @@ fn _decode<T: DeserializeOwned>(
|
||||||
Ok(TokenData { header, claims: decoded_claims })
|
Ok(TokenData { header, claims: decoded_claims })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode a token into a struct containing 2 fields: `claims` and `header`.
|
/// Decode and validate a JWT using a secret for HS and a public PEM format for RSA/EC
|
||||||
///
|
///
|
||||||
/// If the token or its signature is invalid or the claims fail validation, it will return an error.
|
/// If the token or its signature is invalid or the claims fail validation, it will return an error.
|
||||||
///
|
///
|
||||||
|
@ -80,8 +89,27 @@ pub fn decode<T: DeserializeOwned>(
|
||||||
_decode(token, DecodingKey::SecretOrPem(key), validation)
|
_decode(token, DecodingKey::SecretOrPem(key), validation)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TO TEST
|
/// Decode and validate a JWT using (n, e) base64 encoded public key components for RSA
|
||||||
pub fn decode_rsa_jwk<T: DeserializeOwned>(
|
///
|
||||||
|
/// If the token or its signature is invalid or the claims fail validation, it will return an error.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use serde::{Deserialize, Serialize};
|
||||||
|
/// use jsonwebtoken::{decode_rsa_components, Validation, Algorithm};
|
||||||
|
///
|
||||||
|
/// #[derive(Debug, Serialize, Deserialize)]
|
||||||
|
/// struct Claims {
|
||||||
|
/// sub: String,
|
||||||
|
/// company: String
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let modulus = "some-base64-data";
|
||||||
|
/// let exponent = "some-base64-data";
|
||||||
|
/// let token = "a.jwt.token".to_string();
|
||||||
|
/// // Claims is a struct that implements Deserialize
|
||||||
|
/// let token_message = decode_rsa_components::<Claims>(&token, &modulus, &exponent, &Validation::new(Algorithm::HS256));
|
||||||
|
/// ```
|
||||||
|
pub fn decode_rsa_components<T: DeserializeOwned>(
|
||||||
token: &str,
|
token: &str,
|
||||||
modulus: &str,
|
modulus: &str,
|
||||||
exponent: &str,
|
exponent: &str,
|
||||||
|
@ -90,7 +118,7 @@ pub fn decode_rsa_jwk<T: DeserializeOwned>(
|
||||||
_decode(token, DecodingKey::RsaModulusExponent { n: modulus, e: exponent }, validation)
|
_decode(token, DecodingKey::RsaModulusExponent { n: modulus, e: exponent }, validation)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode a token without any signature validation into a struct containing 2 fields: `claims` and `header`.
|
/// Decode a JWT without any signature verification/validations.
|
||||||
///
|
///
|
||||||
/// NOTE: Do not use this unless you know what you are doing! If the token's signature is invalid, it will *not* return an error.
|
/// NOTE: Do not use this unless you know what you are doing! If the token's signature is invalid, it will *not* return an error.
|
||||||
///
|
///
|
||||||
|
@ -118,10 +146,9 @@ pub fn dangerous_unsafe_decode<T: DeserializeOwned>(token: &str) -> Result<Token
|
||||||
Ok(TokenData { header, claims: decoded_claims })
|
Ok(TokenData { header, claims: decoded_claims })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode a token and return the Header. This is not doing any kind of validation: it only splits
|
/// Decode a JWT without any signature verification/validations and return its [Header](struct.Header.html).
|
||||||
/// on the `.` and return the base64 decoded header.
|
|
||||||
///
|
///
|
||||||
/// If the token has an invalid format, it will return an error.
|
/// If the token has an invalid format (ie 3 parts separated by a `.`), it will return an error.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use jsonwebtoken::decode_header;
|
/// use jsonwebtoken::decode_header;
|
||||||
|
|
|
@ -93,11 +93,11 @@ impl StdError for Error {
|
||||||
ErrorKind::InvalidAlgorithm => "algorithms don't match",
|
ErrorKind::InvalidAlgorithm => "algorithms don't match",
|
||||||
ErrorKind::InvalidAlgorithmName => "not a known algorithm",
|
ErrorKind::InvalidAlgorithmName => "not a known algorithm",
|
||||||
ErrorKind::InvalidKeyFormat => "invalid key format",
|
ErrorKind::InvalidKeyFormat => "invalid key format",
|
||||||
ErrorKind::__Nonexhaustive => "unknown error",
|
|
||||||
ErrorKind::Base64(ref err) => err.description(),
|
ErrorKind::Base64(ref err) => err.description(),
|
||||||
ErrorKind::Json(ref err) => err.description(),
|
ErrorKind::Json(ref err) => err.description(),
|
||||||
ErrorKind::Utf8(ref err) => err.description(),
|
ErrorKind::Utf8(ref err) => err.description(),
|
||||||
ErrorKind::Crypto(ref err) => err.description(),
|
ErrorKind::Crypto(ref err) => err.description(),
|
||||||
|
ErrorKind::__Nonexhaustive => "unknown error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,11 +115,11 @@ impl StdError for Error {
|
||||||
ErrorKind::InvalidAlgorithm => None,
|
ErrorKind::InvalidAlgorithm => None,
|
||||||
ErrorKind::InvalidAlgorithmName => None,
|
ErrorKind::InvalidAlgorithmName => None,
|
||||||
ErrorKind::InvalidKeyFormat => None,
|
ErrorKind::InvalidKeyFormat => None,
|
||||||
ErrorKind::__Nonexhaustive => None,
|
|
||||||
ErrorKind::Base64(ref err) => Some(err),
|
ErrorKind::Base64(ref err) => Some(err),
|
||||||
ErrorKind::Json(ref err) => Some(err),
|
ErrorKind::Json(ref err) => Some(err),
|
||||||
ErrorKind::Utf8(ref err) => Some(err),
|
ErrorKind::Utf8(ref err) => Some(err),
|
||||||
ErrorKind::Crypto(ref err) => Some(err),
|
ErrorKind::Crypto(ref err) => Some(err),
|
||||||
|
ErrorKind::__Nonexhaustive => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
49
src/lib.rs
49
src/lib.rs
|
@ -4,21 +4,21 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
mod algorithms;
|
mod algorithms;
|
||||||
mod crypto;
|
/// Lower level functions, if you want to do something other than JWTs
|
||||||
|
pub mod crypto;
|
||||||
mod decoding;
|
mod decoding;
|
||||||
/// All the errors
|
/// All the errors that can be encountered while encoding/decoding JWTs
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
mod header;
|
mod header;
|
||||||
mod pem_decoder;
|
mod pem;
|
||||||
mod pem_encoder;
|
|
||||||
mod serialization;
|
mod serialization;
|
||||||
mod validation;
|
mod validation;
|
||||||
|
|
||||||
pub use algorithms::Algorithm;
|
pub use algorithms::Algorithm;
|
||||||
pub use crypto::{sign, verify};
|
pub use decoding::{
|
||||||
pub use decoding::{dangerous_unsafe_decode, decode, decode_header, decode_rsa_jwk};
|
dangerous_unsafe_decode, decode, decode_header, decode_rsa_components, TokenData,
|
||||||
|
};
|
||||||
pub use header::Header;
|
pub use header::Header;
|
||||||
pub use serialization::TokenData;
|
|
||||||
pub use validation::Validation;
|
pub use validation::Validation;
|
||||||
|
|
||||||
use serde::ser::Serialize;
|
use serde::ser::Serialize;
|
||||||
|
@ -26,7 +26,8 @@ use serde::ser::Serialize;
|
||||||
use crate::errors::Result;
|
use crate::errors::Result;
|
||||||
use crate::serialization::b64_encode_part;
|
use crate::serialization::b64_encode_part;
|
||||||
|
|
||||||
/// Encode the header and claims given 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.
|
||||||
|
/// If the algorithm given is RSA or EC, the key needs to be in the PEM format.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use serde::{Deserialize, Serialize};
|
/// use serde::{Deserialize, Serialize};
|
||||||
|
@ -51,37 +52,7 @@ pub fn encode<T: Serialize>(header: &Header, claims: &T, key: &[u8]) -> Result<S
|
||||||
let encoded_header = b64_encode_part(&header)?;
|
let encoded_header = b64_encode_part(&header)?;
|
||||||
let encoded_claims = b64_encode_part(&claims)?;
|
let encoded_claims = b64_encode_part(&claims)?;
|
||||||
let message = [encoded_header.as_ref(), encoded_claims.as_ref()].join(".");
|
let message = [encoded_header.as_ref(), encoded_claims.as_ref()].join(".");
|
||||||
let signature = sign(&*message, key, header.alg)?;
|
let signature = crypto::sign(&*message, key, header.alg)?;
|
||||||
|
|
||||||
Ok([message, signature].join("."))
|
Ok([message, signature].join("."))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO
|
|
||||||
pub fn encode_rsa_public_pkcs1_pem(modulus: &[u8], exponent: &[u8]) -> Result<String> {
|
|
||||||
pem_encoder::encode_rsa_public_pkcs1_pem(modulus, exponent)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TODO
|
|
||||||
pub fn encode_rsa_public_pkcs1_der(modulus: &[u8], exponent: &[u8]) -> Result<Vec<u8>> {
|
|
||||||
pem_encoder::encode_rsa_public_pkcs1_der(modulus, exponent)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TODO
|
|
||||||
pub fn encode_rsa_public_pkcs8_pem(modulus: &[u8], exponent: &[u8]) -> Result<String> {
|
|
||||||
pem_encoder::encode_rsa_public_pkcs8_pem(modulus, exponent)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TODO
|
|
||||||
pub fn encode_rsa_public_pkcs8_der(modulus: &[u8], exponent: &[u8]) -> Result<Vec<u8>> {
|
|
||||||
pem_encoder::encode_rsa_public_pkcs8_der(modulus, exponent)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TODO
|
|
||||||
pub fn encode_ec_public_pem(x_coordinate: &[u8]) -> Result<String> {
|
|
||||||
pem_encoder::encode_ec_public_pem(x_coordinate)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TODO
|
|
||||||
pub fn encode_ec_public_der(x_coordinate: &[u8]) -> Result<Vec<u8>> {
|
|
||||||
pem_encoder::encode_ec_public_der(x_coordinate)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
use crate::errors::{ErrorKind, Result};
|
use crate::errors::{ErrorKind, Result};
|
||||||
|
|
||||||
extern crate pem;
|
|
||||||
extern crate simple_asn1;
|
|
||||||
|
|
||||||
use simple_asn1::{BigUint, OID};
|
use simple_asn1::{BigUint, OID};
|
||||||
|
|
||||||
/// Supported PEM files for EC and RSA Public and Private Keys
|
/// Supported PEM files for EC and RSA Public and Private Keys
|
|
@ -2,43 +2,39 @@ use crate::errors::{ErrorKind, Result};
|
||||||
use pem::Pem;
|
use pem::Pem;
|
||||||
use simple_asn1::{ASN1Block, BigInt, BigUint, OID};
|
use simple_asn1::{ASN1Block, BigInt, BigUint, OID};
|
||||||
|
|
||||||
extern crate base64;
|
pub(crate) fn encode_rsa_public_pkcs1_pem(modulus: &[u8], exponent: &[u8]) -> Result<String> {
|
||||||
extern crate pem;
|
|
||||||
extern crate simple_asn1;
|
|
||||||
|
|
||||||
pub fn encode_rsa_public_pkcs1_pem(modulus: &[u8], exponent: &[u8]) -> Result<String> {
|
|
||||||
Ok(pem::encode(&Pem {
|
Ok(pem::encode(&Pem {
|
||||||
contents: encode_rsa_public_pkcs1_der(modulus, exponent)?,
|
contents: encode_rsa_public_pkcs1_der(modulus, exponent)?,
|
||||||
tag: "RSA PUBLIC KEY".to_string(),
|
tag: "RSA PUBLIC KEY".to_string(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode_rsa_public_pkcs1_der(modulus: &[u8], exponent: &[u8]) -> Result<Vec<u8>> {
|
pub(crate) fn encode_rsa_public_pkcs1_der(modulus: &[u8], exponent: &[u8]) -> Result<Vec<u8>> {
|
||||||
match simple_asn1::to_der(&encode_rsa_public_pksc1_asn1(modulus, exponent)) {
|
match simple_asn1::to_der(&encode_rsa_public_pksc1_asn1(modulus, exponent)) {
|
||||||
Ok(bytes) => Ok(bytes),
|
Ok(bytes) => Ok(bytes),
|
||||||
Err(_) => Err(ErrorKind::InvalidRsaKey.into()),
|
Err(_) => Err(ErrorKind::InvalidRsaKey.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode_rsa_public_pkcs8_pem(modulus: &[u8], exponent: &[u8]) -> Result<String> {
|
pub(crate) fn encode_rsa_public_pkcs8_pem(modulus: &[u8], exponent: &[u8]) -> Result<String> {
|
||||||
Ok(pem::encode(&Pem {
|
Ok(pem::encode(&Pem {
|
||||||
contents: encode_rsa_public_pkcs8_der(modulus, exponent)?,
|
contents: encode_rsa_public_pkcs8_der(modulus, exponent)?,
|
||||||
tag: "PUBLIC KEY".to_string(),
|
tag: "PUBLIC KEY".to_string(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode_rsa_public_pkcs8_der(modulus: &[u8], exponent: &[u8]) -> Result<Vec<u8>> {
|
pub(crate) fn encode_rsa_public_pkcs8_der(modulus: &[u8], exponent: &[u8]) -> Result<Vec<u8>> {
|
||||||
match simple_asn1::to_der(&encode_rsa_public_pksc8_asn1(modulus, exponent)?) {
|
match simple_asn1::to_der(&encode_rsa_public_pksc8_asn1(modulus, exponent)?) {
|
||||||
Ok(bytes) => Ok(bytes),
|
Ok(bytes) => Ok(bytes),
|
||||||
Err(_) => Err(ErrorKind::InvalidRsaKey.into()),
|
Err(_) => Err(ErrorKind::InvalidRsaKey.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode_ec_public_pem(x: &[u8]) -> Result<String> {
|
pub(crate) fn encode_ec_public_pem(x: &[u8]) -> Result<String> {
|
||||||
Ok(pem::encode(&Pem { contents: encode_ec_public_der(x)?, tag: "PUBLIC KEY".to_string() }))
|
Ok(pem::encode(&Pem { contents: encode_ec_public_der(x)?, tag: "PUBLIC KEY".to_string() }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode_ec_public_der(x: &[u8]) -> Result<Vec<u8>> {
|
pub(crate) fn encode_ec_public_der(x: &[u8]) -> Result<Vec<u8>> {
|
||||||
match simple_asn1::to_der(&encode_ec_public_asn1(x)) {
|
match simple_asn1::to_der(&encode_ec_public_asn1(x)) {
|
||||||
Ok(bytes) => Ok(bytes),
|
Ok(bytes) => Ok(bytes),
|
||||||
Err(_) => Err(ErrorKind::InvalidEcdsaKey.into()),
|
Err(_) => Err(ErrorKind::InvalidEcdsaKey.into()),
|
|
@ -0,0 +1,34 @@
|
||||||
|
pub(crate) mod decoder;
|
||||||
|
mod encoder;
|
||||||
|
|
||||||
|
use crate::errors::Result;
|
||||||
|
|
||||||
|
/// Encode (n, e) components into the public PKCS1 PEM format
|
||||||
|
pub fn encode_rsa_public_pkcs1_pem(modulus: &[u8], exponent: &[u8]) -> Result<String> {
|
||||||
|
encoder::encode_rsa_public_pkcs1_pem(modulus, exponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode (n, e) components into the public PKCS1 PEM format
|
||||||
|
pub fn encode_rsa_public_pkcs1_der(modulus: &[u8], exponent: &[u8]) -> Result<Vec<u8>> {
|
||||||
|
encoder::encode_rsa_public_pkcs1_der(modulus, exponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn encode_rsa_public_pkcs8_pem(modulus: &[u8], exponent: &[u8]) -> Result<String> {
|
||||||
|
encoder::encode_rsa_public_pkcs8_pem(modulus, exponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn encode_rsa_public_pkcs8_der(modulus: &[u8], exponent: &[u8]) -> Result<Vec<u8>> {
|
||||||
|
encoder::encode_rsa_public_pkcs8_der(modulus, exponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn encode_ec_public_pem(x_coordinate: &[u8]) -> Result<String> {
|
||||||
|
encoder::encode_ec_public_pem(x_coordinate)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn encode_ec_public_der(x_coordinate: &[u8]) -> Result<Vec<u8>> {
|
||||||
|
encoder::encode_ec_public_der(x_coordinate)
|
||||||
|
}
|
|
@ -4,16 +4,6 @@ use serde_json::map::Map;
|
||||||
use serde_json::{from_str, to_string, Value};
|
use serde_json::{from_str, to_string, Value};
|
||||||
|
|
||||||
use crate::errors::Result;
|
use crate::errors::Result;
|
||||||
use crate::header::Header;
|
|
||||||
|
|
||||||
/// The return type of a successful call to decode
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TokenData<T> {
|
|
||||||
/// The decoded JWT header
|
|
||||||
pub header: Header,
|
|
||||||
/// The decoded JWT claims
|
|
||||||
pub claims: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn b64_encode(input: &[u8]) -> String {
|
pub(crate) fn b64_encode(input: &[u8]) -> String {
|
||||||
base64::encode_config(input, base64::URL_SAFE_NO_PAD)
|
base64::encode_config(input, base64::URL_SAFE_NO_PAD)
|
||||||
|
|
|
@ -7,7 +7,7 @@ use serde_json::{from_value, Value};
|
||||||
use crate::algorithms::Algorithm;
|
use crate::algorithms::Algorithm;
|
||||||
use crate::errors::{new_error, ErrorKind, Result};
|
use crate::errors::{new_error, ErrorKind, Result};
|
||||||
|
|
||||||
/// Contains the various validations that are applied after decoding a token.
|
/// Contains the various validations that are applied after decoding a JWT.
|
||||||
///
|
///
|
||||||
/// All time validation happen on UTC timestamps as seconds.
|
/// All time validation happen on UTC timestamps as seconds.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use jsonwebtoken::{decode, encode, sign, verify, Algorithm, Header, Validation};
|
use jsonwebtoken::{
|
||||||
|
crypto::{sign, verify},
|
||||||
|
decode, encode, Algorithm, Header, Validation,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use jsonwebtoken::{
|
use jsonwebtoken::{
|
||||||
dangerous_unsafe_decode, decode, decode_header, encode, sign, verify, Algorithm, Header,
|
crypto::{sign, verify},
|
||||||
Validation,
|
dangerous_unsafe_decode, decode, decode_header, encode, Algorithm, Header, Validation,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use jsonwebtoken::{decode, decode_rsa_jwk, encode, sign, verify, Algorithm, Header, Validation};
|
use jsonwebtoken::{
|
||||||
|
crypto::{sign, verify},
|
||||||
|
decode, decode_rsa_components, encode, Algorithm, Header, Validation,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
const RSA_ALGORITHMS: &[Algorithm] = &[
|
const RSA_ALGORITHMS: &[Algorithm] = &[
|
||||||
|
@ -76,7 +79,7 @@ fn rsa_modulus_exponent() {
|
||||||
let e = "AQAB";
|
let e = "AQAB";
|
||||||
|
|
||||||
let encrypted = encode(&Header::new(Algorithm::RS256), &my_claims, privkey.as_ref()).unwrap();
|
let encrypted = encode(&Header::new(Algorithm::RS256), &my_claims, privkey.as_ref()).unwrap();
|
||||||
let res = decode_rsa_jwk::<Claims>(&encrypted, n, e, &Validation::new(Algorithm::RS256));
|
let res = decode_rsa_components::<Claims>(&encrypted, n, e, &Validation::new(Algorithm::RS256));
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue