update dependencies

This commit is contained in:
dhax 2017-10-21 18:30:08 +02:00
parent fce1b99683
commit 397e9c0842
164 changed files with 5207 additions and 2213 deletions

View file

@ -1,2 +1,3 @@
vendor/
Gopkg.lock
.idea/

View file

@ -1,6 +1,6 @@
[[constraint]]
name = "github.com/dgrijalva/jwt-go"
version = "^3.0.0"
version = "^3.1.0"
[[constraint]]
name = "github.com/go-chi/chi"
version = "^3.0.0"

View file

@ -1,5 +1,6 @@
jwtauth - JWT authentication middleware for Go HTTP services
============================================================
[![GoDoc Widget]][GoDoc]
The `jwtauth` http middleware package provides a simple way to verify a JWT token
from a http request and send the result down the request context (`context.Context`).
@ -22,12 +23,11 @@ plain-text payload for all unverified tokens and passes the good ones through. Y
also copy the Authenticator and customize it to handle invalid tokens to better fit
your flow (ie. with a JSON error response body).
The `Verifier` will search for a JWT token in a http request, in the order:
By default, the `Verifier` will search for a JWT token in a http request, in the order:
1. 'jwt' URI query parameter
2. 'Authorization: BEARER T' request header
3. Cookie 'jwt' value
4. (optional), use `jwtauth.Verify("state")` for additional query/cookie parameter aliases
3. 'jwt' Cookie value
The first JWT string that is found as a query parameter, authorization header
or cookie header is then decoded by the `jwt-go` library and a *jwt.Token
@ -39,6 +39,11 @@ be the generic `jwtauth.Authenticator` middleware or your own custom handler
which checks the request context jwt token and error to prepare a custom
http response.
Note: jwtauth supports custom verification sequences for finding a token
from a request by using the `Verify` middleware instantiator directly. The default
`Verifier` is instantiated by calling `Verify(ja, TokenFromQuery, TokenFromHeader, TokenFromCookie)`.
# Usage
See the full [example](https://github.com/go-chi/jwtauth/blob/master/_example/main.go).
@ -105,3 +110,6 @@ func router() http.Handler {
# LICENSE
[MIT](/LICENSE)
[GoDoc]: https://godoc.org/github.com/go-chi/jwtauth
[GoDoc Widget]: https://godoc.org/github.com/go-chi/jwtauth?status.svg

View file

@ -21,16 +21,44 @@ var (
ErrExpired = errors.New("jwtauth: token is expired")
)
var (
// TokenFromCookie tries to retreive the token string from a cookie named
// "jwt".
TokenFromCookie = func(r *http.Request) string {
cookie, err := r.Cookie("jwt")
if err != nil {
return ""
}
return cookie.Value
}
// TokenFromHeader tries to retreive the token string from the
// "Authorization" reqeust header: "Authorization: BEARER T".
TokenFromHeader = func(r *http.Request) string {
// Get token from authorization header.
bearer := r.Header.Get("Authorization")
if len(bearer) > 7 && strings.ToUpper(bearer[0:6]) == "BEARER" {
return bearer[7:]
}
return ""
}
// TokenFromQuery tries to retreive the token string from the "jwt" URI
// query parameter.
TokenFromQuery = func(r *http.Request) string {
// Get token from query param named "jwt".
return r.URL.Query().Get("jwt")
}
)
type JwtAuth struct {
signKey []byte
verifyKey []byte
signKey interface{}
verifyKey interface{}
signer jwt.SigningMethod
parser *jwt.Parser
}
// New creates a JwtAuth authenticator instance that provides middleware handlers
// and encoding/decoding functions for JWT signing.
func New(alg string, signKey []byte, verifyKey []byte) *JwtAuth {
func New(alg string, signKey interface{}, verifyKey interface{}) *JwtAuth {
return NewWithParser(alg, &jwt.Parser{}, signKey, verifyKey)
}
@ -40,7 +68,7 @@ func New(alg string, signKey []byte, verifyKey []byte) *JwtAuth {
// We explicitly toggle `SkipClaimsValidation` in the `jwt-go` parser so that
// we can control when the claims are validated - in our case, by the Verifier
// http middleware handler.
func NewWithParser(alg string, parser *jwt.Parser, signKey []byte, verifyKey []byte) *JwtAuth {
func NewWithParser(alg string, parser *jwt.Parser, signKey interface{}, verifyKey interface{}) *JwtAuth {
parser.SkipClaimsValidation = true
return &JwtAuth{
signKey: signKey,
@ -68,15 +96,15 @@ func NewWithParser(alg string, parser *jwt.Parser, signKey []byte, verifyKey []b
// http response.
func Verifier(ja *JwtAuth) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return Verify(ja, "")(next)
return Verify(ja, TokenFromQuery, TokenFromHeader, TokenFromCookie)(next)
}
}
func Verify(ja *JwtAuth, paramAliases ...string) func(http.Handler) http.Handler {
func Verify(ja *JwtAuth, findTokenFns ...func(r *http.Request) string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
hfn := func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
token, err := VerifyRequest(ja, r, paramAliases...)
token, err := VerifyRequest(ja, r, findTokenFns...)
ctx = NewContext(ctx, token, err)
next.ServeHTTP(w, r.WithContext(ctx))
}
@ -84,37 +112,17 @@ func Verify(ja *JwtAuth, paramAliases ...string) func(http.Handler) http.Handler
}
}
func VerifyRequest(ja *JwtAuth, r *http.Request, paramAliases ...string) (*jwt.Token, error) {
func VerifyRequest(ja *JwtAuth, r *http.Request, findTokenFns ...func(r *http.Request) string) (*jwt.Token, error) {
var tokenStr string
var err error
// Get token from query params
tokenStr = r.URL.Query().Get("jwt")
// Get token from other param aliases
if tokenStr == "" && paramAliases != nil && len(paramAliases) > 0 {
for _, p := range paramAliases {
tokenStr = r.URL.Query().Get(p)
if tokenStr != "" {
break
}
}
}
// Get token from authorization header
if tokenStr == "" {
bearer := r.Header.Get("Authorization")
if len(bearer) > 7 && strings.ToUpper(bearer[0:6]) == "BEARER" {
tokenStr = bearer[7:]
}
}
// Get token from cookie
if tokenStr == "" {
// TODO: paramAliases should apply to cookies too..
cookie, err := r.Cookie("jwt")
if err == nil {
tokenStr = cookie.Value
// Extract token string from the request by calling token find functions in
// the order they where provided. Further extraction stops if a function
// returns a non-empty string.
for _, fn := range findTokenFns {
tokenStr = fn(r)
if tokenStr != "" {
break
}
}
@ -127,24 +135,17 @@ func VerifyRequest(ja *JwtAuth, r *http.Request, paramAliases ...string) (*jwt.T
case "token is expired":
err = ErrExpired
}
// ctx = NewContext(ctx, token, err)
// next.ServeHTTP(w, r.WithContext(ctx))
return token, err
}
if token == nil || !token.Valid || token.Method != ja.signer {
err = ErrUnauthorized
// ctx = NewContext(ctx, token, err)
// next.ServeHTTP(w, r.WithContext(ctx))
return token, err
}
// Check expiry via "exp" claim
if IsExpired(token) {
err = ErrExpired
// ctx = NewContext(ctx, token, err)
// next.ServeHTTP(w, r.WithContext(ctx))
return token, err
}
@ -173,7 +174,7 @@ func (ja *JwtAuth) Decode(tokenString string) (t *jwt.Token, err error) {
}
func (ja *JwtAuth) keyFunc(t *jwt.Token) (interface{}, error) {
if ja.verifyKey != nil && len(ja.verifyKey) > 0 {
if ja.verifyKey != nil {
return ja.verifyKey, nil
} else {
return ja.signKey, nil

View file

@ -1,12 +1,15 @@
package jwtauth_test
import (
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"time"
@ -16,22 +19,83 @@ import (
)
var (
TokenAuth *jwtauth.JwtAuth
TokenSecret = []byte("secretpass")
TokenAuthHS256 *jwtauth.JwtAuth
TokenSecret = []byte("secretpass")
TokenAuthRS256 *jwtauth.JwtAuth
PrivateKeyRS256String = `-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBALxo3PCjFw4QjgOX06QCJIJBnXXNiEYwDLxxa5/7QyH6y77nCRQy
J3x3UwF9rUD0RCsp4sNdX5kOQ9PUyHyOtCUCAwEAAQJARjFLHtuj2zmPrwcBcjja
IS0Q3LKV8pA0LoCS+CdD+4QwCxeKFq0yEMZtMvcQOfqo9x9oAywFClMSlLRyl7ng
gQIhAOyerGbcdQxxwjwGpLS61Mprf4n2HzjwISg20cEEH1tfAiEAy9dXmgQpDPir
C6Q9QdLXpNgSB+o5CDqfor7TTyTCovsCIQDNCfpu795luDYN+dvD2JoIBfrwu9v2
ZO72f/pm/YGGlQIgUdRXyW9kH13wJFNBeBwxD27iBiVj0cbe8NFUONBUBmMCIQCN
jVK4eujt1lm/m60TlEhaWBC3p+3aPT2TqFPUigJ3RQ==
-----END RSA PRIVATE KEY-----
`
PublicKeyRS256String = `-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALxo3PCjFw4QjgOX06QCJIJBnXXNiEYw
DLxxa5/7QyH6y77nCRQyJ3x3UwF9rUD0RCsp4sNdX5kOQ9PUyHyOtCUCAwEAAQ==
-----END PUBLIC KEY-----
`
)
func init() {
TokenAuth = jwtauth.New("HS256", TokenSecret, nil)
TokenAuthHS256 = jwtauth.New("HS256", TokenSecret, nil)
}
//
// Tests
//
func TestSimpleRSA(t *testing.T) {
privateKeyBlock, _ := pem.Decode([]byte(PrivateKeyRS256String))
privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
if err != nil {
t.Fatalf(err.Error())
}
publicKeyBlock, _ := pem.Decode([]byte(PublicKeyRS256String))
publicKey, err := x509.ParsePKIXPublicKey(publicKeyBlock.Bytes)
if err != nil {
t.Fatalf(err.Error())
}
TokenAuthRS256 = jwtauth.New("RS256", privateKey, publicKey)
claims := jwtauth.Claims{
"key": "val",
"key2": "val2",
"key3": "val3",
}
_, tokenString, err := TokenAuthRS256.Encode(claims)
if err != nil {
t.Fatalf("Failed to encode claims %s\n", err.Error())
}
token, err := TokenAuthRS256.Decode(tokenString)
if err != nil {
t.Fatalf("Failed to decode token string %s\n", err.Error())
}
if !reflect.DeepEqual(claims, jwtauth.Claims(token.Claims.(jwt.MapClaims))) {
t.Fatalf("The decoded claims don't match the original ones\n")
}
}
func TestSimple(t *testing.T) {
r := chi.NewRouter()
r.Use(jwtauth.Verifier(TokenAuth), jwtauth.Authenticator)
r.Use(jwtauth.Verifier(TokenAuthHS256), jwtauth.Authenticator)
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome"))
@ -76,7 +140,7 @@ func TestMore(t *testing.T) {
// Protected routes
r.Group(func(r chi.Router) {
r.Use(jwtauth.Verifier(TokenAuth))
r.Use(jwtauth.Verifier(TokenAuthHS256))
authenticator := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {