GithubHelp home page GithubHelp logo

jwks-client's Introduction

Build Status License:MIT License:Apache Minimum rustc version

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. Requires Rust Stable 1.39 or higher

** IMPORTANT **

JWKS-Client was designed to work with a project that uses Rocket. Unfortunately, the version of Rocket in crates.io is not compatible with the version of Ring required for JWKS-Client. Until the next version of Rocket is published, consider using the following in your Cargo.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 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 fields

JWKS-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:

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).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.

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:

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).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:

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).unwrap();

    let token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJuYW1lIjoiQWRhIExvdmVsYWNlIiwiaXNzIjoiaHR0cHM6Ly9jaHJvbm9nZWFycy5jb20vdGVzdCIsImF1ZCI6InRlc3QiLCJhdXRoX3RpbWUiOjEwMCwidXNlcl9pZCI6InVpZDEyMyIsInN1YiI6InNidTEyMyIsImlhdCI6MjAwLCJleHAiOjUwMCwibmJmIjozMDAsImVtYWlsIjoiYWxvdmVsYWNlQGNocm9ub2dlYXJzLmNvbSJ9.eTQnwXrri_uY55fS4IygseBzzbosDM1hP153EZXzNlLH5s29kdlGt2mL_KIjYmQa8hmptt9RwKJHBtw6l4KFHvIcuif86Ix-iI2fCpqNnKyGZfgERV51NXk1THkgWj0GQB6X5cvOoFIdHa9XvgPl_rVmzXSUYDgkhd2t01FOjQeeT6OL2d9KdlQHJqAsvvKVc3wnaYYoSqv2z0IluvK93Tk1dUBU2yWXH34nX3GAVGvIoFoNRiiFfZwFlnz78G0b2fQV7B5g5F8XlNRdD1xmVZXU8X2-xh9LqRpnEakdhecciFHg0u6AyC4c00rlo_HBb69wlXajQ3R4y26Kpxn7HA";

    let jwt = key_store.decode(token).unwrap();

    let claims = jwt.payload().into::<MyClaims>().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)
  • 0.1.8

  • 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 version

TODO:

  • Lots More documentation :P
  • Automatically refresh keys

(Made with ❤️ with Rust)

jwks-client's People

Contributors

jfbilodeau avatar wingertge avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.