Short answer
Explanation about server side cookie: cookies are set via headers, that's how the browser understands that you want to set a cookie, and you need ctx.Writer to write headers. at the end, "you want to access ctx.Writer in your resolver".
You have a ctx in your middleware(In my case it's gin and it's ctx.Request.Context()), And you have a ctx.Writer, which is what you need in order to write to your headers and set the cookie.
You should put your ctx.Writer into your ctx.Request.Context(), because your ctx.Request is going forward to your resolver, not the hole ctx!
And then you can access your Writer.
Full answer
In middleware you must build a struct object, pass ctx.Writer into it and set a pointer to ctx.Request.Context and set a method to set cookie for you.
type CookieAccess struct {
Writer http.ResponseWriter
UserId uint64
IsLoggedIn bool
}
// method to write cookie
func (this *CookieAccess) SetToken(token string) {
http.SetCookie(this.Writer, &http.Cookie{
Name: cookieName,
Value: token,
HttpOnly: true,
Path: "/",
Expires: time.Now().Add(token_expire),
})
}
And in your middleware:
func extractUserId(ctx *gin.Context) (uint64, error) {
c, err := ctx.Request.Cookie(cookieName)
if err != nil {
return 0, errors.New("There is no token in cookies")
}
userId, err := ParseToken(c.Value)
if err != nil {
return 0, err
}
return userId, nil
}
func setValInCtx(ctx *gin.Context, val interface{}) {
newCtx := context.WithValue(ctx.Request.Context(), cookieAccessKeyCtx, val)
ctx.Request = ctx.Request.WithContext(newCtx)
}
func Middleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
cookieA := CookieAccess{
Writer: ctx.Writer,
}
// &cookieA is a pointer so any changes in future is changing cookieA is context
setValInCtx(ctx, &cookieA)
userId, err := extractUserId(ctx)
if err != nil {
cookieA.IsLoggedIn = false
ctx.Next()
return
}
cookieA.UserId = userId
cookieA.IsLoggedIn = true
// calling the actual resolver
ctx.Next()
// here will execute after resolver and all other middlewares was called
// so &cookieA is safe from garbage collector
}
}
You must call this func in your resolver.
It's getting ctx and returning &cookieA
func GetCookieAccess(ctx context.Context) *CookieAccess {
return ctx.Value(cookieAccessKeyCtx).(*CookieAccess)
}
And finally in your Login resolver:
CA := security.GetCookieAccess(ctx)
CA.SetToken(token)
CA.UserId = userId
I hope this will help someone :)))