Giter Site home page Giter Site logo

go-jwt-middleware's Introduction

Go JWT Middleware

GoDoc Go Report Card License Release Codecov Tests

๐Ÿ“š Documentation โ€ข ๐Ÿš€ Getting Started โ€ข ๐Ÿ’ฌ Feedback

Documentation

  • Godoc - explore the go-jwt-middleware documentation.
  • Docs site โ€” explore our docs site and learn more about Auth0.
  • Quickstart - our guide for adding go-jwt-middleware to your app.

Getting started

Requirements

This library follows the same support policy as Go. The last two major Go releases are actively supported and compatibility issues will be fixed. While you may find that older versions of Go may work, we will not actively test and fix compatibility issues with these versions.

  • Go 1.19+

Installation

go get github.com/auth0/go-jwt-middleware/v2

Usage

package main

import (
	"context"
	"encoding/json"
	"log"
	"net/http"

	"github.com/auth0/go-jwt-middleware/v2"
	"github.com/auth0/go-jwt-middleware/v2/validator"
	jwtmiddleware "github.com/auth0/go-jwt-middleware/v2"
)

var handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	claims, ok := r.Context().Value(jwtmiddleware.ContextKey{}).(*validator.ValidatedClaims)
	if !ok {
		http.Error(w, "failed to get validated claims", http.StatusInternalServerError)
		return
	}
	
	payload, err := json.Marshal(claims)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

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

func main() {
	keyFunc := func(ctx context.Context) (interface{}, error) {
		// Our token must be signed using this data.
		return []byte("secret"), nil
	}

	// Set up the validator.
	jwtValidator, err := validator.New(
		keyFunc,
		validator.HS256,
		"https://<issuer-url>/",
		[]string{"<audience>"},
	)
	if err != nil {
		log.Fatalf("failed to set up the validator: %v", err)
	}

	// Set up the middleware.
	middleware := jwtmiddleware.New(jwtValidator.ValidateToken)

	http.ListenAndServe("0.0.0.0:3000", middleware.CheckJWT(handler))
}

After running that code (go run main.go) you can then curl the http server from another terminal:

$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJpc3MiOiJnby1qd3QtbWlkZGxld2FyZS1leGFtcGxlIiwiYXVkIjoiZ28tand0LW1pZGRsZXdhcmUtZXhhbXBsZSJ9.xcnkyPYu_b3qm2yeYuEgr5R5M5t4pN9s04U1ya53-KM" localhost:3000

That should give you the following response:

{
  "CustomClaims": null,
  "RegisteredClaims": {
    "iss": "go-jwt-middleware-example",
    "aud": "go-jwt-middleware-example",
    "sub": "1234567890",
    "iat": 1516239022
  }
}

The JWT included in the Authorization header above is signed with secret.

To test how the response would look like with an invalid token:

$ curl -v -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.yiDw9IDNCa1WXCoDfPR_g356vSsHBEerqh9IvnD49QE" localhost:3000

That should give you the following response:

...
< HTTP/1.1 401 Unauthorized
< Content-Type: application/json
{"message":"JWT is invalid."}
...

For more examples please check the examples folder.

Feedback

Contributing

We appreciate feedback and contribution to this repo! Before you get started, please see the following:

Raise an issue

To provide feedback or report a bug, please raise an issue on our issue tracker.

Vulnerability Reporting

Please do not report security vulnerabilities on the public Github issue tracker. The Responsible Disclosure Program details the procedure for disclosing security issues.


Auth0 Logo

Auth0 is an easy to implement, adaptable authentication and authorization platform.
To learn more checkout Why Auth0?

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

go-jwt-middleware's People

Contributors

aerostitch avatar azazi-sa avatar blogscot avatar bt-nia avatar chenkie avatar d-kuro avatar d10i avatar dependabot[bot] avatar dschenkelman avatar evansims avatar ewanharris avatar fiveohhh avatar grounded042 avatar ilius avatar javiercbk avatar jfatta avatar jonbonazza avatar liggitt avatar mehreencs87 avatar mehulgohil avatar mgonto avatar mtiller avatar ntotten avatar osamingo avatar oxisto avatar sebest avatar sergiught avatar sre-57-opslevel[bot] avatar trobert2 avatar widcket avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-jwt-middleware's Issues

v2 features

This issue is here to list out all of the features we're working towards in v2. We have the milestone and I wanted to create an issue to explicitly call out everything we are working towards. Things can be checked off once they are merged into the v2 branch.

core features

  • vendor neutral (#40)
  • replaceable validation logic (#73)
  • allow custom error handler (#51)
  • error handler interface should take error type and not just string (#52)
  • clone request instead of shallow copy (#62)
  • add FromCookie token extractor (#10 and #63)
  • look at context key (#64)
  • reorder fields for better alignment (#61)
  • do not exclude SA1029 from linting (Makefile and workflows - see #64)
  • look into providing a caching key provider #97
  • examples
  • use github.com/pkg/errors #98

validation features

  • support custom claims (#53)
  • support clock skew (#58)
  • add option for additional checks (#74)

before launch

  • update all documentation here (use go doc)
  • examples for popular http frameworks
  • update main Auth0 docs
  • update Auth0 quickstarts
  • create migration guide #99
  • blog post about release

Has jwt-go changed since the README was published?

This code snippet no longer works:

for k, v := range user.(*jwt.Token).Claims { fmt.Fprintf(w, "%s :\t%#v\n", k, v) }

Claims cannot be ranged over, but I assume this was possible previously. I'm actually unsure how to access any of the claims at all from inside of the handler. When I log Claims, it's logged as a map. When I try to work with Claims as one would work with a map, I just get a litany of errors.

context.Set() but no context.delete()

Is there a special reason you call context.Set(r, m.Options.UserProperty, parsedToken) without calling the delete for that entry? This seems like a memory leak to me but I didn't get the chance to test it yet.

Test handler that requires authentication

I wanted to write tests for my handlers, but had the issue of my handlers requiring the auth0 decoded access token in the context. Some searching further and I managed to do the following to add the user id (subject field) to the context.

Maybe someone can help how one would add the other fields and custom claims. I have not yet been successful in adding custom claims to the context.

func TestHandler(t *testing.T) {

	// Create a request to pass to our handler. We don't have any query parameters for now, so we'll
	// pass 'nil' as the third parameter.
	req, err := http.NewRequest("GET", "/path", nil)
	if err != nil {
		t.Fatal(err)
	}

	// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(MyHandlerFunction)

	// Populate the request's context with our test data.
	// ie. add the Auth0 claims
	ctx := req.Context()
	registeredClaims := validator.RegisteredClaims{Subject: "auth0|1158"}

	ctx = context.WithValue(ctx, jwtmiddleware.ContextKey{},
		&validator.ValidatedClaims{
			RegisteredClaims: registeredClaims,
			CustomClaims:     nil,
		},
	)

	// Add our context to the request: note that WithContext returns a copy of
	// the request, which we must assign.
	req = req.WithContext(ctx)

	// Our handlers satisfy http.Handler, so we can call their ServeHTTP method
	// directly and pass in our Request and ResponseRecorder.
	handler.ServeHTTP(rr, req)

	// Check the status code is what we expect.
	if status := rr.Code; status != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			status, http.StatusOK)
	}

	// Check the response body is what we expect.
	var responseJson interface{}
	err = json.Unmarshal(rr.Body.Bytes(), &responseJson)
	if err != nil {
		t.Fatalf(err.Error())
	}
	log.Println(utils.PrettyPrint(responseJson))
}

Clock skew can cause JWT parsing to fail

go-jwt-middleware uses a default jwt Parser instance to parse the JWT. The default behavior is to "validate" the JWT and reject it if this fails.

Currently "validate" only looks at issue & expire time and compares with now(). If the parsing machine's current time is earlier than the JWT issuer's time (in my case it was by 0.4 secs) the parse will fail because my "now" is before their "issue time" (which is considered invalid).

Current code:
parsedToken, err := jwt.Parse(token, m.Options.ValidationKeyGetter)

Example of bypassing this validation:
jwtParser := &jwt.Parser{SkipClaimsValidation:true,}
parsedToken, err := jwtParser.Parse(token, m.Options.ValidationKeyGetter)

How to expose this option to the go-jwt-middleware user I will leave "as an exercise for the reader".

Custom claims not decoded

Describe the problem

Decoded access token does not contain custom claims.

What was the expected behavior?

I want to see the custom claims that were added by the auth0 actions.

Reproduction

I have an access token that looks like this:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjdOU1ZEMHpoMnJmSG9JWjM3YjBqWiJ9.eyJodHRwczovL2F1dGguZWJ1Zy5jby56YS9lbWFpbCI6ImpwbWVpamVyc0Bob21lYnVnLmNvLnphIiwiaHR0cHM6Ly9hdXRoLmVidWcuY28uemEvZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXV0aC5lYnVnLmNvLnphLyIsInN1YiI6ImF1dGgwfDEyMTEiLCJhdWQiOiJodHRwczovL2FwaS5lYnVnLmNvLnphIiwiaWF0IjoxNjU0MDE0MDM2LCJleHAiOjE2NTQxMDA0MzYsImF6cCI6InFyZGJkeGllZGtTQVBtbHhVckw0OTJVSjR3WHRWajVBIiwic2NvcGUiOiJlbWFpbCIsImd0eSI6InBhc3N3b3JkIiwicGVybWlzc2lvbnMiOltdfQ.MvKzvEbmmZRgOOGvG35npCkS3FfDmEJt1dpc_uRey5MZLvuO_a2Z8L-Z7TizVBkWhIHWL8mxopzjI9PLx_VzeexL8XKt7mrg0eiabu6sLlky29pXGjfh1SDDMhV4MTWMc_G94riNs-LfSZ7sevZMOn2TyCGEcSwJf5uW-xbcBQLeHIDMIhm1vAqFvJj_qsE68KFO2O0g1JZbSjakRBUq_aL0CsSpOScKXKk9Bi19L0U_mjYeUxYD24sMyZ6wbOot5_OPgIV3ouBUEuLR8RA0itGj7n22flRdzTR6inAB-KJdQZ7reFcP7YrKzTyrKA5p3nb245sJhvGPGmYIaZSBvw

I call the golang API which prints out the json marshalled claims:

func TestUserAuthedRoute(w http.ResponseWriter, r *http.Request) {
	claims := r.Context().Value(jwtmiddleware.ContextKey{}).(*validator.ValidatedClaims)
	log.Println(claims.RegisteredClaims.Subject)

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

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

The result is:

{
    "CustomClaims": {
        "scope": "email"
    },
    "RegisteredClaims": {
        "iss": "https://auth.ebug.co.za/",
        "sub": "auth0|1211",
        "aud": [
            "https://api.ebug.co.za"
        ],
        "exp": 1654100251,
        "iat": 1654013851
    }
}

I am expecting to see the custom claims, like jwt.io shows when decoding this same access token:

{
  "https://auth.ebug.co.za/email": "[email protected]",
  "https://auth.ebug.co.za/email_verified": true,
  "iss": "https://auth.ebug.co.za/",
  "sub": "auth0|1211",
  "aud": "https://api.ebug.co.za",
  "iat": 1654014036,
  "exp": 1654100436,
  "azp": "qrdbdxiedkSAPmlxUrL492UJ4wXtVj5A",
  "scope": "email",
  "gty": "password",
  "permissions": []
}

Environment

  • Version of go-jwt-middleware used: github.com/auth0/go-jwt-middleware/v2 v2.0.1
  • Other modules/plugins/libraries that might be involved:

replace jwt-go

We need to determine a replacement for the jwt-go dependency. Currently github.com/dgrijalva/jwt-go is not actively maintained, and we switched to https://github.com/form3tech-oss/jwt-go. However, the later also seems to have a lack of maintenance which leads us to seek a replacement.

We need to choose a package. In this issue I'd like to track discussing a replacement, doing some PoC work, and the actual replacement work.

So far I've been looking at the following criteria for replacement:

  • actively maintained
  • idiomatic go
  • adheres to RFC 7519
  • support best practices in RFC 8725

references

Middleware function

Some applications support blacklisting of tokens, which is currently really ugly to do without a middleware function. The options struct should take an optional middleware function. That function then takes a parsed token and returns if it is valid. The function may also return an error if something failed, e. g. a database query.

Basically like the isRevoked callback in express-jwt

Readme example not working

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

This throws the error:

cannot use func literal (type func(*"bitbucket.org/woodyzapp/woodyplatform/vendor/github.com/dgrijalva/jwt-go".Token) (interface {}, error)) as type "github.com/dgrijalva/jwt-go".Keyfunc in field value

I am using Govendor

Can't verify signature

I have am using the Angular2 project from here, https://github.com/auth0/auth0-angular2

I've created a go service built using gin and have created a middleware to decode and verify the JWT passed from the Angular2 app to the go service. I can verify the JWT sent in the Auth header from angular to the go service using jwt.io and my auth0 client secret. However using this middleware I can't seem to verify the signature. Am I missing something fundamental?

func jwtMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        str := "Secret copied from Auth0 dashboard"

        // Testing base64 decode, also tried returning base64Text as []byte(base64Text) in ValidationKeyGetter
        base64Text := make([]byte, base64.StdEncoding.DecodedLen(len(str)))
        base64.StdEncoding.Decode(base64Text, []byte(str))
        fmt.Printf("base64: %s\n", base64Text)

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

        err := jwtMiddleware.CheckJWT(c.Writer, c.Request)

        if err != nil {
            c.AbortWithStatus(401)
        } else {
            c.Next()
        }
    }
}

The response is always, 'signature is invalid'

context user is missing

Since I upgrade from f3f7de3b9e394e3af3b88e1b9457f6f71d1ae0ac to latest commit, I encounter a problem that I can not get user from request.

It looks like user := context.Get(c.Request, "user") is nil.
Hence, when I want to do userIDClaim := user.(*jwt.Token).Claims.(jwt.MapClaims)["userID"] ,
it will cause the following error.

interface conversion: interface {} is nil, not *jwt.Token

Incompatible | breaking change on callback function signature in jwtmiddleware options

Hey, I have this error with the latest version of this library. It looks like the callback function signature does not match current ValidationKeyGetter option value.

I don't know how to solve this as I did not change my code and the code is broken. So it looks like a regression / breaking change where I do not have a clue how to solve as the readme example remains unchanged and looks not compatible with the current library version.

I don't know which version I was using before, however now I have updated this library and it is up to date on my workstation.

Is there something to do on my side ? Am I missing something ?

I work on linux (ubuntu) with go go version go1.16.3 linux/amd64

Thank you for any help :)

image

Allow middleware to be used in a gRPC environment

Checklist

Describe the problem you'd like to have solved

We're using Auth0 for user authentication and authorization of our product, and we're also using the auth0/go-jwt-middleware/v2 and auth0/go-jwt-middleware/v2/validator packages to facilitate the validation of access token origins and properties.

We're using go-kit for our API services, and thus would like to use jwtmiddleware.CheckJWT as an endpoint.Middleware in our endpoints, but are unable to because CheckJWT returns an http.Handler.

Describe the ideal solution

The existing CheckJWT handler could be forked in 2: one that can return a go-kit endpoint.Middleware, and one that returns an http.Handler; wherein the contents of CheckJWT could be abstracted and reused for both handlers. I'd imagine that TokenExtractor abilities would need to be expanded, wherein the token could be pulled from context, as in our case. Or, CheckJWT could call MultiTokenExtractor instead of AuthHeaderTokenExtractor. I may be missing some details here, but this is what I've been thinking.

Alternatives and current workarounds

Right now, we're avoiding using the jwtmiddleware.CheckJWT HTTP handler, because we're not able to use the handler in our endpoint layer. I have recreated the CheckJWT method in a middleware in our codebase that can be pull the token from the passed in context object, and validated manually using ValidateToken, and verifying the returned errors.

Additional context

I'd be happy to take a stab at a POC or initial PR for this, if a community contribution is encouraged here. I'm sure that'd grant me some points towards my quarterly Auth0 Ambassador goals. ๐Ÿ˜„ ๐Ÿ™Œ

Either way, happy to chat more!

v1.0.0 no longer can unmarshel a single audience from client credentials using CustomClaims

Description

I believe the upgrade to 1.0.0 and the change from github.com/dgrijalva/jwt-go to github.com/form3tech-oss/jwt-go broke using Auth0's Client Credentials. We currently use this to do integration tests. The types have changed for StandardClaims for the audience field, going from string to []string, which makes the Bearer token returned from /oauth/token endpoint not useable.

Reproduction

curl --request POST \
  --url https://<<your app>>.auth0.com/oauth/token \
  --header 'content-type: application/json' \
  --data '{"client_id":"<<YOUR ID>>","client_secret":"<<YOUR SECRET>>","audience":"https://<<your app>>.auth0.com/api/","grant_type":"client_credentials"}'

Passing that credential into the go-jwt-middleware library results in the error JWT Check failed. Error: Error parsing token: json: cannot unmarshal string into Go struct field CustomClaims.aud of type []string.

If I try to make the same call with multiple audiences or a single audience with an array "audience":["https://<>.auth0.com/api/"]) the curl call fails.

{"error":"invalid_request","error_description":"Invalid audience. Expected type 'string' but found type 'object'."}%

I also tried escaping ("audience":\["https://<<your app>>.auth0.com/api/"\]), but I get an invalid json error.

Environment

Please provide the following:

  • Version of this library used: 1.0.0
  • Version of the platform or framework used, if applicable: N/A
  • Other relevant versions (language, server software, OS, browser): N/A
  • Other modules/plugins/libraries that might be involved: github.com/form3tech-oss/jwt-go

Tests

This library really needs some tests. I would propose that one goal should be to add some.

Personally, I really like using goconvey for my testing. It works perfectly fine with the normal go test command line. But it also includes a nice web interface that continuously reruns tests.

Any objections to adding some tests using goconvey?

Context may be too specific?

In the current implementation, we have a dependencies on gorilla/context. Some framework do not use this package. It may make sense to remove the dependency to it no ?

ContextKey not declared by package jwtmiddleware

Describe the problem

ContextKey not declared by package jwtmiddleware

Screenshot 2021-12-01 at 11 51 23

What was the expected behavior?

it should works how it was discribed in documentation

Reproduction

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

I

Environment

require (
	github.com/auth0/go-jwt-middleware v1.0.1 // indirect
	github.com/aws/aws-sdk-go v1.38.0
	github.com/brianvoe/gofakeit/v6 v6.8.0
	github.com/gin-gonic/gin v1.7.4
	github.com/go-openapi/jsonreference v0.19.6 // indirect
	github.com/go-openapi/swag v0.19.15 // indirect
	github.com/go-resty/resty/v2 v2.6.0
	github.com/gofrs/uuid v4.0.0+incompatible
	github.com/google/go-querystring v1.1.0
	github.com/gotidy/fhir-client v0.2.3
	github.com/guregu/dynamo v1.11.0
	github.com/jarcoal/httpmock v1.0.8
	github.com/json-iterator/go v1.1.10 // indirect
	github.com/mailru/easyjson v0.7.7 // indirect
	github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2
	github.com/swaggo/gin-swagger v1.3.1
	github.com/swaggo/swag v1.7.3
	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673
	golang.org/x/net v0.0.0-20210825183410-e898025ed96a
	golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
	golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect
	golang.org/x/text v0.3.7 // indirect
	gopkg.in/yaml.v2 v2.4.0
	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

CheckJWT with Martini throwing <invalid Value>?

I'm trying the JWT middleware examples to get JWT to work with Martini and it's giving me an return when it hits the authentication handler.

Here's the code, straight from the examples..

package main

import (
    "encoding/json"
    "github.com/auth0/go-jwt-middleware"
    "github.com/dgrijalva/jwt-go"
    "github.com/go-martini/martini"
    "net/http"
)

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)
}

If I hit the unauthenticated route, it works. If I hit the authenticated route, it tells me I need a token. If I hit the authenticated route with a token that's not right, I get an error stating,

signature is invalid
<*errors.errorString Value>

So all that works. If I hit the authenticated route with the right token, it gives me as the return text. I can't find out why. This is straight from their example, though I get the same in my personal project.

Custom `ValidateWithLeeway` in #176 Introduced Breaking Changes to Token Validation

Checklist

  • I have looked into the README and have not found a suitable solution or answer.
  • I have looked into the documentation and have not found a suitable solution or answer.
  • I have searched the issues and have not found a suitable solution or answer.
  • I have upgraded to the latest version of this SDK and the issue still persists.
  • I have searched the Auth0 Community forums and have not found a suitable solution or answer.
  • I agree to the terms within the Auth0 Code of Conduct.

Description

In #176 with the introduction of the custom validateClaimsWithLeeway function, the audience validation the package dropped support for an empty expectedClaims.Audience.

Previously *jwt.validateClaimsWithLeeway would be called when validating a token, which ignored expectations that were left empty & allowed the caller to decide what they would like to validate. For example, this snippet of code from their audience validation ignores the audience if the expected audiences is empty.

Expected Behavior

When validating a token with a validator that had an empty audience slice, the validator should ignore the audience claim.

Actual Behavior

A validator with an empty expected Audience slice will always fail any token on validation. This could be mitigated by wrapping in an if len(expectedClaims.Audience) > 0 or initializing foundAudience to true if there are no expected audiences.

Reproduction

  1. Given you call the validator with an empty expected audience slice
  2. The token will always fail validation as foundAudience defaults to false

Go JWT Middleware version

v2.1.0

Go version

v1.19

add option for additional checks

At times consumers of the package want to validate more things in a token than what this package provides. The go to method of doing this is via the ValidatonKeyGetter func. We should provide specific functionality to do these checks instead of having users tack things into the key getter func.

jwt.Keyfunc

ๆ— ๆณ•ๅฐ† 'func(*jwt.Token) (interface{}, error) { return []byte(configure.Config.GetString("jwt.secret")), nil }' (็ฑปๅž‹ func(*jwt.Token) (interface{}, error)) ็”จไฝœ็ฑปๅž‹ jwt.Keyfunc

CustomClaims with multiple audiences cannot be unmarshalled

Description

I believe this is an isssue with the underlying jwt-go code, but I've been spending a lot of time struggling to make progress with this. I've updated my go modules and I'm currently on v1 of the jwt-middleware and v3.2.2 of the form3tech-oss/jwt-go packages

When presented with an audience which is an array, the library cannot unmarshal into the CustomClaims object and gives:
json: cannot unmarshal string into Go struct field CustomClaims.aud of type []string

Provide a clear and concise description of the issue, including what you expected to happen.

I believe the core issue lies in ParseWithClaims, but I can't say for sure

Reproduction

I'm mostly going off of the Auth0 Go Quickstart, and my code looks like this:
Middleware:

func (api API) SetupJWTMiddleware() *jwtmiddleware.JWTMiddleware {
	authJWTMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
		ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
			checkAudience := token.Claims.(jwt.MapClaims).VerifyAudience(api.AuthConfig.Audience, false)
			if !checkAudience {
				return token, errors.New("could not verify token audience")
			}

			checkIssuer := token.Claims.(jwt.MapClaims).VerifyIssuer(api.AuthConfig.Issuer, false)
			if !checkIssuer {
				return token, errors.New("could not verify token issuer")
			}

			cert, err := api.getPemCert(token)
			if err != nil {
				panic(err)
			}

			result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))

			return result, nil
		},
		SigningMethod: jwt.SigningMethodRS256,
	})

	return authJWTMiddleware
}

Authenticated request handler:

func (api API) AuthedEndpoint(apiEndpoint http.HandlerFunc) http.HandlerFunc {
	return func(response http.ResponseWriter, request *http.Request) {
		err := api.AuthMiddleware.CheckJWT(response, request)
		if err != nil {
			return // CheckJWT writes to response for us
		}

		token, err := jwtmiddleware.FromAuthHeader(request)

		if err != nil {
			http.Error(response, err.Error(), http.StatusForbidden)

			return
		}

		hasScope := api.checkScope("some-scope", token)
		if !hasScope {
			http.Error(response, "insufficient scope to access endpoint", http.StatusForbidden)

			return
		}

		apiEndpoint(response, request)
	}
}

checkScope:

func (api API) checkScope(scope string, tokenString string) bool {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		cert, err := api.getPemCert(token)

		if err != nil {
			return nil, err
		}

		result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))

		return result, nil
	})

	if err != nil {
		fmt.Printf(err.Error())
	}

	claims, ok := token.Claims.(*CustomClaims)

	hasScope := false

	if ok && token.Valid {
		scopes := strings.Split(claims.Scope, " ")
		for index := range scopes {
			if scopes[index] == scope {
				hasScope = true
			}
		}
	}

	return hasScope
}

I ran into this issue when trying to hit authenticated endpoints with a token generated with the React useAuth0 hook, specifically getAccessTokenSilently, which gives me a token with:

 "aud": [
    "my.project.domain/api/",
    "https://project.us.auth0.com/userinfo"
  ],

Support for custom Claims

Are there any plans to support custom Claims as the underlying library jwt-go does (with the parseWithClaims method) ?

If no, is there any reason for not adding it to the library? (I am willing to open a PR if anyone has an idea on what is the best way to add support for this)

Missing cookie causes CookieTokenExtractor to return error

Describe the problem

I'm using the CookieTokenExtractor and when the named cookie is not present in the request, the following error is returned: error extracting token: http: named cookie not present.

The Cookie method on http.Request returns a ErrNoCookie error when the cookie doesn't exist, and that result is being directly returned by the extractor code.

What was the expected behavior?

I would expect that when the named cookie isn't present, the extractor returns no error (and an empty token). This would put its behaviour in line with the AuthHeaderTokenExtractor.

Reproduction

  1. Use the CookieTokenExtractor, and don't pass a cookie with the provided name.
  2. Check the error returned to the errorHandler.

Environment

go-jwt-middleware v2.0.1

Incorrect documentation / breaking change lead to failed build

Description

Provide a clear and concise description of the issue, including what you expected to happen.

It seems there is incorrect documentation for Auth0 to address the most recent commit 1c6db3c. When providing options to github.com/auth0/go-jwt-middleware, specifically ValidationKeyGetter, we've always used github.com/dgrijalva/jwt-go *jwt.Token, which is currently invalid and throwns the shown error:

cannot use func literal (type func(*"github.com/dgrijalva/jwt-go".Token) (interface {}, error)) as type "github.com/form3tech-oss/jwt-go".Keyfunc in field value

I thought that I could just look at the documentation again to build auth (https://auth0.com/docs/quickstart/backend/golang/01-authorization) for an updated solution, but it is still using the old github.com/dgrijalva/jwt-go package.

I'm not sure why this was deployed to master branch without updating documentation on Auth0's website. If I'm incorrect on this and missing some information, my apologies. Please let me know.

Reproduction

Detail the steps taken to reproduce this error, what was expected, and whether this issue can be reproduced consistently or if it is intermittent.

I built my package and it failed. I expected it not to fail. This is a consistent issue. If you would like more information please let me know, otherwise I have followed the Auth0 docs and have not updated my package in over a year.

Environment

Please provide the following:

  • Version of this library used: Master branch
  • Other relevant versions (language, server software, OS, browser): Golang, Ubuntu
  • Other modules/plugins/libraries that might be involved:
    github.com/auth0/go-jwt-middleware
    github.com/dgrijalva/jwt-go
    https://github.com/urfave/negroni

Cannot import internal oidc package

Checklist

  • I have looked into the README and have not found a suitable solution or answer.
  • I have looked into the documentation and have not found a suitable solution or answer.
  • I have searched the issues and have not found a suitable solution or answer.
  • I have upgraded to the latest version of this SDK and the issue still persists.
  • I have searched the Auth0 Community forums and have not found a suitable solution or answer.
  • I agree to the terms within the Auth0 Code of Conduct.

Description

While following the example described in the repo: https://github.com/auth0/go-jwt-middleware/tree/master/examples/http-jwks-example. Initializing the test file fails with the error...

could not import github.com/auth0/go-jwt-middleware/v2/internal/oidc (invalid use of internal package github.com/auth0/go-jwt-middleware/v2/internal/oidc)

Reproduction

  1. Copied main.go and main_test.go.
  2. Tried to get oidc package.

Expected: Import is successful.
Actual: Import fails with...

could not import github.com/auth0/go-jwt-middleware/v2/internal/oidc (invalid use of internal package github.com/auth0/go-jwt-middleware/v2/internal/oidc)

Go JWT Middleware version

2.1.0

Go version

1.18

Example for using certs and RS256

Hey,

I'm trying to get this working with RS256 but I seem to do something wrong. As far as I can see, I can't figure out how to return the public cert in the ValidationKeyGetter.

Do you have some example code?

Thanks

Query string

Currently, only the Authorization header is checked. One could imagine that developers might want to configure this middleware to check the Authorization header and/or a query parameter (e.g., auth_code).

What do you think about making this configurable? I can submit the PR. I just want to know if you will accept such a PR.

Form3tech 0SS dependency cannot be imported

My go.mod file looks like this

module marunk20/rs

go 1.16

require (
	github.com/auth0/go-jwt-middleware v1.0.0
	github.com/gorilla/mux v1.8.0
)

When i build my app using go build main.go. I am stopped with the below error

go: marunk20/rs/server: package github.com/form3tech-oss/jwt-go imported from implicitly required module; to add missing requirements, run:
	go get github.com/form3tech-oss/[email protected]+incompatible

I couldn't execute go get too. Need help in resolving this

If we set multiple audience, we get the authentication error.

Describe the problem

When we set multiple audience and jwt only have one audience. then we got authentication error.

jwtValidator, err := validator.New(
	provider.KeyFunc,
	validator.RS256,
	issuerURL.String(),
	[]string{"aaaaaa", "bbbbbb"},
)

jwt payload is like below

{
  "email": "[email protected]",
  "email_verified": true,
  "iss": "xxxxxxxxxxxxxxxx",
  "sub": "xxxxxxxxxxxxxxxx",
  "aud": "aaaaaa",
  "iat": 1648000756,
  "exp": 1648036756
}

error is like below.

2022/03/23 08:08:42 Encountered error while validating JWT: jwt invalid: expected claims not validated: square/go-jose/jwt: validation failed, invalid audience claim (aud)

if we set only 1 audience. then we get success authentication

What was the expected behavior?

In my understanding,
if one of the audiences we set matches one of the client-side audiences, the authentication should succeed.

My understanding is correct?

Reproduction

  1. set multiple audience in validator.New.
  2. send jwt with one audience

please see Describe the problem

Environment

Version of go-jwt-middleware used: v2.0.1

Negroni example

Hey

It looks to me, like when we use negroni, we have to write explicitly that we want to use the middleware, everytime we use it. Am I wrong?

What to do when, for example, I have 25 secured routes, and 2 not secured (they don't need user to send the token), do I have to write this:

    r.Handle("/ping", negroni.New(
        negroni.HandlerFunc(jwtMiddleware.HandlerWithNext),
        negroni.Wrap(myHandler),
    ))

25 times?

Can I somehow use negroni.Use() ?

Best Regards :)

Pedantic stuff

I was reading RFC 6750. In researching #2, I wanted to look at the formal specification for how OAuth tokens should be handled. It mentions three schemes: Authorization header, form fields and query string parameters.

I've implemented the first and the last in #3. However, I note that RFC 6750 specifically talks about some header manipulations that SHOULD be performed in the case of query string parameters. That isn't currently done by this middleware but we should probably consider adding it. The main issue here is "leaking" tokens via caching.

Although RFC 6750 is for OAuth, I think almost all of the practical details (especially around security) apply for JWTs as well.

Comments?

Is this library vendor neutral?

It is not clear in README that this library vendor neutral.
It looks like it is. Please clarify and to what extent it implement the spec

URL in README.md results in 404

The following URL in the README.md returns a 404:

Important to avoid security issues described here: https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/

gorilla/context is not compatible with Go 1.7

https://github.com/gorilla/context

Note: gorilla/context, having been born well before context.Context existed, does not play well with the shallow copying of the request that http.Request.WithContext (added to net/http Go 1.7 onwards) performs. You should either use just gorilla/context, or moving forward, the new http.Request.Context().

It seems this project should no longer use gorilla context, but should replace it with the new package Go provides. Perhaps build constraints (see Build Constraints section) could allow supporting both versions depending on what version of go is being used.

It also appears, a "go1.6" tag on this current version would cause go get to checkout that tag for users that have go 1.6 installed. From go cmd docs:

When checking out or updating a package, get looks for a branch or tag that matches the locally installed version of Go. The most important rule is that if the local installation is running version "go1", get searches for a branch or tag named "go1". If no such version exists it retrieves the most recent version of the package.

There is already a fork of this project that removes gorilla context. If the current state were tagged, the branch could be merged in.

Pass the unverified token to Validator keyFunc

Describe the problem you'd like to have solved

In the v1 version of this library the ValidationKeyGetter attribute of the Options struct is passed a pointer to the unverified jwt Token object. In v2 ValidationKeyGetter has been replaced with validator.New's first argument which is of type func(context.Context) (interface{}, error).

The unverified token is no longer provided to the function used to retrieve the validation key. V1 allowed you to use properties in the header of the token (such as kid) to identify which key to use. V2 doesn't seem to support this.

Describe the ideal solution

Provide the unverified token to to the Validator.keyFunc function.

example on main page doesn't run

Description

copy pasted example from front page leads to error.

Provide a clear and concise description of the issue, including what you expected to happen.
The purpose of an example is to demonstrate the fitness / features of a project, copy pasting the example didn't do it.

Reproduction

follow the instructions of README.md

Detail the steps taken to reproduce this error, what was expected, and whether this issue can be reproduced consistently or if it is intermittent.
it is consistent, several go run main.go lead to:

./main.go:10:3: imported and not used: "context"

Suggested Fix

remove context from imports on both posted example.

Token used before issued

Describe the problem

When a Token is issued it is not immediately usable. When developing with Go 1.18 on Windows I frequently have to resync to NTP or the token appears to of been used before it was issued.

What was the expected behavior?

Tokens should have a backdated issue date (by at least a few seconds) to avoid time-drift causing users to use the token before it was used. This could have a serious impact on a production environment.

StackOverflow has a nice writeup on this bug https://stackoverflow.com/questions/60480824/auth0-jwt-authentication-error-parsing-token-token-used-before-issued

Reproduction

Develop an application with React Auth0 and Go JWT Middleware and try to login.

Environment

Windows 11 and Go 1.18 with JetBrains GoLand as my IDE and running the application.

An error occured while validating JWT: jwt invalid: error getting the keys from the key func: could not get well known endpoints from url https:///.well-known/openid-configuration: Get "https:///.well-known/openid-configuration": http: no Host in request URL

Checklist

  • I have looked into the README and have not found a suitable solution or answer.
  • I have looked into the documentation and have not found a suitable solution or answer.
  • I have searched the issues and have not found a suitable solution or answer.
  • I have upgraded to the latest version of this SDK and the issue still persists.
  • I have searched the Auth0 Community forums and have not found a suitable solution or answer.
  • I agree to the terms within the Auth0 Code of Conduct.

Description

I get the error An error occured while validating JWT: jwt invalid: error getting the keys from the key func: could not get well known endpoints from url https:///.well-known/openid-configuration: Get "https:///.well-known/openid-configuration": http: no Host in request URL when trying to use the JWT middleware.

Reproduction

I have the following middleware:

package middleware

import (
        "os"
        "time"
        "log"
        "fmt"
        "context"

        "net/http"
        "net/url"

        "github.com/auth0/go-jwt-middleware/v2"
        "github.com/auth0/go-jwt-middleware/v2/jwks"
        "github.com/auth0/go-jwt-middleware/v2/validator"
)

type CustomClaims struct {
        Scope string `json:"scope"`
}

func (c CustomClaims) Validate(ctx context.Context) error {
        return nil
}

// Only handles request when the user is authorized to access the endpoint
func EnsureValidToken() func(next http.Handler) http.Handler {
        issuerURL, err := url.Parse("https://" + os.Getenv("EMAIL_API_AUTH0_DOMAIN") + "/")
        if err != nil {
                panic(fmt.Sprintf("Failed to parse the issuer url: %v", err))
        }

        provider := jwks.NewCachingProvider(issuerURL, 5 * time.Minute)

        jwtValidator, err := validator.New(
                provider.KeyFunc,
                validator.RS256,
                issuerURL.String(),
                []string{"https://ksa-email-api.jomy.dev"}, // AUTH0 API Identifier (Audience)
                validator.WithCustomClaims(
                        func() validator.CustomClaims {
                                return &CustomClaims{}
                        },
                ),
                validator.WithAllowedClockSkew(time.Minute),
        )
        if err != nil {
                panic(err)
        }

        errorHandler := func(resp http.ResponseWriter, req *http.Request, err error) {
                log.Printf("An error occured while validating JWT: %v", err)
                log.Println(req)

                resp.Header().Set("Content-Type", "application/json")
                resp.WriteHeader(http.StatusUnauthorized)
                resp.Write([]byte(`{"message":"Failed to validate JWT."}`))
        }

        middleware := jwtmiddleware.New(
                jwtValidator.ValidateToken,
                jwtmiddleware.WithErrorHandler(errorHandler),
        )

        // return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
        //      next.ServeHTTP(resp, req)
        // })
        return func(next http.Handler) http.Handler {
                return middleware.CheckJWT(next)
        }
}

I generate a JWK token:

curl --request POST \
  --url 'https://dev-tce1ggxc6lqxphjc.eu.auth0.com/oauth/token' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data grant_type=client_credentials \
  --data client_id=MY_CLENT_ID \
  --data client_secret=MY_CLIENT_SECRET \
  --data audience=https://ksa-email-api.jomy.dev

I then use it in a request that uses this middleware:

curl \
  --request POST \
  --url https://ksa-email-api.jomy.dev/the_endpoint \
  --header "Authorization: Bearer THE_GENERATED_TOKEN" \
  --header 'content-type: application/json' \
  --data '...'

This gives me as response: {"message":"Failed to validate JWT."}, while on the server I get AAn error occured while validating JWT: jwt invalid: error getting the keys from the key func: could not get well known endpoints from url https:///.well-known/openid-configuration: Get "https:///.well-known/openid-configuration": http: no Host in request URL.

Go JWT Middleware version

v2.0.0

Go version

1.19.4 linux/amd64

Gin middleware

I would very much like to see some middleware for this that works with the gin framework.

Sample not working - ValidationKeyGetter - cannot use as jwt.Keyfunc value in struct literal

Description

image

Reproduction

  1. Download sample from Auth0 tutorial
  2. Extract and cd into folder
  3. go mod init test
go get github.com/codegangsta/negroni
go get github.com/auth0/go-jwt-middleware
go get github.com/dgrijalva/jwt-go
go get github.com/gorilla/mux
go get github.com/joho/godotenv
go get github.com/rs/cors
mv main.go test.go
go test

Open with VS Code and the error will appear.

Environment

Please provide the following:

  • Version of this library used: v1.0.1 (according to go.mod)
  • Version of the platform or framework used, if applicable: Go 1.16
  • Other relevant versions (language, server software, OS, browser): OpenSUSE 15.2
  • Other modules/plugins/libraries that might be involved: N/A

I hope I have not missed something obvious. I tested and the function is acceptable to jwt.Parse (which also takes a jwt.Keyfunc type). Please help! :)

Failed to get decoded token using Go fiber

Please do not report security vulnerabilities here.
The Responsible Disclosure Program details the procedure for disclosing security issues.

Thank you in advance for helping us to improve this library!
Your attention to detail here is greatly appreciated and will help us respond as quickly as possible.
For general support or usage questions, use the Auth0 Community or
Auth0 Support.
Finally, to avoid duplicates, please search existing Issues before submitting one here.

By submitting an Issue to this repository, you agree to the terms within the
Auth0 Code of Conduct.

Describe the problem

I'm using gofiber https://gofiber.io/ and so far I managed to validate the token. I followed this tutorial

I had to convert using the middleware using this library - https://github.com/gofiber/adaptor

Example code:

func main() {
  app := fiber.New()
  app.Use(cors.New())
 
  app.Use(adaptor.HTTPMiddleware(EnsureValidToken())
  app.Get("/api/books", books.GetAll)
  }

and based on the code I know that it will store in the Context

r = r.Clone(context.WithValue(r.Context(), ContextKey{}, validToken))

Route example:

  func GetAll(c *fiber.Ctx) error {
       token := c.Context().Value(jwtmiddleware.ContextKey{}).(*validator.ValidatedClaims)

        // The above code will always panic, I'm assuming that it already stored in the context since it passes the validation
   }

Panic example

panic: interface conversion: interface {} is nil, not *validator.ValidatedClaims

Any help will be really useful. Thanks

What was the expected behavior?

Reproduction

Environment

Error handler interface is too restricted

Hello! Right now you have type errorHandler:
type errorHandler func(w http.ResponseWriter, r *http.Request, err string)
i think, it seems too restricted, because type of err is string, instead of error. In case of type error, interface will be more Go-ideomatic. And in the client error handler it will be possible to operate with types, not strings:

err, ok := error.(MySuperError)
if ok == true {
	//do something with this error
}

instead:

if err == "Hey man! I'am Your Super Error! How are you? Are you missing?" {
	//do something
}

What do you think?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.