Skip to content
/ jose Public

Idiomatic implementation of JWS/JWK/JWT for golang

License

Notifications You must be signed in to change notification settings

halimath/jose

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jose

An implementation of several JSON Object Signature and Encryption (JOSE) specs for Go: JWS, JWK and JWT.

CI Status Go Report Card Package Doc Releases

This repo contains a module for the Golang programming language that provides an implementation for JSON Web Signature (JWS; RFC 7517), JSON Web Keys (JWK; RFC 7517) as well as JSON Web Tokens (JWT; RFC7519).

The module tries to provide an idiomatic Go API for creating, signing, decoding and verifying JSON Web Tokens and exporting cryptographic keys in JSON Web Key standard. While this module contains packages named jwk, jws and jwt these packages do not strictly adhere to the content specified in the respective RFC. This is especially true for all the algorithms defined in RFC 7518 - JSON Web Algorithms - which are in part implemented in this module.

Features

The following list summarizes the features provided by the this module.

  • JWS
    • Sign and verify content using
      • HS256
      • HS384
      • HS512
      • RS256
      • RS384
      • RS512
      • ES256
      • ES384
      • ES512
  • JWT
    • Sign and verify tokens using the above signature methods
    • Encode and decode claims standard claims
    • Encode and decode custom claims
    • Verify standard claims:
      • Issuer
      • Audience
      • Expires
      • Not before
      • Max age

Installation

Use go get to install the libary with your project.

$ go get github.com/halimath/jose

You need Go >= 1.18 to use the lib.

The production code has no other dependencies but the Go standard library. It uses encoding/json to do the marshaling/unmarshaling of JSON and uses several crypto packages. The only dependency declared in go.mod is the excellent github.com/go-test/deep which is used in the unit tests.

Usage

JWT & JWS

The following code snippet shows how to create, sign, decode and verify a JWT using just the standard claims of the spec. The example uses the HS256 signature method which uses as single, symmetric key to both sign and verify.

sig := jws.HS256([]byte("sh256-secret-key"))

claims := jwt.StandardClaims{
    ID:      "17",
    Subject: "john.doe",
    Issuer:  "test",
    Audience: []string{
        "test",
        "anotherTest",
    },
    ExpirationTime: time.Now().Add(time.Hour).Unix(),
}

token, err := jwt.Sign(sig, claims)
if err != nil {
    panic(err)
}

tokenInCompactSerialization := token.Compact()

fmt.Printf("JWT: %s\n", tokenInCompactSerialization)

tokenDecoded, err := jwt.Decode(tokenInCompactSerialization)
if err != nil {
    panic(err)
}

if err := tokenDecoded.Verify(jwt.Signature(sig), jwt.ExpirationTime(time.Second)); err != nil {
    panic(err)
}

To run the code you need to add the following imports along with all the standard import:

"github.com/halimath/jose/jws"
"github.com/halimath/jose/jwt"

A central type when using JWT is jwt.Token. A token is basically a jws.JWS which consists of a JOSE-header, a payload and a signature. In case of a jwt.Token, additional methods are provided to interact with the payload which is known to be valid JSON. To create a jwt.Token you can use one of the two functions:

  • jwt.Sign which creates a token by applying a signer to the claims
  • jwt.Decode which decodes a token from its compact serialization which is the form most people associate with a JWT: three base64 encoded strings separated by dots.

Note that a decoded token is not verified. This is a design intention which simplifies using tokens which are known to be valid and safe to use. Decoding simply makes sure, that the given string contains a valid token in compact serialization.

To verify a token you use the Verify method and pass a list of verifiers to apply. A jwt.Verifier is an interface type with a single Verify method. The package contains implementations for most of the standard claims as well as the signature. You can also create your own verifier and have them applied.

The following example shows how to create a token using custom claims and use a RSA key pair to sign and verify the token.

privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
    panic(err)
}

signer := jws.RS256Signer(privateKey)

type Claims struct {
    jwt.StandardClaims
    Fullname string `json:"example.com/fullname"`
}

claims := Claims{
    StandardClaims: jwt.StandardClaims{
        ID:      "17",
        Subject: "john.doe",
        Issuer:  "test",
        Audience: []string{
            "test",
            "anotherTest",
        },
        ExpirationTime: time.Now().Add(time.Hour).Unix(),
    },
    Fullname: "John Doe",
}

token, err := jwt.Sign(signer, claims)
if err != nil {
    panic(err)
}

tokenInCompactSerialization := token.Compact()

fmt.Printf("JWT: %s\n", tokenInCompactSerialization)

token2, err := jwt.Decode(tokenInCompactSerialization)
if err != nil {
    panic(err)
}

verifier := jws.RS256Verifier(&privateKey.PublicKey)

if err := token2.Verify(jwt.Signature(verifier), jwt.ExpirationTime(time.Second)); err != nil {
    panic(err)
}

var c Claims
if err := token2.Claims(&c); err != nil {
    panic(err)
}

fmt.Printf("Full name: %s\n", c.Fullname)

Note that when using an asymmetric signature method (such as RSA or elliptic curves) you need to create a signer and a verifier, which are different values. When using a symmetric method, a single value implements both steps.

Also note that the example creates a custom type Claims to hold the token's claims. While it is common that this type embeds jwt.StandardClaims it is not required. You can use whatever type you want as claims, as long as it can be marshaled to JSON using encoding/json.

To unmarshal the token's payload into a custom claims value use the token.Claims method which uses encoding/json under the hood.

License

Copyright 2021 Alexander Metzner

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

About

Idiomatic implementation of JWS/JWK/JWT for golang

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages