go-jwt-middleware: 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 1c6db3ceac5f4d28b35ee4b9949a3f80030aa6a3. 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

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 7
  • Comments: 26 (11 by maintainers)

Most upvoted comments

Same here; to make things work again, I had to explicitly convert the []interface{} to []string by adding the following segment to the ValidationKeyGetter code:

jwtmiddleware.New(jwtmiddleware.Options{
	ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
		claims, ok := token.Claims.(jwt.MapClaims)
		if !ok {
			return token, errors.New("invalid claims type")
		}

		if audienceList, ok := claims["aud"].([]interface{}); ok{
			auds := make([]string, len(audienceList))
			for _, aud := range(audienceList) {
				audStr, ok := aud.(string)
				if !ok {
					return token, errors.New("invalid audience type")
				}
				auds = append(auds, audStr)
			}
			claims["aud"] = auds
		}
                
                // Verify 'aud' claim
		checkAud := claims.VerifyAudience(auth0Aud, false)
		if !checkAud {
			return token, errors.New("invalid audience")
		}
                // ... etc

Not a big problem perse, but sadly it makes the function way more verbose.

Is there a shorter way to cast []interface{} to []string? I understand this is not as easy as you’d expect by doing some searching on this.

Hi @grounded042, thanks for the quick response! Glad to see that the docs have been updated. However, I just wanted you to be aware of two potential issues: https://github.com/form3tech-oss/jwt-go/issues/7 https://github.com/form3tech-oss/jwt-go/issues/5

I seem to be running into similar issues. I hope I go in to enough detail for you:

  1. My JWT’s aud is [http://xxxxx.xxx.xxxxx.com https://xxxxx-xxxx.auth0.com/xxx]

  2. To confirm, I print this out: fmt.Println(token.Claims.(jwt.MapClaims)["aud"]). Output is the same as the previous line aud

  3. I kept getting invalid audience using this aud. Not sure why at this point.

  4. After looking into the form3tech-oss/jwt-go package, specifically func(m MapClaims) VerifyAudience(cmp string, req bool) bool {, I noticed aud, ok := m["aud"].([]string) , ok was always false.

  5. So I thought there might be some type issue, such as the one in https://github.com/form3tech-oss/jwt-go/issues/7. To check this out, before VerifyAudience(AUDIENCE_HERE, false) I just set the aud to test with token.Claims.(jwt.MapClaims)["aud"] = "test", and then called checkAud := token.Claims.(jwt.MapClaims).VerifyAudience("test", false). This worked successfully. Super weird. Thought it might be because originally I was using a []string, and this one uses a string, which shouldn’t even happen since RFC states both should be supported. So I tried to then set the token’s aud to a []string, and verify that audience, which also worked.

  6. So I thought it was a good time to check what my original aud type is according to form3tech-oss/jwt-go within the VerifyAudience method. Here’s what I used:

     aud, ok := m["aud"].([]string)
     fmt.Println(fmt.Sprintf("%T", m["aud"]))

Output: []interface {}

So my aud is not being asserted to []string it seems.

At this point, I’m not really sure where things went wrong when parsing jwt.MapClaims have you run into this issue yet? It seems at least some must be.

Hi everyone, we also encountered this problem, I’m putting our solution here in case it will useful for someone.

This solution doesn’t require using the auth0 v2 sdk which was in beta until recently (if you are implementing from scratch I suggest using it), and does not require vendoring the jwt package to your repository and modify it, and does not require github.com/form3tech-oss/jwt-go which is no longer maintained.

The problem as we understand it relies in golang-jwt v3.X which only have a StandardClaims struct that allows a single Audience, so when unmarshaling a jwt with multiple audience, it fails.

type StandardClaims struct {
	Audience  string `json:"aud,omitempty"`
	ExpiresAt int64  `json:"exp,omitempty"`
	Id        string `json:"jti,omitempty"`
	IssuedAt  int64  `json:"iat,omitempty"`
	Issuer    string `json:"iss,omitempty"`
	NotBefore int64  `json:"nbf,omitempty"`
	Subject   string `json:"sub,omitempty"`
}

However, this was solved in golang-jwt v4, which features a RegisteredClaims that allows multiple audience (ClaimStrings is []string)

type RegisteredClaims struct {
	Issuer string `json:"iss,omitempty"`
	Subject string `json:"sub,omitempty"`
	Audience ClaimStrings `json:"aud,omitempty"`
	ExpiresAt *NumericDate `json:"exp,omitempty"`
	NotBefore *NumericDate `json:"nbf,omitempty"`
	IssuedAt *NumericDate `json:"iat,omitempty"`
	ID string `json:"jti,omitempty"`
}

So we decided to import both versions (v3.2.1 and v4) of golang-jwt (unfortunately using only v4 breaks the currently required jwt-go version). Then, we used the v4 version for the CustomClaim struct, and the old version for the rest of the validation procedures as stated in the docs

import (
	"github.com/golang-jwt/jwt"
	jwt_modern_claims "github.com/golang-jwt/jwt/v4"
)

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

...

After that, everything worked as expected, no unmarshaling errors and we were able to implement functions such as

func (c CustomClaims) verifyAudience() bool {
	for _, aud := range c.Audience {
		if aud == c.expectedAudience {
			return true
		}
	}
	return false
}

I think problem solved. I tried those versions of modules

github.com/auth0/go-jwt-middleware v1.0.0
github.com/form3tech-oss/jwt-go v3.2.3+incompatible

And with them was no need to convert interface into string.

@ashish-scalent Could you please open a separate issue describing in full detail the issue following the template we provide please?😃 It would be great to provide a reproducible of your setup code.

Hey Sorry my bad you were right I actually forgot to pass *validator.ValidateToken function in middleware

no issue from my side, thanks for the instant support

Hey @ashish-scalent ! It seems you’re trying to do /v2/v2 twice in your go get:) It should be just go get github.com/auth0/go-jwt-middleware/v2@v2.0.0-beta.

I was using

github.com/auth0/go-jwt-middleware v1.0.0 github.com/form3tech-oss/jwt-go v3.2.2+incompatible

Upgrading to

github.com/auth0/go-jwt-middleware v1.0.0 github.com/form3tech-oss/jwt-go v3.2.3+incompatible

did not solve the problem.

I think it has something to do with this line of code:

claims, ok := token.Claims.(jwt.MapClaims)

which according to https://github.com/auth0/go-jwt-middleware/issues/72#issuecomment-788389180 I have to change to

claims, ok := token.Claims.(*MyClaims) // Custom-Claims allows leeway support

@truescotian Thinking about this some more I think this has to do with how JSON is parsed into the map claims. I don’t have time to dig deeper right now, but that’s where I would start looking.