oapi-codegen icon indicating copy to clipboard operation
oapi-codegen copied to clipboard

Echo context with StrictServerInterface

Open ss-mark opened this issue 10 months ago • 1 comments

Is there an example or configuration showing how to use echo.Context with the StrictServerInterface generated by oapi-codegen?

ss-mark avatar Mar 31 '25 05:03 ss-mark

I had a very similar problem and the only way I managed to solve it involved replacing the context object which definetely seems hacky (but hey, it works!)

so, first and foremost, excerpt of setting up middleware:

e := echo.New()

_server := server.New(app) // just initializing some business logic, nothing of importance here
specs.RegisterHandlersWithBaseURL(
    e,
    specs.NewStrictHandler(
	_server,
	[]specs.StrictMiddlewareFunc{
		authenticationMiddleware(app),
	},
    ), 
    "/api",
)

now let's take a look at the middleware:

// I did warn you that it was hacky, didn't I ?
func setSession(ctx echo.Context, session *auth.Session) {
	currentRequest := ctx.Request()
	currentContext := currentRequest.Context()

        // yes, the compiler did warn me to not use string type. this is just for demo purposes, relax.
	newContext := context.WithValue(currentContext, "session", session)
	newRequest := currentRequest.WithContext(newContext)
	ctx.SetRequest(newRequest)
}

// again, myApp is how I communicated with external stuff, like database and so on. nothing really specific to the question asked.
func authenticationMiddleware(app myApp) specs.StrictMiddlewareFunc {
	return func(f specs.StrictHandlerFunc, operationId string) specs.StrictHandlerFunc {
                 // here you can check if operationId is on the list of known public operations, oapi-codegen does include
                 // another example which IIRC parses the spec and extracts this information from the spec itself
                 // https://github.com/oapi-codegen/oapi-codegen/tree/main/examples/authenticated-api/echo
                 // on the other hand, this involves a separate library and does not use strict server
		return func(ctx echo.Context, request any) (response interface{}, err error) {
			header := ctx.Request().Header.Get("Authorization")

			// just for demo purposes: 
			if (header == "super-secure-token") {
				setSession(ctx, &SessionObject{UserId: "logged-in-user"})
				return f(ctx, request)
			}

			return nil, echo.NewHTTPError(http.StatusUnauthorized, "better luck next time")
		}
	}
}

finally, in your handler function you can retrieve the session object from the context:

func (s *server)  MyHandler(ctx context.Context, ...) {
    // again, don't use string for a key, demo purposes, etc.
    fmt.Printf("session: %+v", ctx.Value("session"))
}

toudi avatar May 04 '25 22:05 toudi