diff --git a/node/jwt_handler.go b/node/jwt_handler.go index 4f85c12367..7818eb9063 100644 --- a/node/jwt_handler.go +++ b/node/jwt_handler.go @@ -47,8 +47,10 @@ func (handler *jwtHandler) ServeHTTP(out http.ResponseWriter, r *http.Request) { strToken string claims jwt.RegisteredClaims ) + // RFC 7235: authentication schemes are case-insensitive. Match with EqualFold + // and slice off the scheme prefix (do not use TrimPrefix, which is case-sensitive). if auth := r.Header.Get("Authorization"); len(auth) >= 7 && strings.EqualFold(auth[:7], "bearer ") { - strToken = strings.TrimPrefix(auth, "Bearer ") + strToken = auth[7:] } if len(strToken) == 0 { http.Error(out, "missing token", http.StatusUnauthorized) diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go index f5668abb08..2f25a75769 100644 --- a/node/rpcstack_test.go +++ b/node/rpcstack_test.go @@ -371,6 +371,16 @@ func TestJWT(t *testing.T) { "bar": "baz", })) }, + // RFC 7235: authentication scheme is case-insensitive. + func() string { + return fmt.Sprintf("bearer %v", issueToken(secret, nil, testClaim{"iat": time.Now().Unix()})) + }, + func() string { + return fmt.Sprintf("BEARER %v", issueToken(secret, nil, testClaim{"iat": time.Now().Unix()})) + }, + func() string { + return fmt.Sprintf("bEaReR %v", issueToken(secret, nil, testClaim{"iat": time.Now().Unix()})) + }, } for i, tokenFn := range expOk { token := tokenFn() @@ -421,9 +431,6 @@ func TestJWT(t *testing.T) { func() string { return fmt.Sprintf("Bearer %v", issueToken(secret, nil, testClaim{"iat": time.Now().Unix()})) }, - func() string { - return fmt.Sprintf("bearer %v", issueToken(secret, nil, testClaim{"iat": time.Now().Unix()})) - }, func() string { return fmt.Sprintf("Bearer: %v", issueToken(secret, nil, testClaim{"iat": time.Now().Unix()})) },