symtek
symtek

Reputation: 61

Use existing session cookie in gin router

I'm building a simple webserver in Go / Gin and I want to use cookies to create a persistent session that keeps a user logged in if they navigate away or navigate across several pages.

Ideally, this is the flow:

Code later on will verify if the cookie token value is expired and/or corresponds to an active user in the database.

The most recent iteration I've tried is:

router := gin.Default();

token_value := func(c *gin.Context) string {

    var value string

    if cookie, err := c.Request.Cookie("session"); err == nil {
      value = cookie.Value
    } else {
      value = RandToken(64)
    }
    return value
  }

  cookie_store := cookie.NewStore([]byte(token_value))
  router.Use(sessions.Sessions("session",cookie_store))

But this fails because token_value is type func(c *gin.Context) string, and not string.

I know there's something I'm missing here, and I'd love some guidance in how to resolve this issue.

Thanks!

Upvotes: 3

Views: 12072

Answers (1)

Brits
Brits

Reputation: 18255

The demo code is mostly doing what you want but there is one issue. A cookie that does not include "Expires" or "Max-Age" attributes is a "session" cookie and "A session finishes when the client shuts down, and session cookies will be removed.".

So the reason you are not successfully retrieving the existing cookie is because the browser has removed it (you can check this using the developer tools in most browsers). To resolve this use something like:

store := cookie.NewStore([]byte("secret"))
store.Options(sessions.Options{MaxAge:   60 * 60 * 24}) // expire in a day

following this change the cookie will be sent with the appropriate option (and will remain on the browser for a day unless cleared due to user settings/actions):

mysession=MTYxMzg5MDc5OXxEdi1CQkFFQ180SUFBUkFCRUFBQUhQLUNBQUVHYzNSeWFXNW5EQWNBQldOdmRXNTBBMmx1ZEFRQ0FBWT18jWiVoXgauu5T16pLbinR4uYu5XakM7RSmnGHNxCzPf0=; Expires=Mon, 22 Feb 2021 06:59:59 GMT; Max-Age=86400

You can test this by adding the above to the example code i.e.:

package main

import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    store := cookie.NewStore([]byte("secret"))
    store.Options(sessions.Options{MaxAge:   60 * 60 * 24}) // expire in a day
    r.Use(sessions.Sessions("mysession", store))

    r.GET("/incr", func(c *gin.Context) {
        session := sessions.Default(c)
        var count int
        v := session.Get("count")
        if v == nil {
            count = 0
        } else {
            count = v.(int)
            count++
        }
        session.Set("count", count)
        session.Save()
        c.JSON(200, gin.H{"count": count})
    })
    r.Run(":8000")
}

If you compile/run this and and then open http:127.0.0.1:8000/incr you should see {"count":0}. refresh the page a few times and verify that the number increases. Now close and reopen your browser and go to http:127.0.0.1:8000/incr - the number should be one above what it was before you closed the browser (demonstrating that this the cookie survived the restart).

I believe that this is the info you are looking for but I may have misunderstood. It is important to note that, in this case, cookies are generated by the server. Once the browser has received the cookie it will include it in subsequent requests (meaning the server can make use of it).

For the comment:

I'm using this to read the cookie value:

if cookie, err := c.Request.Cookie("test_session"); err == nil {       
    value := cookie.Value
    log.Printf("Cookie value: %v",value) 
} 

but it doesn't match the cookie value I see in my browser.

The session store is managing the cookie for you; use session.Get (as per the example) to retrieve it.

If you want to use 'raw' cookies then you should not be using a store to set them (use SetCookie). But BE WARNED you need to be very careful when doing this because you cannot trust any cookies you receive; it's trivial for an end user to edit the value of a cookie (the browser dev tools provide a nice user interface for this!).

To protect against this session writes the cookies encoded in a way that makes it possible to detect if they have been altered (with caveats; if security is a real concern you need to read the docs). This uses the "authentication" key ("secret" in this example so change that because anyone who knows the key can change the token!). There is also an option to encrypt the cookie contents (as it stands its fairly easy for a user to extract the information your cookie contains; in most cases this is not a big issue but it may be for you).

Upvotes: 6

Related Questions