Categorygithub.com/stevecallear/janice-jwt
modulepackage
1.2.0
Repository: https://github.com/stevecallear/janice-jwt.git
Documentation: pkg.go.dev

# README

janice-jwt

Build Status codecov Go Report Card

janice-jwt provides JWT token middleware for use with Janice. It uses jwt-go for token parsing and is similar in approach to go-jwt-middleware but as a janice.MiddlewareFunc implementation.

Getting started

go get -u github.com/stevecallear/janice-jwt
package main

import (
	"fmt"
	"net/http"

	jwt "github.com/stevecallear/janice-jwt"
)

var key = []byte("secretkey")

func main() {
	auth := jwt.New(jwt.HMAC(key))

	handler := auth.Then(func(w http.ResponseWriter, r *http.Request) error {
		c, _ := jwt.GetClaims(r)
		_, err := fmt.Fprintf(w, c["sub"].(string))
		return err
	})

	http.ListenAndServe(":8080", handler)
}

Token extraction

By default the middleware is configured to extract a bearer token from the request authorization header. This behaviour can be customised by replacing the options TokenFn. For example:

auth := jwt.New(jwt.HMAC(key), func(opt *jwt.Options) {
    opt.TokenFn = func(r *http.Request) (string, bool) {
        // return the extracted token and true if successful
    }
})

Signing methods

The module supports HMAC and RSA signing keys using jwt.HMAC and jwt.RSA respectively. Other signing methods are supported by replacing the options KeyFn. For example, the following returns an ECDSA signing key:

// import jwtgo "github.com/dgrijalva/jwt-go"

auth := jwt.New(func(o *jwt.Options) {
    o.KeyFn = func(_ *http.Request, t *jwtgo.Token) (interface{}, error) {
        // validate the signing method
        if _, ok := t.Method.(*jwtgo.SigningMethodECDSA); !ok {
            return nil, fmt.Errorf("invalid signing method: %s", t.Header["alg"])
        }

        return key, nil
    }
})

The requests is included in the KeyFn signature to support request-specific keys.

Error handling

Any errors extracting or parsing the token are handled by ErrorFn. By default this writes http.StatusUnauthorized to the response and returns nil. The behaviour can be customised to add logging or use error handling. The below example uses strudel to log and handle unauthorized errors:

auth := jwt.New(jwt.HMAC(key), func(opt *jwt.Options) {
    opt.ErrorFn = func(_ http.ResponseWriter, err error) error {
        return strudel.NewError(http.StatusText(http.StatusUnauthorized)).
            WithCode(http.StatusUnauthorized).
            WithLogField("err", err)
    }
})

chain := janice.New(strudel.ErrorHandling, auth)

handler := chain.Then(func(w http.ResponseWriter, r *http.Request) error {
    return nil
})

Anonymous requests

Anonymous requests can be permitted using the jwt.Optional option. This ensures that the request will be handled if TokenFn does not return a value. If a token is returned, but cannot be parsed then ErrorFn will be invoked as per standard behaviour. For example:

auth := jwt.New(jwt.RSA(key), jwt.Optional)

handler := auth.Then(func(w http.ResponseWriter, r *http.Request) error {
    var err error

    if c, ok := jwt.GetClaims(r); ok {
        _, err = fmt.Fprint(w, c["sub"].(string))
    } else {
        _, err = fmt.Fprint(w, "anonymous")
    }

    return err
})

# Functions

GetClaims returns the claims for the specified request context.
HMAC configures the middleware to use the specified HMAC key.
New returns new JWT middleware for the specified option funcs.
Optional configures the middleware to allow unauthorized requests.
RSA configures the middleware to use the specified RSA PEM key.
WithClaims returns a copy of the request with the specified claims stored in the context The function is exported to simplify testing for apps that use GetClaims.

# Structs

No description provided by the author