jwx

package module
v2.0.0-alpha1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 3, 2022 License: MIT Imports: 5 Imported by: 0

README

github.com/lestrrat-go/jwx/v2 Go Reference codecov.io

Go module implementing various JWx (JWA/JWE/JWK/JWS/JWT, otherwise known as JOSE) technologies.

If you are using this module in your product or your company, please add your product and/or company name in the Wiki! It really helps keeping up our motivation.

Features

  • Complete coverage of JWA/JWE/JWK/JWS/JWT, not just JWT+minimum tool set.
    • Supports JWS messages with multiple signatures, both compact and JSON serialization
    • Supports JWS with detached payload
    • Supports JWS with unencoded payload (RFC7797)
    • Supports JWE messages with multiple recipients, both compact and JSON serialization
    • Most operations work with either JWK or raw keys e.g. *rsa.PrivateKey, *ecdsa.PrivateKey, etc).
  • Opinionated, but very uniform API. Everything is symmetric, and follows a standard convetion
    • jws.Parse/Verify/Sign
    • jwe.Parse/Encrypt/Decrypt
    • Arguments are organized as explicit required paramters and optional WithXXXX() style options.
  • Extra utilities
    • jwk.Cache to always keep a JWKS up-to-date

Some more in-depth discussion on why you might want to use this library over others can be found in the Description section

SYNOPSIS

package examples_test

import (
  "bytes"
  "fmt"
  "net/http"
  "time"

  "github.com/lestrrat-go/jwx/v2/jwa"
  "github.com/lestrrat-go/jwx/v2/jwe"
  "github.com/lestrrat-go/jwx/v2/jwk"
  "github.com/lestrrat-go/jwx/v2/jws"
  "github.com/lestrrat-go/jwx/v2/jwt"
)

func ExampleJWX() {
  // Parse, serialize, slice and dice JWKs!
  privkey, err := jwk.ParseKey(jsonRSAPrivateKey)
  if err != nil {
    fmt.Printf("failed to parse JWK: %s\n", err)
    return
  }

  pubkey, err := jwk.PublicKeyOf(privkey)
  if err != nil {
    fmt.Printf("failed to get public key: %s\n", err)
    return
  }

  // Work with JWTs!
  {
    // Build a JWT!
    tok, err := jwt.NewBuilder().
      Issuer(`github.com/lestrrat-go/jwx`).
      IssuedAt(time.Now()).
      Build()
    if err != nil {
      fmt.Printf("failed to build token: %s\n", err)
      return
    }

    // Sign a JWT!
    signed, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, privkey))
    if err != nil {
      fmt.Printf("failed to sign token: %s\n", err)
      return
    }

    // Verify a JWT!
    {
      verifiedToken, err := jwt.Parse(signed, jwt.WithKey(jwa.RS256, pubkey))
      if err != nil {
        fmt.Printf("failed to verify JWS: %s\n", err)
        return
      }
      _ = verifiedToken
    }

    // Work with *http.Request!
    {
      req, err := http.NewRequest(http.MethodGet, `https://github.com/lestrrat-go/jwx`, nil)
      req.Header.Set(`Authorization`, fmt.Sprintf(`Bearer %s`, signed))

      verifiedToken, err := jwt.ParseRequest(req, jwt.WithKey(jwa.RS256, pubkey))
      if err != nil {
        fmt.Printf("failed to verify token from HTTP request: %s\n", err)
        return
      }
      _ = verifiedToken
    }
  }

  // Encrypt and Decrypt arbitrary payload with JWE!
  {
    encrypted, err := jwe.Encrypt(payloadLoremIpsum, jwe.WithKey(jwa.RSA_OAEP, jwkRSAPublicKey))
    if err != nil {
      fmt.Printf("failed to encrypt payload: %s\n", err)
      return
    }

    decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP, jwkRSAPrivateKey))
    if err != nil {
      fmt.Printf("failed to decrypt payload: %s\n", err)
      return
    }

    if !bytes.Equal(decrypted, payloadLoremIpsum) {
      fmt.Printf("verified payload did not match\n")
      return
    }
  }

  // Sign and Verify arbitrary payload with JWS!
  {
    signed, err := jws.Sign(payloadLoremIpsum, jws.WithKey(jwa.RS256, jwkRSAPrivateKey))
    if err != nil {
      fmt.Printf("failed to sign payload: %s\n", err)
      return
    }

    verified, err := jws.Verify(signed, jws.WithKey(jwa.RS256, jwkRSAPublicKey))
    if err != nil {
      fmt.Printf("failed to verify payload: %s\n", err)
      return
    }

    if !bytes.Equal(verified, payloadLoremIpsum) {
      fmt.Printf("verified payload did not match\n")
      return
    }
  }
  // OUTPUT:
}

source: examples/jwx_readme_example_test.go

How-to Documentation

Description

This Go module implements JWA, JWE, JWK, JWS, and JWT. Please see the following table for the list of available packages:

Package name Notes
jwt RFC 7519
jwk RFC 7517 + RFC 7638
jwa RFC 7518
jws RFC 7515 + RFC 7797
jwe RFC 7516

History

My goal was to write a server that heavily uses JWK and JWT. At first glance the libraries that already exist seemed sufficient, but soon I realized that

  1. To completely implement the protocols, I needed the entire JWT, JWK, JWS, JWE (and JWA, by necessity).
  2. Most of the libraries that existed only deal with a subset of the various JWx specifications that were necessary to implement their specific needs

For example, a certain library looks like it had most of JWS, JWE, JWK covered, but then it lacked the ability to include private claims in its JWT responses. Another library had support of all the private claims, but completely lacked in its flexibility to generate various different response formats.

Because I was writing the server side (and the client side for testing), I needed the entire JOSE toolset to properly implement my server, and they needed to be flexible enough to fulfill the entire spec that I was writing.

So here's github.com/lestrrat-go/jwx/v2. This library is extensible, customizable, and hopefully well organized to the point that it is easy for you to slice and dice it.

Why would I use this library?

There are several other major Go modules that handle JWT and related data formats, so why should you use this library?

From a purely functional perspective, the only major difference is this: Whereas most other projects only deal with what they seem necessary to handle JWTs, this module handles the entire spectrum of JWS, JWE, JWK, and JWT.

That is, if you need to not only parse JWTs, but also to control JWKs, or if you need to handle payloads that are NOT JWTs, you should probably consider using this module. You should also note that JWT is built on top of those other technologies. You simply cannot have a complete JWT package without implementing the entirety of JWS/JWS/JWK, which this library does.

Next, from an implementation perspective, this module differs significantly from others in that it tries very hard to expose only the APIs, and not the internal data. For example, individual JWT claims are not accessible through struct field lookups. You need to use one of the getter methods.

This is because this library takes the stance that the end user is fully capable and even willing to shoot themselves on the foot when presented with a lax API. By making sure that users do not have access to open structs, we can protect users from doing silly things like creating incomplete structs, or access the structs concurrently without any protection. This structure also allows us to put extra smarts in the structs, such as doing the right thing when you want to parse / write custom fields (this module does not require the user to specify alternate structs to parse objects with custom fields)

In the end I think it comes down to your usage pattern, and priorities. Some general guidelines that come to mind are:

  • If you want a single library to handle everything JWx, such as using JWE, JWK, JWS, handling auto-refreshing JWKs, use this module.
  • If you want to honor all possible custom fields transparently, use this module.
  • If you want a standardized clean API, use this module.

Otherwise, feel free to choose something else.

Contributions

Issues

For bug reports and feature requests, please try to follow the issue templates as much as possible. For either bug reports or feature requests, failing tests are even better.

Pull Requests

Please make sure to include tests that excercise the changes you made.

If you are editing auto-generated files (those files with the _gen.go suffix, please make sure that you do the following:

  1. Edit the generator, not the generated files (e.g. internal/cmd/genreadfile/main.go)
  2. Run make generate (or go generate) to generate the new code
  3. Commit both the generator and the generated files

Discussions / Usage

Please try discussions first.

Credits

  • Initial work on this library was generously sponsored by HDE Inc (https://www.hde.co.jp)
  • Lots of code, especially JWE was initially taken from go-jose library (https://github.com/square/go-jose)
  • Lots of individual contributors have helped this project over the years. Thank each and everyone of you very much.

Documentation

Overview

Package jwx contains tools that deal with the various JWx (JOSE) technologies such as JWT, JWS, JWE, etc in Go.

JWS (https://tools.ietf.org/html/rfc7515)
JWE (https://tools.ietf.org/html/rfc7516)
JWK (https://tools.ietf.org/html/rfc7517)
JWA (https://tools.ietf.org/html/rfc7518)
JWT (https://tools.ietf.org/html/rfc7519)

Examples are stored in a separate Go module (to avoid adding dependencies to this module), and thus does not appear in the online documentation for this module. You can find the examples in Github at https://github.com/lestrrat-go/jwx/tree/v2/examples

You can find more high level documentation at Github (https://github.com/lestrrat-go/jwx/tree/v2)

FAQ style documentation can be found in the repository (https://github.com/lestrrat-go/jwx/tree/develop/v2/docs)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecoderSettings

func DecoderSettings(options ...JSONOption)

DecoderSettings gives you a access to configure the "encoding/json".Decoder used to decode JSON objects within the jwx framework.

Types

type FormatKind

type FormatKind int
const (
	UnknownFormat FormatKind = iota
	JWE
	JWS
	JWK
	JWKS
	JWT
)

func GuessFormat

func GuessFormat(payload []byte) FormatKind

GuessFormat is used to guess the format the given payload is in using heuristics. See the type FormatKind for a full list of possible types.

This may be useful in determining your next action when you may encounter a payload that could either be a JWE, JWS, or a plain JWT.

Because JWTs are almost always JWS signed, you may be thrown off if you pass what you think is a JWT payload to this function. If the function is in the "Compact" format, it means it's a JWS signed message, and its payload is the JWT. Therefore this function will reuturn JWS, not JWT.

This function requires an extra parsing of the payload, and therefore may be inefficient if you call it every time before parsing.

func (FormatKind) String

func (i FormatKind) String() string

type JSONOption

type JSONOption interface {
	Option
	// contains filtered or unexported methods
}

func WithUseNumber

func WithUseNumber(b bool) JSONOption

WithUseNumber controls whether the jwx package should unmarshal JSON objects with the "encoding/json".Decoder.UseNumber feature on.

Default is false.

type Option

type Option = option.Interface

Directories

Path Synopsis
internal
ecutil
Package ecutil defines tools that help with elliptic curve related computation
Package ecutil defines tools that help with elliptic curve related computation
Package jwa defines the various algorithm described in https://tools.ietf.org/html/rfc7518
Package jwa defines the various algorithm described in https://tools.ietf.org/html/rfc7518
jwe
Package jwe implements JWE as described in https://tools.ietf.org/html/rfc7516
Package jwe implements JWE as described in https://tools.ietf.org/html/rfc7516
Package jwk implements JWK as described in https://tools.ietf.org/html/rfc7517
Package jwk implements JWK as described in https://tools.ietf.org/html/rfc7517
Package jws implements the digital signature on JSON based data structures as described in https://tools.ietf.org/html/rfc7515 If you do not care about the details, the only things that you would need to use are the following functions: jws.Sign(payload, jws.WithKey(algorithm, key)) jws.Verify(serialized, jws.WithKey(algorithm, key)) To sign, simply use `jws.Sign`.
Package jws implements the digital signature on JSON based data structures as described in https://tools.ietf.org/html/rfc7515 If you do not care about the details, the only things that you would need to use are the following functions: jws.Sign(payload, jws.WithKey(algorithm, key)) jws.Verify(serialized, jws.WithKey(algorithm, key)) To sign, simply use `jws.Sign`.
jwt
Package jwt implements JSON Web Tokens as described in https://tools.ietf.org/html/rfc7519
Package jwt implements JSON Web Tokens as described in https://tools.ietf.org/html/rfc7519
openid
Package openid provides a specialized token that provides utilities to work with OpenID JWT tokens.
Package openid provides a specialized token that provides utilities to work with OpenID JWT tokens.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL