Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jfbilodeau/jwks-client
JWKS client for Rust
https://github.com/jfbilodeau/jwks-client
Last synced: about 3 hours ago
JSON representation
JWKS client for Rust
- Host: GitHub
- URL: https://github.com/jfbilodeau/jwks-client
- Owner: jfbilodeau
- License: apache-2.0
- Created: 2019-09-21T01:21:06.000Z (about 5 years ago)
- Default Branch: master
- Last Pushed: 2023-04-13T17:09:54.000Z (over 1 year ago)
- Last Synced: 2024-10-31T11:56:43.599Z (8 days ago)
- Language: Rust
- Size: 131 KB
- Stars: 27
- Watchers: 6
- Forks: 29
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
[![Build Status](https://travis-ci.com/jfbilodeau/jwks-client.svg?branch=master)](https://travis-ci.com/jfbilodeau/jwks-client) [![License:MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![License:Apache](https://img.shields.io/badge/License-Apache-yellow.svg)](https://opensource.org/licenses/Apache-2.0) ![Minimum rustc version](https://img.shields.io/badge/rustc-stable-success.svg)
JWKS-Client is a library written in Rust to decode and validate JWT tokens using a JSON Web Key Store.
### Breaking Change
Now at version 2.0. Support for async/await thanks to [Genna Wingert](https://github.com/wingertge).
Requires Rust Stable 1.39 or higher## ** IMPORTANT **
JWKS-Client was designed to work with a project that uses [Rocket](https://crates.io/crates/rocket). Unfortunately, the version of Rocket in [crates.io](https://crates.io) is not compatible with the version of [Ring](https://crates.io/crates/ring) required for JWKS-Client. Until the next version of Rocket is published, consider using the following in your `Cargo.toml`:```toml
[dependencies]
jwks-client = "0.1.4"
rocket = { git = "https://github.com/jfbilodeau/Rocket", version = "0.5.0-dev"}
# Other dependencies...[dependencies.rocket_contrib]
version = "0.5.0-dev"
git = "https://github.com/jfbilodeau/Rocket"
# Other options...```
Features
---### Library wide:
* No panic!
* Build with Rust stable (1.40)
* Designed for a production system (not an academic project)
* Concise results (see [error::Type](https://docs.rs/shared_jwt/latest/shared_jwt/error/enum.Type.html) for example)### JWKS key store
* Download key set from HTTP address
* Decode JWT tokens into header, payload and signature
* Verify token signature, expiry and not-before
* Determine when keys should be refreshed
### JWT:
* Transfer header and payload in user-defined struct. See the example below[^1]
* Accessor for standard header and payload fieldsJWKS-Client was create specifically to decode GCP/Firebase JWT but should be useable with little to no modification. Contact me to propose support for different JWKS key store. Feedback, suggestions, complaints and criticism is appreciated.
Basic Usage
---The following demonstrates how to load a set of keys from an HTTP address and verify a JWT token using those keys:
```rust
use jwks_client::error::Error;
use jwks_client::keyset::KeyStore;#[tokio::main]
async fn main() {
let jkws_url = "https://raw.githubusercontent.com/jfbilodeau/jwks-client/0.1.8/test/test-jwks.json";let key_set = KeyStore::new_from(jkws_url.to_owned()).await.unwrap();
// ...
let token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJuYW1lIjoiQWRhIExvdmVsYWNlIiwiaXNzIjoiaHR0cHM6Ly9jaHJvbm9nZWFycy5jb20vdGVzdCIsImF1ZCI6InRlc3QiLCJhdXRoX3RpbWUiOjEwMCwidXNlcl9pZCI6InVpZDEyMyIsInN1YiI6InNidTEyMyIsImlhdCI6MjAwLCJleHAiOjUwMCwibmJmIjozMDAsImVtYWlsIjoiYWxvdmVsYWNlQGNocm9ub2dlYXJzLmNvbSJ9.eTQnwXrri_uY55fS4IygseBzzbosDM1hP153EZXzNlLH5s29kdlGt2mL_KIjYmQa8hmptt9RwKJHBtw6l4KFHvIcuif86Ix-iI2fCpqNnKyGZfgERV51NXk1THkgWj0GQB6X5cvOoFIdHa9XvgPl_rVmzXSUYDgkhd2t01FOjQeeT6OL2d9KdlQHJqAsvvKVc3wnaYYoSqv2z0IluvK93Tk1dUBU2yWXH34nX3GAVGvIoFoNRiiFfZwFlnz78G0b2fQV7B5g5F8XlNRdD1xmVZXU8X2-xh9LqRpnEakdhecciFHg0u6AyC4c00rlo_HBb69wlXajQ3R4y26Kpxn7HA";
match key_set.verify(token) {
Ok(jwt) => {
println!("name={}", jwt.payload().get_str("name").unwrap());
}
Err(Error { msg, typ: _ }) => {
eprintln!("Could not verify token. Reason: {}", msg);
}
}
}
```JWKS-Client can be use to simply decode a JWT token without validating the signature.
```rust
use jwks_client::keyset::KeyStore;fn main() {
let key_store = KeyStore::new();let token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJuYW1lIjoiQWRhIExvdmVsYWNlIiwiaXNzIjoiaHR0cHM6Ly9jaHJvbm9nZWFycy5jb20vdGVzdCIsImF1ZCI6InRlc3QiLCJhdXRoX3RpbWUiOjEwMCwidXNlcl9pZCI6InVpZDEyMyIsInN1YiI6InNidTEyMyIsImlhdCI6MjAwLCJleHAiOjUwMCwibmJmIjozMDAsImVtYWlsIjoiYWxvdmVsYWNlQGNocm9ub2dlYXJzLmNvbSJ9.eTQnwXrri_uY55fS4IygseBzzbosDM1hP153EZXzNlLH5s29kdlGt2mL_KIjYmQa8hmptt9RwKJHBtw6l4KFHvIcuif86Ix-iI2fCpqNnKyGZfgERV51NXk1THkgWj0GQB6X5cvOoFIdHa9XvgPl_rVmzXSUYDgkhd2t01FOjQeeT6OL2d9KdlQHJqAsvvKVc3wnaYYoSqv2z0IluvK93Tk1dUBU2yWXH34nX3GAVGvIoFoNRiiFfZwFlnz78G0b2fQV7B5g5F8XlNRdD1xmVZXU8X2-xh9LqRpnEakdhecciFHg0u6AyC4c00rlo_HBb69wlXajQ3R4y26Kpxn7HA";
let jwt = key_store.decode(token).unwrap();
if jwt.expired().unwrap_or(false) {
println!("Sorry, token expired")
} else {
let result = jwt.payload().get_str("name");match result {
Some(name) => {
println!("Welcome, {}!", name);
}
None => {
println!("Welcome, anonymous");
}
}
}
}
```JWKS-Client offers descriptive error results:
```rust
use jwks_client::error::{Error, Type};
use jwks_client::keyset::KeyStore;#[rustfmt::skip]
#[tokio::main]
async fn main() {
let url = "https://raw.githubusercontent.com/jfbilodeau/jwks-client/0.1.8/test/test-jwks.json";
let token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJuYW1lIjoiQWRhIExvdmVsYWNlIiwiaXNzIjoiaHR0cHM6Ly9jaHJvbm9nZWFycy5jb20vdGVzdCIsImF1ZCI6InRlc3QiLCJhdXRoX3RpbWUiOjEwMCwidXNlcl9pZCI6InVpZDEyMyIsInN1YiI6InNidTEyMyIsImlhdCI6MjAwLCJleHAiOjUwMCwibmJmIjozMDAsImVtYWlsIjoiYWxvdmVsYWNlQGNocm9ub2dlYXJzLmNvbSJ9.eTQnwXrri_uY55fS4IygseBzzbosDM1hP153EZXzNlLH5s29kdlGt2mL_KIjYmQa8hmptt9RwKJHBtw6l4KFHvIcuif86Ix-iI2fCpqNnKyGZfgERV51NXk1THkgWj0GQB6X5cvOoFIdHa9XvgPl_rVmzXSUYDgkhd2t01FOjQeeT6OL2d9KdlQHJqAsvvKVc3wnaYYoSqv2z0IluvK93Tk1dUBU2yWXH34nX3GAVGvIoFoNRiiFfZwFlnz78G0b2fQV7B5g5F8XlNRdD1xmVZXU8X2-xh9LqRpnEakdhecciFHg0u6AyC4c00rlo_HBb69wlXajQ3R4y26Kpxn7HA";let key_set = KeyStore::new_from(url.to_owned()).await.unwrap();
match key_set.verify(token) {
Ok(jwt) => {
println!("name={}", jwt.payload().get_str("name").unwrap());
}
Err(Error {
msg,
typ: Type::Header,
}) => {
eprintln!("Problem with header. Message: {}", msg);
}
Err(Error {
msg,
typ: Type::Payload,
}) => {
eprintln!("Problem with payload. Message: {}", msg);
}
Err(Error {
msg,
typ: Type::Signature,
}) => {
eprintln!("Problem with signature. Message: {}", msg);
}
Err(Error {
msg: _,
typ: Type::Expired,
}) => {
eprintln!("Token is expired.");
}
Err(Error {
msg: _,
typ: Type::Early,
}) => {
eprintln!("Too early to use token.");
}
Err(e) => {
eprintln!("Something else went wrong. Message {:?}", e);
}
}
}
```[^1] JWKS-Client can decode a JWT payload (claims) into a struct:
```rust
use serde_derive::Deserialize;use jwks_client::keyset::KeyStore;
fn main() {
#[derive(Deserialize)]
pub struct MyClaims {
pub iss: String,
pub name: String,
pub email: String,
}let url = "https://raw.githubusercontent.com/jfbilodeau/jwks-client/0.1.8/test/test-jwks.json";
let key_store = KeyStore::new_from(url.to_owned()).unwrap();
let token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJuYW1lIjoiQWRhIExvdmVsYWNlIiwiaXNzIjoiaHR0cHM6Ly9jaHJvbm9nZWFycy5jb20vdGVzdCIsImF1ZCI6InRlc3QiLCJhdXRoX3RpbWUiOjEwMCwidXNlcl9pZCI6InVpZDEyMyIsInN1YiI6InNidTEyMyIsImlhdCI6MjAwLCJleHAiOjUwMCwibmJmIjozMDAsImVtYWlsIjoiYWxvdmVsYWNlQGNocm9ub2dlYXJzLmNvbSJ9.eTQnwXrri_uY55fS4IygseBzzbosDM1hP153EZXzNlLH5s29kdlGt2mL_KIjYmQa8hmptt9RwKJHBtw6l4KFHvIcuif86Ix-iI2fCpqNnKyGZfgERV51NXk1THkgWj0GQB6X5cvOoFIdHa9XvgPl_rVmzXSUYDgkhd2t01FOjQeeT6OL2d9KdlQHJqAsvvKVc3wnaYYoSqv2z0IluvK93Tk1dUBU2yWXH34nX3GAVGvIoFoNRiiFfZwFlnz78G0b2fQV7B5g5F8XlNRdD1xmVZXU8X2-xh9LqRpnEakdhecciFHg0u6AyC4c00rlo_HBb69wlXajQ3R4y26Kpxn7HA";
let jwt = key_store.decode(token).unwrap();
let claims = jwt.payload().into::().unwrap();
println!("Issuer: {}", claims.iss);
println!("Name: {}", claims.name);
println!("Email: {}", claims.email);
}
```History
---
* 0.2.0
* **Breaking Change**: Support for async/await (Thanks to [Genna Wingert](htps://github.com/wingertge))
* 0.1.8
* Fixed issued https://github.com/jfbilodeau/jwks-client/issues/1 (Thanks to [Tim Schuster](https://github.com/tscs37) for reporting and assisting)
* 0.1.7
* Updated dependencies
* 0.1.6
* Added `key_set::KeyStore::should_refresh()` to test if keys should be refreshed
* Added `key_set::KeyStore::refresh_interval` to determine how early keys should be refreshed before they expire
* Some more documentation
* 0.1.5:
* Added `readme = "README.md"` to `Cargo.toml`
* 0.1.4:
* Updated documentation--specifically how to use JWKS-Client with Rocket
* Added the ability to determine if keys should be refreshed from the `KeyStore`
* Fixed example on this page--they are now directly from `./examples/*`
* 0.1.3:
* Change the license to be MIT/Apache
* Moved demos into `./example`
* Added the ability to verify if keys need to be refreshed in the keystore based on the cache-control header
* 0.1.2: (Sorry for the breaking changes)
* Rename module `jwks` to `keyset`
* Renamed struct `Jwks` to `KeyStore`
* Expanded documentation a bit
* Fixed some demos
* 0.1.1: Original versionTODO:
---
* Lots More documentation :P
* Automatically refresh keys(Made with ❤️ with Rust)