Home

Awesome

JWT Middleware

GoDoc Build Status codecov

<a href="https://www.buymeacoffee.com/mfuentesg" target="_blank"> <img height="41" src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" /> </a> <br /> <br />

This package has as purpose will help you to check the validness of any token based on JWT spec.

Getting started

Installation

$ go get github.com/mfuentesg/go-jwtmiddleware

Using it

You can use it with the net/http package or even with a middleware-focused library like Negroni.

net/http

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/mfuentesg/go-jwtmiddleware"
)

func helloWorld(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("hello world!"))
}

func main() {
	m := jwtmiddleware.New(jwtmiddleware.WithSignKey([]byte("secret")))
	http.Handle("/", m.Handler(http.HandlerFunc(helloWorld)))
	fmt.Println("listening on port :3000")
	log.Fatal(http.ListenAndServe(":3000", nil))
}

negroni

package main

import (
	"net/http"

	"github.com/codegangsta/negroni"
	"github.com/mfuentesg/go-jwtmiddleware"
)

func main() {
	m := jwtmiddleware.New(jwtmiddleware.WithSignKey("user"))
	n := negroni.Classic()
	mux := http.NewServeMux()

	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("hello world!"))
	})
	n.UseFunc(m.HandlerNext)
	n.UseHandler(mux)

	http.ListenAndServe(":3000", n)
}

Options

This project is based on functional options concept, to set the initial settings of the middleware. These options must be passed on the jwtmiddleware.New() function which options are of the type MiddlewareOption func(*options).

errorHandler

This property is a callback to control the ocurred errors on the validation process. Unlike other middlewares, this package returns jwt-go native errors, given you the posibility to check errors with the defined contants, for example jwt.ErrSignatureInvalid.

package main

import (
	"fmt"
	"log"
	"net/http"

	mdw "github.com/mfuentesg/go-jwtmiddleware"
)

func errorHandler(w http.ResponseWriter, r *http.Request, err error) {
	w.WriteHeader(http.StatusUnauthorized)
	w.Write([]byte(fmt.Sprintf("unauthorized : %v", err)))
}

func main() {
	m := mdw.New(mdw.WithSignKey([]byte("secret")), mdw.WithErrorHandler(errorHandler))
	http.Handle("/", m.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("hello world!"))
	})))
	fmt.Println("listening on port :3000")
	log.Fatal(http.ListenAndServe(":3000", nil))
}

extractor

This property allows you to get token value from any place, by default the token will be extracted from the Authorization header on the incominng request.

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/golang-jwt/jwt/v4"
	mdw "github.com/mfuentesg/go-jwtmiddleware"
)

func errorHandler(w http.ResponseWriter, r *http.Request, err error) {
	w.WriteHeader(http.StatusUnauthorized)
	w.Write([]byte(fmt.Sprintf("unauthorized : %v", err)))
}

func main() {
	m := mdw.New()
	http.Handle("/", m.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(fmt.Sprintf("hello world %s!", token["name"])))
	})))
	fmt.Println("listening on port :3000")
	log.Fatal(http.ListenAndServe(":3000", nil))
}
$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o" localhost:3000

hello world John Doe!

signingMethod

This property indicates the used algorithm to encrypt the token, i.e. jwt.SigningMethodES256.

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/golang-jwt/jwt/v4"
	mdw "github.com/mfuentesg/go-jwtmiddleware"
)

func main() {
	m := mdw.New(mdw.WithSigningMethod())
	http.Handle("/", m.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(fmt.Sprintf("hello world %s!", token["name"])))
	})))
	fmt.Println("listening on port :3000")
	log.Fatal(http.ListenAndServe(":3000", nil))
}

signKey

It used to set the secret key to encrypt/decrypt the token.

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/golang-jwt/jwt/v4"
	mdw "github.com/mfuentesg/go-jwtmiddleware"
)

func main() {
	m := mdw.New(mdw.WithSignKey([]byte("my secret key")))
	http.Handle("/", m.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(fmt.Sprintf("hello world %s!", token["name"])))
	})))
	fmt.Println("listening on port :3000")
	log.Fatal(http.ListenAndServe(":3000", nil))
}
# token created with the secret `secret`
$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o" localhost:3000

unauthorized

userProperty

It defines the name of the property in the request where the token will be stored.

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/golang-jwt/jwt/v4"
	mdw "github.com/mfuentesg/go-jwtmiddleware"
)

type userKey string

func errorHandler(w http.ResponseWriter, r *http.Request, err error) {
	w.WriteHeader(http.StatusUnauthorized)
	w.Write([]byte(fmt.Sprintf("unauthorized : %v", err)))
}

func main() {
	m := mdw.New(mdw.WithSignKey([]byte("secret")), mdw.WithUserProperty(userKey("user")))
	http.Handle("/", m.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := r.Context().Value(userKey("user")).(*jwt.Token).Claims.(jwt.MapClaims)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(fmt.Sprintf("hello world %s!", token["name"])))
	})))
	fmt.Println("listening on port :3000")
	log.Fatal(http.ListenAndServe(":3000", nil))
}
$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o" localhost:3000

hello world John Doe!