- With issues:
- Use the search tool before opening a new issue.
- Please provide source code and commit sha if you found a bug.
- Review existing issues and provide feedback or react to them.
Description
I have an API which I have created and I have some endpoints protected. The problem I am facing now on the client making a request is that the first request comes through with the Authorization header provided but a second request is blocked because Authorization is not present. I can confirm that Authorization is present and worked perfectly when I was running Typescript till I recreated the endpoints in Go with Gin.
How to reproduce
- Call estimate endpoint from client (iOS app) response succeceds
- Make a second call from Client (iOS app) response failed because it is not taking the Authorization header which contains token
package main
import (
"github.com/gin-gonic/gin"
)
type App struct {
Router *gin.Engine
Gateman *gateman.Gateman
Database *mongo.Client
}
func (a *App) StartApp() {
err := godotenv.Load()
if err != nil {
fmt.Printf("Could not load .env \n")
}
a.Database = database.DB
a.Router = gin.New()
a.Gateman = middleware.Gateman()
a.Router.Use(gin.Recovery())
a.Router.Use(middleware.DefaultHelmet())
a.Router.Use(middleware.GinContextToContextMiddleware())
a.Router.Use(middleware.RequestID(nil))
a.Router.Use(middleware.ErrorHandler())
a.Router.Use(middleware.Logger("package-service"))
connection, err := amqp091.Dial(os.Getenv("AMQP_URL"))
if err != nil {
log.Fatal(fmt.Printf("Error on dial %v\n", err.Error()))
}
routes.Routes(a.Router, database.GetDatabase(a.Database), a.Gateman, connection)
}
func (a *App) Run(addr string) {
logs := log.New(os.Stdout, "package-service", log.LstdFlags)
server := &http.Server{
Addr: addr,
Handler: a.Router,
ErrorLog: logs,
IdleTimeout: 120 * time.Second, // max time for connections using TCP Keep-Alive
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
go func() {
if err := server.ListenAndServe(); err != nil {
logs.Fatal(err)
}
}()
// trap sigterm or interrupt and gracefully shutdown the server
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
signal.Notify(c, os.Kill)
sig := <-c
logs.Println("Recieved terminate, graceful shutdown", sig)
tc, _ := context.WithTimeout(context.Background(), 30*time.Second)
server.Shutdown(tc)
}
func Routes(r *gin.Engine, db *mongo.Database, g *gateman.Gateman, conn *amqp091.Connection) {
atHandler := pc.NewPackagesController(ps.NewPackagesService(pr.NewPackagesRepository(db)), g, events.NewEventEmitter(conn))
r.Use(CORS())
v1 := r.Group("/api/v1/package")
{
v1.POST("/query", GraphqlHandler(db, directives.NewDirectivesManager(g)))
v1.GET("/", PlaygroundHandler(db))
v1.POST("/", g.Guard([]string{"user"}, nil), atHandler.Create)
v1.POST("/estimate", g.Guard([]string{"user"}, nil), atHandler.Estimate)
v1.PUT("/:packageID", g.Guard([]string{"user", "admin"}, nil), atHandler.Update)
v1.PUT("/:packageID/assign", g.Guard([]string{"admin"}, nil), atHandler.Assign)
v1.POST("/:packageID/cancel", g.Guard([]string{"user", "admin"}, nil), atHandler.CancelRequest)
v1.POST("/:packageID/complete", g.Guard([]string{"admin"}, nil), atHandler.Complete)
v1.POST("/:packageID/reject", g.Guard([]string{"admin"}, nil), atHandler.RejectRequest)
v1.GET("/healthz", atHandler.GetHealth)
}
r.GET("/", atHandler.GetUP)
}
func CORS() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
func main() {
start := App{}
start.StartApp()
start.Run(":3009")
}
Expectations
All endpoints that are Guarded simply checks the header for Authorization and if provided in the request, it should be successful
Actual result
First request succeed /estimate
Second request /
POST request fails to accept Authorization header
Also irrespective of what the first post request is, the second post request just never accept the Authorization
header
Also need to mention that I do not have this issue with postman. Both request run independently but using another client for the request, gives this problem
Another point, calling a single endpoint multiple time, works fine, calling a different one is where the header is rejected
Environment
- go version: 1.19
- gin version (or commit ref): v1.8.1
- operating system: Mac and iOS mobile
Comment From: AdieOlami
I think I can pin the problem down now. A weird problem. In my request, I am using /api/v1/package this was causing a 307 redirect. It expected me to use /api/v1/package/ which is weird.