JAWS: JSON Web tokens
JSON Web Tokens are used to send signed, authenticated and/or encrypted data using a JavaScript Object Notation (JSON) object. This crate provides a strongly typed interface for creating and validating JWTs, built on top of the RustCrypto ecosystem.
Example JWT
This is an example JWT, taken from the ACME standard (RFC 8555):
This JWT represents a request signed with an ECDSA key using the NIST P-256 curve (ES256).
The payload is a JSON object containing an account creation request for an ACME server.
Since ACME accounts are identified by the public half of an encryption key, the public key
used to encode this JWT is included in the JWT's headers as jwk, a JSON Web Key (JWK).
JSON Web Tokens contain a protected header (protected above), which contains a number of
"registered" keys (in this case, alg and jwk), along with an arbitrary set of custom
keys. The payload is any JSON object, though the JWT standard defines a set of claims
used for authentication, including some "registered" claims (claims are not used by ACME).
The signature is produced by computing the ECDSA signature over the concatenation of the
protected header and the payload, both base64url encoded, and separated with a period (.).
Strongly Typed JWTs
JAWS provides a strongly typed interface for creating and validating JWTs. Tokens in JAWS must be in one of 4 states:
- [
Unsigned][crate::token::Unsigned]: A token which has not been signed, and has no signature. - [
Signed][crate::token::Signed]: A token which has been signed. Signed tokens can't be modified, as that could invalidate the signature. - [
Verified][crate::token::Verified]: A token which has been verified. Verified tokens can't be modified, as that could invalidate the signature. Verified tokens can't know the relationship between fields (i.e. thejwkheader may represent some key not related to the token at all). - [
Unverified][crate::token::Unverified]: A token which has been deserialized, and not verified.
stateDiagram-v2
[*] --> Unverified : from JSON
Unverified --> Verified : Verify
Unsigned --> Signed : Sign
Signed --> Unverified: Unverify
Signed --> [*] : to JSON
Example Usage
To create a simple JWT, you'll need to provide an encryption key. This example uses the RSA encrption key defined in Appendix A.2 of RFC 7515, don't re use it!
This example is reproduced from examples/rfc7515a2.rs in the repository,
and can be run with cargo run --example rfc7515-a2.
use Compact;
// JAWS provides JWT format for printing JWTs in a style similar to the example above,
// which is directly inspired by the way the ACME standard shows JWTs.
use JWTFormat;
// JAWS provides a single token type which is generic over the state of the token.
// The states are defined in the `state` module, and are used to track the
// signing and verification status.
use Token;
use DeserializeJWK;
// The unverified token state, used like `Token<.., Unverified<..>, ..>`.
// It is generic over the type of the custom header parameters.
use Unverified;
// JAWS provides type-safe support for JWT claims.
use ;
// We are going to use an RSA private key to sign our JWT, provided by
// the `rsa` crate in the RustCrypto suite.
use DecodePrivateKey;
// The signing algorithm we will use (`RS256`) relies on the SHA-256 hash
// function, so we get it here from the `sha2` crate in the RustCrypto suite.
use Sha256;
// Using serde_json allows us to quickly construct a serializable payload,
// but applications may want to instead define a struct and use serde to
// derive serialize and deserialize for added type safety.
use json;
// Trait to convert a SigningKey into a VerifyingKey.
use Keypair;
Philosophy
There are quite a few JWT crates in the Rust ecosystem, but I wanted to create one for a few reasons:
- I wanted to try to build a JWT crate which is strongly typed and models as much of the JWT ecosystm in the rust type system as possible. This means that sometimes types proliferate (see the [
jose] module), but it also means that illegal states are difficult, if not hopefully impossible, to represent in this crate. - I wanted to ensure I had strongly typed support for registered headers and registered claims, and to do so in a fashion which correctly handles interdependent fields, especially the
algheader value, which depends on the encryption key used. Most other JWT crates provide an error at runtime for setting thealgheader to an incompatible value, but this crate simply does not make that possible. Additional header values (e.g.x5t,jwk) can be derived from the signing key, so that they are always correct for the key in use. - I wanted broad support for the RustCrypto ecosystem, and where possible, I've tried to implement JWTs on top of native RustCrypto traits. For example, the
RS256signing algorithm is represented by the [rsa::pkcs1v15::SigningKey<sha2::SHA256>][rsa::pkcs1v15::SigningKey] type, with no additional wrappers. - I also wanted to provide a strong high level interface which makes examples easy to use and easy to follow. I hope that despite the copious comments in my example above, it is clear that JAWS APIs are pretty easy to use.
On Unsafe Code
There are no uses of unsafe code in this crate which are required for the primary JWT functionality. The only uses of unsafe code are in the [jaws::fmt][crate::fmt] module to provide efficient formatting methods.
However, the fmt feature is not required for most functionality, and rather is most useful for debugging the contents of JWTs. If you are concerned about the use of unsafe code, you can disable the fmt feature to remove the unsafe code.