Package gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends.

sessions

GoDoc Build Status Sourcegraph

gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends.

The key features are:

  • Simple API: use it as an easy way to set signed (and optionally encrypted) cookies.
  • Built-in backends to store sessions in cookies or the filesystem.
  • Flash messages: session values that last until read.
  • Convenient way to switch session persistency (aka "remember me") and set other attributes.
  • Mechanism to rotate authentication and encryption keys.
  • Multiple sessions per request, even using different backends.
  • Interfaces and infrastructure for custom session backends: sessions from different stores can be retrieved and batch-saved using a common API.

Let's start with an example that shows the sessions API in a nutshell:

	import (
		"net/http"
		"github.com/gorilla/sessions"
	)

	// Note: Don't store your key in your source code. Pass it via an
	// environmental variable, or flag (or both), and don't accidentally commit it
	// alongside your code. Ensure your key is sufficiently random - i.e. use Go's
	// crypto/rand or securecookie.GenerateRandomKey(32) and persist the result.
	var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))

	func MyHandler(w http.ResponseWriter, r *http.Request) {
		// Get a session. We're ignoring the error resulted from decoding an
		// existing session: Get() always returns a session, even if empty.
		session, _ := store.Get(r, "session-name")
		// Set some session values.
		session.Values["foo"] = "bar"
		session.Values[42] = 43
		// Save it before we write to the response/return from the handler.
		err := session.Save(r, w)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

First we initialize a session store calling NewCookieStore() and passing a secret key used to authenticate the session. Inside the handler, we call store.Get() to retrieve an existing session or create a new one. Then we set some session values in session.Values, which is a map[interface{}]interface{}. And finally we call session.Save() to save the session in the response.

More examples are available on the Gorilla website.

Store Implementations

Other implementations of the sessions.Store interface:

License

BSD licensed. See the LICENSE file for details.

Owner
Gorilla Web Toolkit
Gorilla is a web toolkit for the Go programming language that provides useful, composable packages for writing HTTP-based applications.
Gorilla Web Toolkit
Comments
  • Read the explanation in the linked issue

    Read the explanation in the linked issue

    https://github.com/markbates/goth/issues/55

    This PR is not nessecary a finaly solution but is created to raise an issue I have with my solution right now. I dont mind some better suggestions, but this ID field is unused in the cookiestore and I dont see the harm in using it.

  • #80 add context_legacy.go and context.go for go 1.7 migration

    #80 add context_legacy.go and context.go for go 1.7 migration

    Tests on Go 1.7 currently failing with:

    ➜  sessions [go17-context] go test
    --- FAIL: TestFlashes (0.00s)
        sessions_test.go:71: No cookies. Header: map[]
    FAIL
    exit status 1
    FAIL    github.com/gorilla/sessions 0.014s
    

    Trying to debug. cc @elithrar

  • What Would gorilla/sessions v2 Look Like?

    What Would gorilla/sessions v2 Look Like?

    With Go 1.7's request.Context() requiring a (non-trivial!) breaking API change, I figured it would be a good time to discuss what a "v2" of this package would look like. Further, with golang/dep on the horizon, vendoring/pinning dependencies is more common-place, allowing us to 'safely' leave v1 API users as-is whilst improving the library for others.

    What I see as key changes:

    • [ ] Supporting request.Context (only)
    • [ ] Enriching the Store interface: New, Get, Save, Delete rather than relying on MaxAge to trigger it
    • [ ] Better built-in stores: BoltDB instead of a FilesystemStore (still uses the filesystem, but is a more scalable store)
    • [ ] Improved crypto interface: enforce algorithms, key size, and don't give users a choice. De-emphasize encrypting (an option, rather than a first-class param in New)
    • [ ] Fix the order of - Save(w, r) rather than (r, w)
    • [ ] Improve the user-experience around Save (forgetting to save sucks, and is common enough!)
    • [ ] Make sessions.Values better (setters, getters, rather than a map)
    • [ ] Simplify the internal registry: call it a cache (what it really is), and perhaps have it auto-save at the end of a request?
    • [ ] Out of the box middleware that makes a session available? Checks for one?
    • [ ] JSON as the default encoder, not gob. Faster, less overhead (in bytes); retain gob for those who really need to store blobs.
    • [ ] Potentially supporting more than just cookies for non-cookie stores (Bearer tokens, but not JWTs).

    There is no schedule for this yet. Open to feedback.

  • Race condition in FilesystemStore

    Race condition in FilesystemStore

    There is a race condition in FilesystemStore that I intend to fix but I would like your input before I go ahead and do it. Basically the problem is that if you have concurrent requests from the same user (same session) that the following is possible:

    1. Request 1 opens the session to perform a semi long operation
    2. Request 2 opens the session
    3. Request 2 Removes session data to perform "logout" or similar
    4. Request 2 saves
    5. Request 1 saves, which makes it as if the session was never logged out

    I have added a test case for this flaw at cless/sessions@f84abeda17de0b4fcd72d277412f3d3192f206f2

    The most straight forward way to fix this would be by introducing locks at the file system level. However, golang has no cross platform way to do file locking. It does expose flock in syscall but that only works if the OS supports it. I believe the behavior of flock might also be different on different unixes although I am not sure that this is the case. Another issue with flock is that it might not work on NFS.

    An entirely different solution would be to keep a map of locks in the FilesystemStore object itself. This has another set of disadvantages: You can't have multiple processes access the same file system sessions and you can't create multiple stores for the same file system session within a single application. However, both these things are already impossible to do without causing issues.

    In the end, I think the best solution is to keep a map of locks in the store object because all the disadvantages in that scenario can be properly documented and you can reply on the behavior being the same across different systems.

    Other storage backends that are based on FilesystemStore might copy this flaw (I noticed this issue when reviewing Redistore code for a project of mine boj/redistore#2)

  • Go 1.7: http.Request.Context breaks gorilla/context usage

    Go 1.7: http.Request.Context breaks gorilla/context usage

    The new http.Request.Context() in Go 1.7 creates a shallow copy of the original request that requires the caller to save it upstream. This copy has a different address and therefore has a different map key in the context map provided by gorilla/context.

    In order to fix this in gorilla/sessions we need to make a breaking change for Go 1.7+ users:

    • [ ] Use build tags to provide two context implementations as per gorilla/csrf: https://github.com/gorilla/csrf/blob/master/context_legacy.go
    • [ ] The only use of context.Set is within GetRegistry - this will now need to return (*Registry, *http.Request).
    - func GetRegistry(r *http.Request) *Registry {
    + func GetRegistry(r *http.Request) (*Registry, *http.Request)
    
    • [ ] There is a reasonable amount of code in the wild that this will break: https://github.com/search?q=%22sessions.GetRegistry%22+language%3Ago&type=Code&utf8=%E2%9C%93 as many users are calling sessions.GetRegistry(r).Get(store, name). They will need to change their code:
    - sessions.GetRegistry(r).Get(s, name)
    + var reg *sessions.Registry
    + reg, r = sessions.GetRegistry(r)
    + sess, err := reg.Get(store, name)
    

    That should be about it. This is unavoidable unfortunately, but is (thankfully) a compile-time breaking change that we can document clearly.

    Ref: https://github.com/gorilla/mux/issues/168

  • Not able to delete sessions by setting MaxAge = -1

    Not able to delete sessions by setting MaxAge = -1

    It's possible I'm doing something wrong, but I can't seem to find any documentation that shows a different approach.

    Here is my initialization code: (1)

      cookieStore = sessions.NewCookieStore(
        []byte(securecookie.GenerateRandomKey(64))[:64],
        []byte(securecookie.GenerateRandomKey(32))[:32])
      cookieStore.Options = &sessions.Options{
        MaxAge:   86400 * 7, // Session valid for one week.
        HttpOnly: true,
      }
    

    Here's how sessions are added to the cookie store: (2)

    
      c := appengine.NewContext(r)
      s, _ := cookieStore.Get(r, SessionName)
    
      if s.IsNew {
    
        _, ok := s.Values[sessionUserKey]
    
        if !ok {
    
          log.Debugf(c, "adding user to session")
          s.Values[sessionUserKey] = *u
          err := s.Save(r, w)
          if err != nil {
            log.Errorf(c, "Cannot save session: %s", err)
          }
    
        } else {
          log.Debugf(c, "user already exists in session")
        }
      }
    

    and here is the code that fails to delete the users session: (3)

      c := appengine.NewContext(r)
    
      s, _ := cookieStore.Get(r, SessionName)
      s.Options = &sessions.Options{
        MaxAge: -1,
      } 
      // s.Options.MaxAge = -1
      // Alternate syntax I've tried, exactly the same behavior.
      err := s.Save(r, w)
      if err != nil {
        log.Errorf(appengine.NewContext(r), "Cannot save session: %s", err)
      }
    
      v, ok := s.Values[sessionUserKey]
      if !ok {
        log.Debugf(c, "no user found in current session")
      } else {
        log.Debugf(c, "found user: %v", v)
      }
    
      http.SetCookie(w, &http.Cookie{Name: GtokenCookieName, MaxAge: -1})
    
    

    The behavior is oddly inconsistent, if I attempt to delete the session immediately after creating it, it will deleted, but if I then create another session by signing in to my site with the same user account, subsequent attempts to delete the session will fail. If repeatedly run code segment (3) it will log a user object every time.

    I have found a few circumstances in which the code will appear to delete the session, but upon running it again the session will reappear, Not sure what that says about the underlying issue, but it is peculiar.

  • Third Party Stores Not Applying Options to Codecs correctly.

    Third Party Stores Not Applying Options to Codecs correctly.

    We just upgraded this package and it's not setting cookies correctly. It's setting stuff like _ga=GA1.1.922831813.14264788986 instead of the regular base64 encoded cookie data. Any idea whats going on or when this broke so we can revert back to a working version? Thanks!

  • IsNew is always true

    IsNew is always true

    I'm really sorry to ask this question. I've spent days on this.

    So far I've tried Cookie sessions and FireStore Sessions. No matter what I do, Is New is always true It's a pretty straight forward web site. Session.Get is called to get or create the session.

    Soon after entering the site, a WebSocket is created and the connection is put into a struct. type socket struct { conn *websocket.Conn session *sessions.Session httpRequest *http.Request httpWriter http.ResponseWriter }

    I do this so I can access the session and httpRequest after the websocket is created. The reason I need the session is I use the ID. I need to know who each websocket is for so I can route to them when needed and delete them from the "online" list when done.

    Because of that, when a websocket is connected, I save certain data into the session each time. Meaning, session.Save is run.

    As I refresh the webpage, I can see new sessions just piling up.

    Versions

    Go version: 1.14

    package version: 15ff3511704639ab26f7843f780c015f4bf49565 "Show me the code!"

    client := socket{}
    	client.conn, err = upgrader.Upgrade(w, r, nil)
    	if err != nil {
    		http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
    	}
    
    	client.session, err = getSession(w, r)
    	client.httpRequest = r
    	client.httpWriter = w
    
    func init() {
    	ctx := context.Background()
    	var err error
    	sessionStore, err = firestoregorilla.New(ctx, firestoreClient)
    	if err != nil {
    		log.Panicf("Failed to connect to the session store: %v", err)
    	}
    }
    func getSession(w http.ResponseWriter, r *http.Request) (*sessions.Session, error) {
    	session, err := sessionStore.Get(r, "sessions")
    	checkErr(err)
    	if session.IsNew == true {
    		log.Println("New Session")
    		session.Values["authenticated"] = 0
    		session.Values["email"] = ""
    		t := time.Now()
    		session.Values["created"] = t.String()
    		session.Values["updated"] = t.String()
    		if err := session.Save(r, w); err != nil {
    			log.Printf("Save: %v", err)
    			// Don't return early so the user still gets a response.
    		}
    	} else {
    		log.Println("Got old session")
    	}
    	return session, err
    
    }
    
    s.Values["authenticated"] = 1
    s, err := getSession(client.httpWriter, client.httpRequest) //not using client.session because I'm just sorting out why session is always new.
    if err := s.Save(client.httpRequest, client.httpWriter); err != nil {
    			log.Printf("Error Saving Session: %v", err)
    }
    

    THANK YOU!

  • Garbage in, silent failing ...

    Garbage in, silent failing ...

    Two cases where I'd like things to fail harder/earlier (maybe if in some development mode.)

    Per suggestion I pass the session key in from the environment. Once I failed to set it in the environment and sessions failed silently. Should the NewCookieStore method reject an empty/missing or mis-lengthed string, maybe panic'ing? It would've saved me a lot of head scratching. :-) I added code like below, but hopefully you'd check the multiple key length options:

    var sessionKey = []byte(os.Getenv("SESSION_KEY"))

    // Cannot run without a session key...
    if 0 == len(sessionKey) {
    	panic("Wot no SESSION_KEY?")
    }
    if 16 != len(sessionKey) {
    	panic("Wot sort of SESSION_KEY?")
    }
    

    Additionally I didn't look at the signature of Save(w,r) and didn't know (for too long) that it was failing. Again, maybe in some development mode, it could panic failing faster.

  • How to persiste cookies created with gorilla/sessions?

    How to persiste cookies created with gorilla/sessions?

    I'm creating a sign in system, where after logging in client receives a session. The problem is, the value of the cookie is changing every time I send a request (using Postman to test the API). Is it a thing with gorilla/sessions that it doesn't refresh session and creates new one, or is it something on my side?

  • Add Close() to Store interface.

    Add Close() to Store interface.

    Adding a Close() function to the Store interface facilitates cleanup of custom backends without breaking from the API. If maintainers are in support, I can put together a PR.

  • securecookie: error - caused by: gob: name not registered for interface:

    securecookie: error - caused by: gob: name not registered for interface: "map[string]map[int]string"

    Problem

    Hi! I was trying to run my code, but it appears this error: securecookie: error - caused by: gob: name not registered for interface: "map[string]map[int]string" I've already tried to register the interface with: gob.Register(map[string](map[int]string){}) But I don't know if it's correct

    Versions

    Go version: go version go1.18.1 linux/amd64

    Code

    Here is the part of the code that fails:

    var proy_inst map[string](map[int]string)
    proy_inst = make(map[string](map[int]string))
    for ID_Proyecto, Name_Proyecto := range modelo.GetProyectos_de_Usuario(user.ID) {
      proy_inst[Name_Proyecto] = make(map[int]string)
      if (proy == modelo.Proyecto{}) {
        proy = modelo.GetProyecto_ID(ID_Proyecto)
      }
      for ID_Instalacion, Name_Instalacion := range modelo.GetInstalaciones_Proyecto(ID_Proyecto) {
        proy_inst[Name_Proyecto][ID_Instalacion] = Name_Instalacion
      }
    }
    
    gob.Register(map[string](map[int]string){})
    session.Values["menu"] = proy_inst
    session.Values["section"] = "proyecto"
    err = session.Save(r, w)
    checkErr(err)
    

    Thanks!

  • redigo: get on closed pool

    redigo: get on closed pool

    // Add a value session.Values["foo"] = "bar"

    // Save session
    if err = sessions.Save(req, w); err != nil {
    	log.Fatal("failed saving session: ", err)
    }
    

    when the "bar" is interface{},the session.save is err,why?

  • [bug] SameSite is not set in the default path

    [bug] SameSite is not set in the default path

    The SameSite patch in #165 and #170 forgot to initialize SameSite to a value in the default path.

    I think the intent was to initialize it to http.SameSiteDefaultMode.

    Currently this results in the following error in Firefox Developer Tools

    Cookie “id” will be soon rejected because it has the “SameSite” attribute set to “None” or an invalid value, without the “secure” attribute. To know more about the “SameSite“ attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite
    
  • Don't propagate

    Don't propagate "not exist" error if trying to erase a session matchi…

    Summary of Changes

    Don't consider "oserror.ErrNotExist" as a failure when trying to erase a session which corresponds to a missing file.

    Hello, in the current implementation if a request contains a session token that has been stored in a file that has been deleted the and we're trying to erase it using the Options.MaxAge = -1 workaround, the action still fails, because the missing file error gets propagated higher in the stack.

    This small fix prevents this, and ensures that the session is regenerated.


    This is a reopen of #237 which was closed by the stale bot.

  • Changed auth/encryption key prevents the signed in user from accessing the webpage again

    Changed auth/encryption key prevents the signed in user from accessing the webpage again

    Describe the bug

    When a single pair of an authentication key and encryption key is used - changing it prevents the signed in user from accessing the webpage again.

    Versions

    Go version: 1.16.2 linux/amd64 package version: 15ff3511704639ab26f7843f780c015f4bf49565

    Steps to Reproduce

    I create a sessions Mongostore with:

    store, err := mongostore.NewStore(
    	db.Sessions,
    	http.Cookie{
    		Path:     "/",
    		Domain:   "",
    		MaxAge:   3600,
    		Secure:   true,
    		HttpOnly: true,
    		SameSite: http.SameSiteStrictMode,
    	},
    	securecookie.GenerateRandomKey(64),
    	securecookie.GenerateRandomKey(32),
    )
    

    When user requests the webpage check whether he is signed in:

    fmt.Println(1)
    session, err := store.Get(r, "CookieName")
    fmt.Println(2)
    

    store.Get calls a function from your package:

    return sessions.GetRegistry(r).Get(s, name)
    

    I've added a debug printing to this function:

    func (s *Registry) Get(store Store, name string) (session *Session, err error) {
    	fmt.Println(200, name)
    	if !isCookieNameValid(name) {
    		return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name)
    	}
    	if info, ok := s.sessions[name]; ok {
    		session, err = info.s, info.e
    	} else {
    		fmt.Println(201)
    		session, err = store.New(s.request, name)
    		fmt.Printf("202 %#v\n", session)
    		fmt.Printf("203 %#v\n", err)
    		fmt.Printf("204 %#v\n", session.name)
    		session.name = name
    		fmt.Println(205)
    		s.sessions[name] = sessionInfo{s: session, e: err}
    		fmt.Println(206)
    	}
    	session.store = store
    	return
    }
    

    I sign in to the website. When service is restarted - new pair of an authentication key and encryption key is generated. I refresh the webpage - server returns literally nothing, empty page, no Go errors. In a console I see only:

    1
    200 sessionID
    201
    202 (*sessions.Session)(nil)
    203 &fmt.wrapError{msg:"[ERROR] decoding cookie: securecookie: the value is not valid", err:securecookie.MultiError{securecookie.cookieError{typ:2, msg:"the value is not valid", cause:error(nil)}}}
    

    .. that means fmt.Printf("204") and the later code is never called for some reason, feels like current goroutine is stuck or being dropped.

    The only thing that helps - clearing browser's cookies for this website manually. Please make func (s *Registry) Get() to return something even if it can't decode cookies.

    Expected behavior

    When service is restarted - new pair of an authentication key and encryption key is generated, sessions package informs that user is not signed in and returns an error that can't decode cookies. User has to sign in again.

  • flash messages are not lost and are always saved when using gorilla mux

    flash messages are not lost and are always saved when using gorilla mux

    I use gorilla mux. When I access the flash message. The flash message is always stored and cannot be lost. Is there any solution ?

    var SessionCookie = newCookieStore()
    
    func newCookieStore() *sessions.CookieStore {
    	authKey := []byte("my-auth-key-very-secret")
    	encryptionKey := []byte("my-encryption-key-very-secret123")
    
    	store := sessions.NewCookieStore(authKey,encryptionKey)
    	store.Options.Path = "/"
    	store.Options.MaxAge = 86400 * 7 //expired dalam seminggu
    	store.Options.HttpOnly = true
    
    	return store
    }
    
    func SetFlashdata(w http.ResponseWriter, r *http.Request, name, value string){
    	session, _ := SessionCookie.Get(r, "fmessages")
    	session.AddFlash(value, name)
    
    	session.Save(r, w)
    }
    
    func GetFlashdata(w http.ResponseWriter, r *http.Request, name string) []string {
    	
    	session, _ := SessionCookie.Get(r, "fmessages")
    	fm := session.Flashes(name)
    	//IF we have some message
    
    	if len(fm) > 0 {
    		session.Save(r, w)
    		//initiate a strings slice to return messages
    		var flashes []string 
    		for _, fl := range fm {
    			//Add message to the slice
    			flashes = append(flashes, fl.(string))
    		}
    		
    		return flashes
    	}
    	
    	return nil
    }
    
Cookie - Cookie helper functions for golang

Cookie Cookie helper functions. package main import ( "net/http" "github.co

Feb 10, 2022
golang csrf react example, using gorilla/mux and gorilla/mux

Demo REST backend Gorilla csrf middleware and Js frontend Use gorilla/mux and gorilla/csrf How to run open goland IDE, run middleware_test.go by click

Feb 2, 2022
Advent of Code Input Loader, provide a session cookie and a problem date, returns a string or []byte of the input

Advent of Code Get (aocget) A small lib to download your puzzle input for a given day. Uses your session token to authenticate to obtain your personal

Dec 9, 2021
🍍Jeff provides the simplest way to manage web sessions in Go.

jeff A tool for managing login sessions in Go. Motivation I was looking for a simple session management wrapper for Go and from what I could tell ther

Jan 4, 2023
Fast, secure and efficient secure cookie encoder/decoder

Encode and Decode secure cookies This package provides functions to encode and decode secure cookie values. A secure cookie has its value ciphered and

Dec 9, 2022
Goauth - Basic username password cookie based authentication with Go Lang

goauth [WIP] Basic username password cookie based authentication with Go Lang Overview Use a Postgres DB to store Sign-in and Sign-up info Redis for c

Jan 4, 2022
Cocos2d-x texture unpacker, primarily for Cookie Run.

boofunpack Cocos2d-x texture unpacker, primarily for Cookie Run: OvenBreak and Cookie Run for Kakao/LINE (though it likely works for other .plist form

Oct 11, 2022
JWT login microservice with plugable backends such as OAuth2, Google, Github, htpasswd, osiam, ..
JWT login microservice with plugable backends such as OAuth2, Google, Github, htpasswd, osiam, ..

loginsrv loginsrv is a standalone minimalistic login server providing a JWT login for multiple login backends. ** Attention: Update to v1.3.0 for Goog

Dec 24, 2022
Jan 9, 2023
JWT login microservice with plugable backends such as OAuth2, Google, Github, htpasswd

login-service login-service is a standalone minimalistic login server providing a (JWT)[https://jwt.io/] login for multiple login backends. Abstract l

Feb 12, 2022
A dead simple, highly performant, highly customizable sessions middleware for go http servers.

If you're interested in jwt's, see my jwt library! Sessions A dead simple, highly performant, highly customizable sessions service for go http servers

Dec 19, 2022
makes it easy to keep track of user sessions on a Go API.

usersession is a simple way to keep track of user information on a Go API. it assigns a session ID and gives you a place to store the IP and some user

Dec 22, 2021
Auth Go microservice for managing authentication sessions

cryptomath-go-auth Auth Go microservice for managing authentication sessions. Install dependencies $ make deps Build $ make vendor $ make build Databa

Mar 4, 2022
🍪CookieMonster is a command-line tool and API for decoding and modifying vulnerable session cookies from several different frameworks.

?? CookieMonster CookieMonster is a command-line tool and API for decoding and modifying vulnerable session cookies from several different frameworks.

Jan 8, 2023
An imaginary authentication and session tracking service that is defined in this Apiary

Userland This repository contains impelementation of "Userland" on boarding project Userland is an imaginary authentication and session tracking servi

Dec 5, 2021
HTTP Session Management for Go

SCS: HTTP Session Management for Go Features Automatic loading and saving of session data via middleware. Choice of server-side session stores includi

Jan 1, 2023
Go session management for web servers (including support for Google App Engine - GAE).

Session The Go standard library includes a nice http server, but unfortunately it lacks a very basic and important feature: HTTP session management. T

Oct 10, 2022
Straightforward HTTP session management

sessionup ?? Simple, yet effective HTTP session management and identification package Features Effortless session management: Initialization. Request

Oct 10, 2022
Go (lang) HTTP session authentication

Go Session Authentication See git tags/releases for information about potentially breaking change. This package uses the Gorilla web toolkit's session

Dec 22, 2022