Golang JWT middleware example: adding validation results to context

,

I am looking at the example for JWT middleware in Go.

In my API backend as-is, I use the Echo framework (specifically, based off this example). This is no big issue itself, as Echo has a very useful echo.WrapMiddleware() function for turning regular bare HTTP handler middlewares into Echo ones. I literally just add the following in jwt.go:

func EchoEnsureValidToken() (echo.MiddlewareFunc, error) {
	mw := echo.WrapMiddleware(EnsureValidToken())
	return mw, nil
}

and then use it in my app as per normal. However, I’m running into an issue with my handler functions.

main.go:

package main

import (
	"log"
	"os"

	database "tlmgateway/db"
	_ "tlmgateway/docs"
	"tlmgateway/handlers"
	"tlmgateway/logger"
	"tlmgateway/middlewares"

	"tlmgateway/services"
	"tlmgateway/stores"

	"go.uber.org/zap"
)

var GO_ENV = os.Getenv("GO_ENV")

// @title Go clean echo API v1
// @version 1.0
// @description This is a sample server.
// @termsOfService http://swagger.io/terms/

// @host localhost:3000
// @BasePath /
// @schemes http
func main() {
	err := logger.New()
	if err != nil {
		log.Fatal(err)
	}

	db, err := database.New(GO_ENV == "development")
	if err != nil {
		logger.Fatal("failed to connect to the database", zap.Error(err))
	}

	e := handlers.Echo()

	s := stores.New(db)
	ss := services.New(s)
	h := handlers.New(ss)

	jwtCheck, err := middlewares.EchoEnsureValidToken()
	if err != nil {
		logger.Fatal("failed to set JWT middleware", zap.Error(err))
	}

	handlers.SetDefault(e)
	handlers.SetApi(e, h, jwtCheck)

	PORT := os.Getenv("PORT")
	if PORT == "" {
		PORT = "3001"
	}

	logger.Fatal("failed to start server", zap.Error(e.Start(":"+PORT)))
}

But in Echo, the routing and handlers are done a bit differently:

package handlers

import (
	"net/http"
	"strings"

	"repo/services"
	"repo/utils"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	echoSwagger "github.com/swaggo/echo-swagger"
)

type Handlers struct {
	RecordHandler
}

func New(s *services.Services) *Handlers {
	return &Handlers{
		RecordHandler:      &recordHandler{s.Record},
	}
}

func SetDefault(e *echo.Echo) {
	utils.SetHTMLTemplateRenderer(e)

	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "OK")
	})
	e.GET("/healthcheck", HealthCheckHandler)
	e.GET("/swagger/*", echoSwagger.WrapHandler)
}

func SetApi(e *echo.Echo, h *Handlers, m echo.MiddlewareFunc) {
	g := e.Group("/api/v1")
	g.Use(m)

	// Organizations
	g.GET("/record", h.RecordHandler.GetRecords)
}

func Echo() *echo.Echo {
	e := echo.New()

	e.Use(middleware.Logger())
	e.Use(middleware.Recover())
	e.Pre(middleware.RemoveTrailingSlash())
	e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
		AllowOrigins: []string{"*"},
	}))
	e.Use(middleware.GzipWithConfig(middleware.GzipConfig{
		Skipper: func(c echo.Context) bool {
			return strings.Contains(c.Request().URL.Path, "swagger")
		},
	}))

	return e
}

My issue is, these handlers simply take the Context and go from there. They’re defined like this:

GetRecords(c echo.Context) error

The question then is, how do I add something like ValidatedClaims and CustomClaims from the middleware function in the example to the context so that it can be used where I need to use it? The above code is more for illustration purposes, since the only implementation issue I’m having is “add validation results to context”.

OP here. The solution is that the stated problem doesn’t actually exist, I was misunderstanding the flow of information.

using the middlewares.EchoEnsureValidToken() middleware in fact adds information to the Request() part of the echo.Context type that is then used in the handler functions.

You access the Request with c.Request(). To get the claims specifically, within the middlewares package:

claims := c.Request().Context().Value(jwtmiddleware.ContextKey{}).(*validator.ValidatedClaims).CustomClaims.(*CustomClaims)

If you want to access those types from outside the middlewares package, you have to import middlewares and use it as a specifier for the CustomClaims type:

claims := c.Request().Context().Value(jwtmiddleware.ContextKey{}).(*validator.ValidatedClaims).CustomClaims(*middlewares.CustomClaims)

Hope this helps anyone else running into the same brain teaser I did.

1 Like

Thanks for following up with your solution!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.