Skip to content

Commit

Permalink
feat(auth): add jwt support in auth middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
GeorgeMac committed Sep 23, 2019
1 parent ddce5d3 commit f073c65
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### Features

1. [15151](https://github.com/influxdata/influxdb/pull/15151): Add jsonweb package for future JWT support
1. [15152](https://github.com/influxdata/influxdb/pull/15152): Add JWT support to http auth middleware

### UI Improvements

Expand Down
12 changes: 5 additions & 7 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ import (
// AuthorizationKind is returned by (*Authorization).Kind().
const AuthorizationKind = "authorization"

var (
// ErrUnableToCreateToken sanitized error message for all errors when a user cannot create a token
ErrUnableToCreateToken = &Error{
Msg: "unable to create token",
Code: EInvalid,
}
)
// ErrUnableToCreateToken sanitized error message for all errors when a user cannot create a token
var ErrUnableToCreateToken = &Error{
Msg: "unable to create token",
Code: EInvalid,
}

// Authorization is an authorization. 🎉
type Authorization struct {
Expand Down
16 changes: 16 additions & 0 deletions http/authentication_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

platform "github.com/influxdata/influxdb"
platcontext "github.com/influxdata/influxdb/context"
"github.com/influxdata/influxdb/jsonweb"
"github.com/julienschmidt/httprouter"
"go.uber.org/zap"
)
Expand All @@ -19,6 +20,7 @@ type AuthenticationHandler struct {

AuthorizationService platform.AuthorizationService
SessionService platform.SessionService
TokenParser *jsonweb.TokenParser
SessionRenewDisabled bool

// This is only really used for it's lookup method the specific http
Expand All @@ -34,6 +36,7 @@ func NewAuthenticationHandler(h platform.HTTPErrorHandler) *AuthenticationHandle
Logger: zap.NewNop(),
HTTPErrorHandler: h,
Handler: http.DefaultServeMux,
TokenParser: jsonweb.NewTokenParser(jsonweb.EmptyKeyStore),
noAuthRouter: httprouter.New(),
}
}
Expand Down Expand Up @@ -107,6 +110,19 @@ func (h *AuthenticationHandler) extractAuthorization(ctx context.Context, r *htt
return ctx, err
}

token, err := h.TokenParser.Parse(t)
if err == nil {
return platcontext.SetAuthorizer(ctx, token), nil
}

// if the error returned signifies ths token is
// not a well formed JWT then use it as a lookup
// key for its associated authorization
// otherwise return the error
if !jsonweb.IsMalformedError(err) {
return ctx, err
}

a, err := h.AuthorizationService.FindAuthorizationByToken(ctx, t)
if err != nil {
return ctx, err
Expand Down
47 changes: 47 additions & 0 deletions http/authentication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ import (

platform "github.com/influxdata/influxdb"
platformhttp "github.com/influxdata/influxdb/http"
"github.com/influxdata/influxdb/jsonweb"
"github.com/influxdata/influxdb/mock"
)

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjbG91ZDIuaW5mbHV4ZGF0YS5jb20iLCJhdWQiOiJnYXRld2F5LmluZmx1eGRhdGEuY29tIiwiaWF0IjoxNTY4NjI4OTgwLCJraWQiOiJzb21lLWtleSIsInBlcm1pc3Npb25zIjpbeyJhY3Rpb24iOiJ3cml0ZSIsInJlc291cmNlIjp7InR5cGUiOiJidWNrZXRzIiwiaWQiOiIwMDAwMDAwMDAwMDAwMDAxIiwib3JnSUQiOiIwMDAwMDAwMDAwMDAwMDAyIn19XX0.74vjbExiOd702VSIMmQWaDT_GFvUI0-_P-SfQ_OOHB0"

func TestAuthenticationHandler(t *testing.T) {
type fields struct {
AuthorizationService platform.AuthorizationService
SessionService platform.SessionService
TokenParser *jsonweb.TokenParser
}
type args struct {
token string
Expand Down Expand Up @@ -114,6 +118,46 @@ func TestAuthenticationHandler(t *testing.T) {
code: http.StatusUnauthorized,
},
},
{
name: "jwt provided",
fields: fields{
AuthorizationService: &mock.AuthorizationService{
FindAuthorizationByTokenFn: func(ctx context.Context, token string) (*platform.Authorization, error) {
return nil, fmt.Errorf("authorization not found")
},
},
SessionService: mock.NewSessionService(),
TokenParser: jsonweb.NewTokenParser(jsonweb.KeyStoreFunc(func(string) ([]byte, error) {
return []byte("correct-key"), nil
})),
},
args: args{
token: token,
},
wants: wants{
code: http.StatusOK,
},
},
{
name: "jwt provided - bad signature",
fields: fields{
AuthorizationService: &mock.AuthorizationService{
FindAuthorizationByTokenFn: func(ctx context.Context, token string) (*platform.Authorization, error) {
panic("token lookup attempted")
},
},
SessionService: mock.NewSessionService(),
TokenParser: jsonweb.NewTokenParser(jsonweb.KeyStoreFunc(func(string) ([]byte, error) {
return []byte("incorrect-key"), nil
})),
},
args: args{
token: token,
},
wants: wants{
code: http.StatusUnauthorized,
},
},
}

for _, tt := range tests {
Expand All @@ -125,6 +169,9 @@ func TestAuthenticationHandler(t *testing.T) {
h := platformhttp.NewAuthenticationHandler(platformhttp.ErrorHandler(0))
h.AuthorizationService = tt.fields.AuthorizationService
h.SessionService = tt.fields.SessionService
if tt.fields.TokenParser != nil {
h.TokenParser = tt.fields.TokenParser
}
h.Handler = handler

w := httptest.NewRecorder()
Expand Down
27 changes: 19 additions & 8 deletions jsonweb/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ import (

const kind = "jwt"

// ErrKeyNotFound should be returned by a KeyStore when
// a key cannot be located for the provided key ID
var ErrKeyNotFound = errors.New("key not found")

// ensure Token implements Authorizer
var _ influxdb.Authorizer = (*Token)(nil)
var (
// ErrKeyNotFound should be returned by a KeyStore when
// a key cannot be located for the provided key ID
ErrKeyNotFound = errors.New("key not found")

// EmptyKeyStore is a KeyStore implementation which contains no keys
EmptyKeyStore = KeyStoreFunc(func(string) ([]byte, error) {
return nil, ErrKeyNotFound
})
)

// KeyStore is a type which holds a set of keys accessed
// via an id
Expand All @@ -36,8 +40,8 @@ type TokenParser struct {

// NewTokenParser returns a configured token parser used to
// parse Token types from strings
func NewTokenParser(keyStore KeyStore) TokenParser {
return TokenParser{
func NewTokenParser(keyStore KeyStore) *TokenParser {
return &TokenParser{
keyStore: keyStore,
parser: &jwt.Parser{
ValidMethods: []string{jwt.SigningMethodHS256.Alg()},
Expand Down Expand Up @@ -70,6 +74,13 @@ func (t *TokenParser) Parse(v string) (*Token, error) {
return token, nil
}

// IsMalformedError returns true if the error returned represents
// a jwt malformed token error
func IsMalformedError(err error) bool {
verr, ok := err.(*jwt.ValidationError)
return ok && verr.Errors&jwt.ValidationErrorMalformed > 0
}

// Token is a structure which is serialized as a json web token
// It contains the necessary claims required to authorize
type Token struct {
Expand Down

0 comments on commit f073c65

Please sign in to comment.