A type-driven, ergonomic implementation of the PASETO protocol for secure stateless tokens.
Paseto is everything you love about JOSE (JWT, JWE, JWS) without any of the many design deficits that plague the JOSE standards.
| APIs, Tests & Documentation | v1.
local| v1.
public | v2.
local | v2.
public |v3.
local | v3.
public | v4.
local | v4.
public |
| ------------: | :-----------: | :----------: |:-----------: |:-----------: |:-----------: |:-----------: |:-----------: |:-----------: |
| PASETO Token Builder | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :blackcircle: | :greencircle: | :greencircle: |
| PASETO Token Parser | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :blackcircle: | :greencircle: | :greencircle: |
| Flexible Claim Validation | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :blackcircle: | :greencircle: | :greencircle: |
| Generic Token Builder | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :blackcircle: | :greencircle: | :greencircle: |
| Generic Token Parser | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :blackcircle: | :greencircle: | :greencircle: |
| Encryption/Signing | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :blackcircle: | :greencircle: | :greencircle: |
| Decryption/Verification | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :blackcircle: | :greencircle: | :greencircle: |
| PASETO Test vectors | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :greencircle: | :blackcircle: | :greencircle: | :greencircle: |
| Documentation | :blackcircle: | :blackcircle: | :orangecircle: | :blackcircle: | :blackcircle: | :blackcircle: | :blackcircle: | :blackcircle: |
| Feature | Status |
| ------------: | :-----------: |
| Feature gates | :blackcircle: |
| PASERK support | :blackcircle: |
# Usage
rust
// at the top of your source file
use rusty_paseto::prelude::*;
# Examples: Building and parsing tokens
Here's a basic, default token: ```rust use rusty_paseto::prelude::*;
// create a key specifying the PASETO version and purpose
let key = PasetoSymmetricKey::
```
## A default token
Contains a NotBeforeClaim defaulting to the current utc time the token was created
You can parse and validate an existing token with the following:
```rust
let key = PasetoSymmetricKey::
//the ExpirationClaim assert!(jsonvalue["exp"].isstring()); //the IssuedAtClaim assert!(jsonvalue["iat"].isstring());
```
Validates the token structure and decryptes the payload or verifies the signature of the content
Validates the implicit assertion if one was provided (for V3 or V4 versioned tokens only)
PASETO tokens can have an optional footer. In rustypaseto we have strict types for most things.
So we can extend the previous example to add a footer to the token by using code like the
following:
```rust
use rustypaseto::prelude::*;
let key = PasetoSymmetricKey::
// token is now a String in the form: "v4.local.encoded-payload.footer"
And parse it by passing in the same expected footer
rust
// now we can parse and validate the token with a parser that returns a serdejson::Value
let jsonvalue = PasetoParser::
//the ExpirationClaim assert!(jsonvalue["exp"].isstring()); //the IssuedAtClaim assert!(jsonvalue["iat"].isstring());
```
Version 3 (V3) and Version 4 (V4) PASETO tokens can have an optional implicit assertion.
So we can extend the previous example to add an implicit assertion to the token by using code like the
following:
```rust
use rustypaseto::prelude::*;
let key = PasetoSymmetricKey::
// token is now a String in the form: "v4.local.encoded-payload.footer"
And parse it by passing in the same expected implicit assertion
rust
// now we can parse and validate the token with a parser that returns a serdejson::Value
let jsonvalue = PasetoParser::
```
As mentioned, default tokens expire 1 hour from creation time. You can set your own expiration time by adding an ExpirationClaim which takes an ISO 8601 (Rfc3339) compliant datetime string.
```rust
// must include
use std::convert::TryFrom;
let key = PasetoSymmetricKey::
let token = PasetoBuilder::
// token is a String in the form: "v4.local.encoded-payload.footer"
```
A 1 hour ExpirationClaim is set by default because the use case for non-expiring tokens in the world of security tokens is fairly limited. Omitting an expiration claim or forgetting to require one when processing them is almost certainly an oversight rather than a deliberate choice.
When it is a deliberate choice, you have the opportunity to deliberately remove this claim from the Builder.
The method call required to do so ensures readers of the code understand the implicit risk.
```rust
let token = PasetoBuilder::
```
The PASETO specification includes seven reserved claims which you can set with their explicit types: ```rust // real-world example using the time crate to prevent the token from being used before 2 // minutes from now let in2minutes = (time::OffsetDateTime::now_utc() + time::Duration::minutes(2)).format(&Rfc3339)?;
let token = PasetoBuilder::
```
The CustomClaim struct takes a tuple in the form of (key: String, value: T)
where T is any
serializable type
```rust
let token = PasetoBuilder::
This throws an error:
rust
// "exp" is a reserved PASETO claim key, you should use the ExpirationClaim type
let token = PasetoBuilder::
```
rusty_paseto allows for flexible claim validation at parse time
Let's see how we can check particular claims exist with expected values.
```rust
// use a default token builder with the same PASETO version and purpose
let token = PasetoBuilder::
PasetoParser::
// no need for the assertions below since the check_claim methods // above accomplish the same but at parse time!
//asserteq!(jsonvalue["sub"], "Get schwifty"); //asserteq!(jsonvalue["Contestant"], "Earth"); //asserteq!(jsonvalue["Universe"], 137); ```
What if we have more complex validation requirements? You can pass in a reference to a closure which receives the key and value of the claim you want to validate so you can implement any validation logic you choose.
Let's see how we can validate our tokens only contain universes with prime numbers:
```rust
// use a default token builder with the same PASETO version and purpose
let token = PasetoBuilder::
PasetoParser::
```
This token will fail to parse with the validation code above:
```rust
// 136 is not a prime number
let token = PasetoBuilder::
```
If the API of this crate doesn't suit your tastes, check out the other PASETO implementations in the Rust ecosystem which inspired rusty_paseto:
paseto - by Cynthia Coan
File an issue or hit me up on Twitter!