/jwt

A simple and easy to use JWT generation/verification library

Primary LanguageGoMIT LicenseMIT

Json Web Token (RFC7519) Library

Mission

This library has two goals.

  • Make generating and validating JWTs as intuitive and easy as possible, with the ability to add complexity if the developer chooses
  • Follow RFC7519 and implement the recommended cryptographic algorithms for both JWS and JWE

Disclaimer

This library is extremely new. Integrate it with your applications at your own risk. Notably, the library could rapidly change in design - causing breaking changes.

Quickstart

If all you want to do is generate and validate a JWT, use these examples.

Generating a HS256 JWT

package main

import (
    "fmt"
    . "github.com/bmwadforth/jwt"
    "log"
    "time"
)

func main(){
    key := []byte("Key")

    claims := NewClaimSet()
    claims.Add(string(Audience), "your_audience")
    claims.Add(string(Subject), "your_subject")
    claims.Add(string(IssuedAt), time.Now())
    claims.Add("my_claim", "some_value")

    //Create new HS256 token, set claims and key
    token, err := New(HS256, claims, key)
    if err != nil {
        log.Fatal(err)
    }

    //Encode token
    tokenBytes, err := token.Encode()
    fmt.Println(string(tokenBytes))
    //eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ5b3VyX2F1ZGllbmNlIiwiaWF0IjoiMjAyMC0wMS0wMlQyMTo1NTo1OS40MzE1ODErMTE6MDAiLCJteV9jbGFpbSI6InNvbWVfdmFsdWUiLCJzdWIiOiJ5b3VyX3N1YmplY3QifQ.PAR_a60R6VZakCmBZg8aMgt3eXDi-CMC4P4p08yJy-I
}

Validating a HS256 JWT

package main

import (
    "fmt"
    . "github.com/bmwadforth/jwt"
    "log"
)

func main(){
    key := []byte("Key")
    tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ5b3VyX2F1ZGllbmNlIiwiaWF0IjoiMjAyMC0wMS0wMlQyMTo1NTo1OS40MzE1ODErMTE6MDAiLCJteV9jbGFpbSI6InNvbWVfdmFsdWUiLCJzdWIiOiJ5b3VyX3N1YmplY3QifQ.PAR_a60R6VZakCmBZg8aMgt3eXDi-CMC4P4p08yJy-I"
    
    //Parse token string
    token, err := Parse(tokenString, key)
    if err != nil {
        log.Fatal(err)
    }
    
    //Validate token
    _, err = Validate(token)
    if err != nil {
        log.Fatal(err)
    }   
    
    //Token is valid
}

Custom Signing Method

If you would prefer to define your own JWS signing method, you can define your own signing function. Notably:

  • The signing function will always receive a base64 encoded header and payload as the bytes to sign, per the JWS specification
  • What you return from the signing function is base 64 encoded and attached to the signature component of the JWS

A good example of when you would want to implement your own signing function is when you want more control over how to sign your token. For example, RS256:

package main

import (
    "fmt"
    . "github.com/bmwadforth/jwt"
    "io/ioutil"
    "log"
    "crypto/x509"
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha256"
    "crypto"
    "encoding/pem"
)

func main(){
    b, _ := ioutil.ReadFile("./rsa_private.pem")

    block, _ := pem.Decode(b)
    key, _ := x509.ParsePKCS1PrivateKey(block.Bytes)

    token, err := New(RS256, NewClaimSet(), block.Bytes)
    if err != nil {
        log.Fatal(err)
    }

    //Before calling sign, set SignFunc
    token.SignFunc = func(t *Token, signingInput []byte) (bytes []byte, e error) {
        // crypto/rand.Reader is a good source of entropy for blinding the RSA
        // operation.
        rng := rand.Reader
        hashed := sha256.Sum256(signingInput)

        signature, err := rsa.SignPKCS1v15(rng, key, crypto.SHA256, hashed[:])
        if err != nil {
            return nil, err
        }

        return signature, nil
    }

    signedBytes, err := token.Sign()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(string(signedBytes))
    //eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.uZTBWMOdIYMlSxyJgGOgjPXwISnMDzLyiOE5k9GK2ruWc2IvWkOLtmZ9ECOwDqwLM93WH7CMIP7IEOMVZJzkHkFj16GgQnz-KSgY9MK8fBROij4R09XyXVRMvmBjVAyPxBS8dK9j-FuZIceu5TEN3-FmjcTq87OQfc3-mO6_3mruQfg59m9dSbcVL2SEQrRyrG-Jitkma7f_up8BSJHt0Q08ASVBivHjws2Z_QGYb3NkrI0oEcH_yoXlvJohsEQtNaycFLGNDtzujABHp9ZT5a2L-U8WCf8K9JwttGnuVTMhDviEjWC2M2weXAB8WimiwqQB2zER-4ILpbUhhL_MjA
}

Custom Validation Method

Just as you can create a custom signing method, you can also create a custom validation method.

package main

import (
    "fmt"
    . "github.com/bmwadforth/jwt"
    "io/ioutil"
    "log"
    "crypto/x509"
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha256"
    "crypto"
    "encoding/pem"
    "encoding/base64"
)

func main(){
    b, _ := ioutil.ReadFile("./rsa_private.pem")
    block, _ := pem.Decode(b)
    key, _ := x509.ParsePKCS1PrivateKey(block.Bytes)

    tokenString := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.uZTBWMOdIYMlSxyJgGOgjPXwISnMDzLyiOE5k9GK2ruWc2IvWkOLtmZ9ECOwDqwLM93WH7CMIP7IEOMVZJzkHkFj16GgQnz-KSgY9MK8fBROij4R09XyXVRMvmBjVAyPxBS8dK9j-FuZIceu5TEN3-FmjcTq87OQfc3-mO6_3mruQfg59m9dSbcVL2SEQrRyrG-Jitkma7f_up8BSJHt0Q08ASVBivHjws2Z_QGYb3NkrI0oEcH_yoXlvJohsEQtNaycFLGNDtzujABHp9ZT5a2L-U8WCf8K9JwttGnuVTMhDviEjWC2M2weXAB8WimiwqQB2zER-4ILpbUhhL_MjA"

    token, err := Parse(tokenString, b)
    if err != nil {
        log.Fatal(err)
    }

    //Before calling validate, set ValidateFunc
    token.ValidateFunc = func(t *Token) (b bool, e error) {
        headerB64, _ := t.Header.ToBase64()
        payloadB64, _ := t.Payload.ToBase64()
        hashed := sha256.Sum256([]byte(fmt.Sprintf("%s.%s", headerB64, payloadB64)))
        decodedSignature, err := base64.RawURLEncoding.DecodeString(string(t.Signature.Raw))
        if err != nil {
            return false, err
        }
        err = rsa.VerifyPKCS1v15(&key.PublicKey, crypto.SHA256, hashed[:], decodedSignature)
        if err != nil {
            return false, err
        }
        return true, nil
    }

    _, err = token.Validate()
    if err != nil {
        log.Fatal(err)
    }

    //Token is valid
}

Supported Algorithms

This library currently supports JWS only.

JWS

"alg" Param Digital Signature/MAC Algorithm Implementation Requirement Implemented
HS256 HMAC using SHA-256 Required
HS384 HMAC using SHA-384 Optional
HS512 HMAC using SHA-512 Optional
RS256 RSASSA-PKCS1-v1_5 using SHA-256 Recommended
RS384 RSASSA-PKCS1-v1_5 using SHA-384 Optional
RS512 RSASSA-PKCS1-v1_5 using SHA-512 Optional
ES256 ECDSA using P-256 and SHA-256 Recommended+
ES384 ECDSA using P-384 and SHA-384 Optional
ES512 ECDSA using P-521 and SHA-512 Optional
PS256 RSASSA-PSS using SHA-256 and MGF1 with SHA-256 Optional
PS384 RSASSA-PSS using SHA-384 and MGF1 with SHA-384 Optional
PS512 RSASSA-PSS using SHA-512 and MGF1 with SHA-512 Optional
none No digital signature or MAC performed Optional

JWE

JWE has not been implemented in this library yet