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 :)))