I have a Go backend that uses github user zett-8’s go-clean-echo as a template. Their template uses a middleware explicitly using auth0. I have a React frontend that properly uses auth0 for login and for getting JWT tokens to interact with the backend, which uses an auth0 API in the jwt middleware.
I am attempting to use logic from the example, though in an Echo-y way. So, I am having problems extracting the CustomClaims.
The middleware is as follows:
package middlewares
import (
"context"
"fmt"
"log"
"net/http"
"net/url"
"os"
"strings"
"time"
"tlmgateway/logger"
"github.com/auth0/go-jwt-middleware/v2/jwks"
"github.com/auth0/go-jwt-middleware/v2/validator"
"github.com/labstack/echo/v4"
"go.uber.org/zap"
// "github.com/zett-8/go-clean-echo/configs"
)
// CustomClaims contains custom data we want from the token.
type CustomClaims struct {
Scope string `json:"scope" form:"scope"`
Permissions []string `json:"permissions" form:"permissions"`
}
// Validate does nothing for this example, but we need
// it to satisfy validator.CustomClaims interface.
func (c CustomClaims) Validate(ctx context.Context) error {
return nil
}
func JwtMiddleware() (echo.MiddlewareFunc, error) {
issuerURL, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/")
if err != nil {
log.Fatalf("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{os.Getenv("AUTH0_AUDIENCE")},
validator.WithCustomClaims(
func() validator.CustomClaims {
return &CustomClaims{}
},
),
validator.WithAllowedClockSkew(time.Minute),
)
if err != nil {
log.Fatalf("Failed to set up the jwt validator")
}
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
authorization := c.Request().Header.Get("Authorization")
if authorization == "" {
return echo.NewHTTPError(http.StatusUnauthorized, "No Authorization Header")
}
if !strings.HasPrefix(authorization, "Bearer ") {
return echo.NewHTTPError(http.StatusUnauthorized, "Invalid Authorization Header")
}
token := strings.TrimPrefix(authorization, "Bearer ")
claims, err := jwtValidator.ValidateToken(c.Request().Context(), token)
if err != nil {
logger.Error("Invalid Token: ", zap.Error(err))
return echo.NewHTTPError(http.StatusUnauthorized, "Invalid Token")
}
c.Set("claims", claims.(*validator.ValidatedClaims))
c.Set("customClaims", claims.(*validator.CustomClaims))
return next(c)
}
}, nil
}
// HasScope checks whether our claims have a specific scope.
func (c CustomClaims) HasScope(expectedScope string) bool {
result := strings.Split(c.Scope, " ")
for i := range result {
if result[i] == expectedScope {
return true
}
}
return false
}
// HasPermission checks whether our claims have a specific permission.
func (c CustomClaims) HasPermission(expectedPermission string) bool {
for _, permission := range c.Permissions {
if permission == expectedPermission {
return true
}
}
return false
}
When the API using this middleware is hit, I get the following error:
interface conversion: interface {} is *validator.ValidatedClaims, not *validator.CustomClaims
Which is perplexing, given that the type of *validator.CustomClaims should theoretically indeed be of type CustomClaims.
Do I need to actually implement Validate
properly, unlike what the comments say in the auth0 example (copied over to my code)? How would I get ValidateToken
to give back a CustomClaims struct somewhere?