jwtmiddleware

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Dec 13, 2019 License: MIT Imports: 7 Imported by: 0

README

JWT Middleware Go Report Card Go Coverage

This package allows you to authenticate HTTP requests using JWT tokens in your Golang applications. JWTs are typically used to protect API endpoints, and are often issued using OpenID Connect.

This fork was created due to a lack of activity from the original project owner and a lack of applicable tests for all possible use cases.

Key Features

  • Ability to check the Authorization header, URL parameters & cookies for a JWT
  • Decode, parse & validate the JWT and set the content of it to the request context
  • Verify signing method is not nil and valid
  • Ability to parse JWTs with custom claims

Installing

go get github.com/ciehanski/go-jwt-middleware

Using it

net/http
negroni
martini
gin

Options

type Options struct {
  // The function that will return the Key to validate the JWT.
  // It can be either a shared secret or a public key.
  // Default value: nil
  ValidationKeyGetter jwt.Keyfunc
  // A boolean to ignore expiration of the JWT
  IgnoreExpiration bool
  // The name of the property in the request where the user information
  // from the JWT will be stored.
  // Default value: "user"
  UserProperty string
  // The function that will be called when there's an error validating the token
  // Default value: https://github.com/auth0/go-jwt-middleware/blob/master/jwtmiddleware.go#L35
  ErrorHandler ErrorHandler
  // A boolean indicating if the credentials are required or not
  // Default value: false
  CredentialsOptional bool
  // A function that extracts the token from the request
  // Default: FromAuthHeader (i.e., from Authorization header as bearer token)
  Extractor TokenExtractor
  // Debug flag turns on debugging output
  // Default: false  
  Debug bool
  // When set, all requests with the OPTIONS method will use authentication
  // Default: false
  EnableAuthOnOptions bool,
  // When set, the middleware verifies that tokens are signed with the specific signing algorithm
  // If the signing method is not constant the ValidationKeyGetter callback can be used to implement additional checks
  // Important to avoid security issues described here: https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
  // Default: nil
  SigningMethod jwt.SigningMethod
  // A function that creates custom jwt.Claims object
  // that passes to jwt.ParseWithClaims
  // Default: nil
  CustomClaims customClaims
}

The below functions are simple enough to implement yourself, so I saw no reason them to be exported.

// customClaims is a function that returns custom jwt claims.
type customClaims func() jwt.Claims
// errorHandler is a handler function called whenever an error is encountered.
type errorHandler func(w http.ResponseWriter, r *http.Request, err error)

Token Extraction

The default value for the Extractor option is the FromAuthHeader function which assumes that the JWT will be provided as a bearer token in an Authorization header, e.g.,

Authorization: bearer {token}

To extract the token from a query string parameter, you can use the FromParameter function, e.g.,

jwtmiddleware.New(jwtmiddleware.Options{
  Extractor: jwtmiddleware.FromParameter("auth_code"),
})

In this case, the FromParameter function will look for a JWT in the auth_code query parameter of the URL.

Or, if you want to allow multiple methods of token extraction, you can use the FromFirst function to try and extract the token first in one way and then in one or more other ways, e.g.,

jwtmiddleware.New(jwtmiddleware.Options{
  Extractor: jwtmiddleware.FromFirst(jwtmiddleware.FromAuthHeader,
                                     jwtmiddleware.FromParameter("auth_code")),
})

Lastly, you can also extract the JWT from a specified cookie name:

jwtmiddleware.New(jwtmiddleware.Options{
  Extractor: jwtmiddleware.FromCookie("cookieName"),
})

Examples

net/http
package main

import ...

var myHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  user := context.Get(r, "user")
  fmt.Fprintf(w, "This is an authenticated request")
  fmt.Fprintf(w, "Claim content:\n")
  for k, v := range user.(*jwt.Token).Claims.(jwt.MapClaims) {
    fmt.Fprintf(w, "%s :\t%#v\n", k, v)
  }
})

func main() {
  jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
    ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
      return []byte("dont-hack-me"), nil
    },
    // When set, the middleware verifies that tokens are signed with the specific signing algorithm
    // If the signing method is not constant the ValidationKeyGetter callback can be used to implement additional checks
    // Important to avoid security issues described here: https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
    SigningMethod: jwt.SigningMethodHS256,
  })

  app := jwtMiddleware.Handler(myHandler)
  http.ListenAndServe("0.0.0.0:3000", app)
}
Negroni
package main

import ...

func main() {
	StartServer()
}

func StartServer() {
	r := mux.NewRouter()

	jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
		ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
			return []byte("My Secret"), nil
		},
		SigningMethod: jwt.SigningMethodHS256,
	})

	r.HandleFunc("/ping", PingHandler)
	r.Handle("/secured/ping", negroni.New(
		negroni.HandlerFunc(jwtMiddleware.HandlerWithNext),
		negroni.Wrap(http.HandlerFunc(SecuredPingHandler)),
	))
	http.Handle("/", r)
	http.ListenAndServe(":3001", nil)
}

type Response struct {
	Text string `json:"text"`
}

func respondJson(text string, w http.ResponseWriter) {
	response := Response{text}

	jsonResponse, err := json.Marshal(response)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(jsonResponse)
}

func PingHandler(w http.ResponseWriter, r *http.Request) {
	respondJson("All good. You don't need to be authenticated to call this", w)
}

func SecuredPingHandler(w http.ResponseWriter, r *http.Request) {
	respondJson("All good. You only get this message if you're authenticated", w)
}
Martini
package main

import ...

func main() {
	StartServer()
}

func StartServer() {
	m := martini.Classic()

	jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
		ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
			return []byte("My Secret"), nil
		},
		SigningMethod: jwt.SigningMethodHS256,
	})

	m.Get("/ping", PingHandler)
	m.Get("/secured/ping", jwtMiddleware.CheckJWT, SecuredPingHandler)

	m.Run()
}

type Response struct {
	Text string `json:"text"`
}

func respondJson(text string, w http.ResponseWriter) {
	response := Response{text}

	jsonResponse, err := json.Marshal(response)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(jsonResponse)
}

func PingHandler(w http.ResponseWriter, r *http.Request) {
	respondJson("All good. You don't need to be authenticated to call this", w)
}

func SecuredPingHandler(w http.ResponseWriter, r *http.Request) {
	respondJson("All good. You only get this message if you're authenticated", w)
}
Gin
package main

import ...

func main() {
	startServer()
}

var jwtMiddleware = jwtmiddleware.New(jwtmiddleware.Options{
	ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
		return []byte("your auth0 client secret here"), nil
	},
	SigningMethod: jwt.SigningMethodHS256,
})

func checkJWT() gin.HandlerFunc {
	return func(c *gin.Context) {
		jwtMid := *jwtMiddleware
		if err := jwtMid.CheckJWT(c.Writer, c.Request); err != nil {
			c.AbortWithStatus(401)
		}
	}
}

func startServer() {
	r := gin.Default()

	r.GET("/ping", func(g *gin.Context) {
		g.JSON(200, gin.H{"text": "Hello from public"})
	})

	r.GET("/secured/ping", checkJWT(), func(g *gin.Context) {
		g.JSON(200, gin.H{"text": "Hello from private"})
	})

	r.Run(":3002")
}

Issue Reporting

If you have found a bug or if you have a feature request, please report them at this repository's issue section. Please do not report security vulnerabilities on the public GitHub issue tracker.

Original Author

Auth0's original project can be found here

License

This project is licensed under the MIT license. See the LICENSE file for more info.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FromAuthHeader

func FromAuthHeader(r *http.Request) (string, error)

FromAuthHeader is a "TokenExtractor" that takes a give request and extracts the JWT token from the Authorization header.

Types

type JWTMiddleware

type JWTMiddleware struct {
	Options Options
}

JWTMiddleware stores the user's preferred configuration.

func New

func New(options ...Options) *JWTMiddleware

New constructs a new Secure instance with supplied options.

func (*JWTMiddleware) CheckJWT

func (m *JWTMiddleware) CheckJWT(w http.ResponseWriter, r *http.Request) error

CheckJWT is the function that validates and parses the incoming JWT. Embeds the valid JWT into the context of the request.

func (*JWTMiddleware) Handler

func (m *JWTMiddleware) Handler(h http.Handler) http.Handler

Handler is a basic handler implementing JWT parsing.

func (*JWTMiddleware) HandlerWithNext

func (m *JWTMiddleware) HandlerWithNext(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)

HandlerWithNext is a special implementation for Negroni, but could be used elsewhere.

type Options

type Options struct {
	// The function that will return the Key to validate the JWT.
	// It can be either a shared secret or a public key.
	// Default value: nil
	ValidationKeyGetter jwt.Keyfunc
	// The name of the property in the request where the user information
	// from the JWT will be stored.
	// Default value: "user"
	UserProperty string
	// The function that will be called when there's an error validating the token
	// Default value: errorHandler
	ErrorHandler errorHandler
	// A boolean indicating if the credentials are required or not
	// Default value: false
	CredentialsOptional bool
	// A function that extracts the token from the request
	// Default: FromAuthHeader (i.e., from Authorization header as bearer token)
	Extractor TokenExtractor
	// Debug flag turns on debugging output
	// Default: false
	Debug bool
	// When set, all requests with the OPTIONS method will use authentication
	// Default: false
	EnableAuthOnOptions bool
	// When set, the middleware verifies that tokens are signed with the specific signing algorithm
	// If the signing method is not constant the ValidationKeyGetter callback can be used to implement additional checks
	// Important to avoid security issues described here:
	// https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
	// Default: nil
	SigningMethod jwt.SigningMethod
	// A function that creates custom Claims objects
	// and passing to jwt.ParseWithClaims
	// Default: nil
	CustomClaims customClaims
}

Options is a struct for specifying configuration options for the middleware.

type TokenExtractor

type TokenExtractor func(r *http.Request) (string, error)

TokenExtractor is a function that takes a request as input and returns either a token or an error. An error should only be returned if an attempt to specify a token was found, but the information was somehow incorrectly formed. In the case where a token is simply not present, this should not be treated as an error. An empty string should be returned in that case.

func FromCookie

func FromCookie(cookieName string) TokenExtractor

FromCookie returns a function that extracts the token from the specified key in the HTTP cookie, like "access_token"

func FromFirst

func FromFirst(extractors ...TokenExtractor) TokenExtractor

FromFirst returns a function that runs multiple token extractors and takes the first token it finds

func FromParameter

func FromParameter(param string) TokenExtractor

FromParameter returns a function that extracts the token from the specified query string parameter

Jump to

Keyboard shortcuts

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