Search code examples
gojwtauth0go-gin

How to retrieve permission from Auth0 jwt token using go gin


I'm learning go and wanted to setup a simple application using auth0. Using their tutorials I was able to setup a basic authentication for my api endpoints. Now I wanted to add permission handling using the jwt token. So I activated RBAC for the api-endpoint and added a permission. I used the flow from the tutorial for custom claims, but wrote my own middleware with it and adjusted it to work with gin.

func NeedsPermission(expectedScope string) gin.HandlerFunc {
    return func(context *gin.Context) {
        token := context.Request.Context().Value(jwtmiddleware.ContextKey{}).(*validator.ValidatedClaims)

        claims := token.CustomClaims.(*CustomClaims)

        if !claims.HasScope(expectedScope) {
            context.AbortWithStatus(403)
        }
        context.Next()
    }
}

problem is that there are no custom claims in the token but only the default: openid, profile and email ones.

this is the token content:

{
  "iss": "https://dev-****.us.auth0.com/",
  "sub": "google-oauth2|****",
  "aud": [
    "localhost:3000/books",
    "https://dev-****.us.auth0.com/userinfo"
  ],
  "iat": 1701789297,
  "exp": 1701875697,
  "azp": "***",
  "scope": "openid profile email",
  "permissions": [
    "read:books"
  ]
}

so it does have a field permissions, but how can I access that using the auth0/go-jwt-middleware or do I have to decode it first somehow?


Solution

  • Permissions is a custom claim, so you need to pass the WithCustomClaims option with an implemention of the validator.CustomClaims interface. Then when you create the validator:

        ...
        jwtValidator, _ := validator.New(
            keyFunc,
            validator.HS256,
            issuer,
            audience,
            validator.WithCustomClaims(func() validator.CustomClaims {
                return &MyClaims{}
            }),
        )
        mw := jwtmiddleware.New(jwtValidator.ValidateToken)
        ...
    

    Where MyClaims is something like this. Note your HasScope method:

    type MyClaims struct {
        Permissions    []string `json:"permissions"`
    }
    
    func (c *MyClaims) Validate(ctx context.Context) error {
        // Validate structure of permissions here, i.e. check for 400 not 403
        return nil
    }
    
    func (c MyClaims) HasScope(requiredPerm string) bool {
        for _, perm := range c.Permissions {
            if perm == requiredPerm {
                return true
            }
        }
        return false
    }