HTTP Session Management for Go

SCS: HTTP Session Management for Go

GoDoc Build status Go report card Test coverage

Features

  • Automatic loading and saving of session data via middleware.
  • Choice of server-side session stores including PostgreSQL, MySQL, Redis, BadgerDB and BoltDB. Custom session stores are also supported.
  • Supports multiple sessions per request, 'flash' messages, session token regeneration, idle and absolute session timeouts, and 'remember me' functionality.
  • Easy to extend and customize. Communicate session tokens to/from clients in HTTP headers or request/response bodies.
  • Efficient design. Smaller, faster and uses less memory than gorilla/sessions.

Instructions

Installation

This package requires Go 1.12 or newer.

$ go get github.com/alexedwards/scs/v2

Note: If you're using the traditional GOPATH mechanism to manage dependencies, instead of modules, you'll need to go get and import github.com/alexedwards/scs without the v2 suffix.

Please use versioned releases. Code in tip may contain experimental features which are subject to change.

Basic Use

SCS implements a session management pattern following the OWASP security guidelines. Session data is stored on the server, and a randomly-generated unique session token (or session ID) is communicated to and from the client in a session cookie.

package main

import (
	"io"
	"net/http"
	"time"

	"github.com/alexedwards/scs/v2"
)

var sessionManager *scs.SessionManager

func main() {
	// Initialize a new session manager and configure the session lifetime.
	sessionManager = scs.New()
	sessionManager.Lifetime = 24 * time.Hour

	mux := http.NewServeMux()
	mux.HandleFunc("/put", putHandler)
	mux.HandleFunc("/get", getHandler)

	// Wrap your handlers with the LoadAndSave() middleware.
	http.ListenAndServe(":4000", sessionManager.LoadAndSave(mux))
}

func putHandler(w http.ResponseWriter, r *http.Request) {
	// Store a new key and value in the session data.
	sessionManager.Put(r.Context(), "message", "Hello from a session!")
}

func getHandler(w http.ResponseWriter, r *http.Request) {
	// Use the GetString helper to retrieve the string value associated with a
	// key. The zero value is returned if the key does not exist.
	msg := sessionManager.GetString(r.Context(), "message")
	io.WriteString(w, msg)
}
$ curl -i --cookie-jar cj --cookie cj localhost:4000/put
HTTP/1.1 200 OK
Cache-Control: no-cache="Set-Cookie"
Set-Cookie: session=lHqcPNiQp_5diPxumzOklsSdE-MJ7zyU6kjch1Ee0UM; Path=/; Expires=Sat, 27 Apr 2019 10:28:20 GMT; Max-Age=86400; HttpOnly; SameSite=Lax
Vary: Cookie
Date: Fri, 26 Apr 2019 10:28:19 GMT
Content-Length: 0

$ curl -i --cookie-jar cj --cookie cj localhost:4000/get
HTTP/1.1 200 OK
Date: Fri, 26 Apr 2019 10:28:24 GMT
Content-Length: 21
Content-Type: text/plain; charset=utf-8

Hello from a session!

Configuring Session Behavior

Session behavior can be configured via the SessionManager fields.

sessionManager = scs.New()
sessionManager.Lifetime = 3 * time.Hour
sessionManager.IdleTimeout = 20 * time.Minute
sessionManager.Cookie.Name = "session_id"
sessionManager.Cookie.Domain = "example.com"
sessionManager.Cookie.HttpOnly = true
sessionManager.Cookie.Path = "/example/"
sessionManager.Cookie.Persist = true
sessionManager.Cookie.SameSite = http.SameSiteStrictMode
sessionManager.Cookie.Secure = true

Documentation for all available settings and their default values can be found here.

Working with Session Data

Data can be set using the Put() method and retrieved with the Get() method. A variety of helper methods like GetString(), GetInt() and GetBytes() are included for common data types. Please see the documentation for a full list of helper methods.

The Pop() method (and accompanying helpers for common data types) act like a one-time Get(), retrieving the data and removing it from the session in one step. These are useful if you want to implement 'flash' message functionality in your application, where messages are displayed to the user once only.

Some other useful functions are Exists() (which returns a bool indicating whether or not a given key exists in the session data) and Keys() (which returns a sorted slice of keys in the session data).

Individual data items can be deleted from the session using the Remove() method. Alternatively, all session data can de deleted by using the Destroy() method. After calling Destroy(), any further operations in the same request cycle will result in a new session being created --- with a new session token and a new lifetime.

Behind the scenes SCS uses gob encoding to store session data, so if you want to store custom types in the session data they must be registered with the encoding/gob package first. Struct fields of custom types must also be exported so that they are visible to the encoding/gob package. Please see here for a working example.

Loading and Saving Sessions

Most applications will use the LoadAndSave() middleware. This middleware takes care of loading and committing session data to the session store, and communicating the session token to/from the client in a cookie as necessary.

If you want to customize the behavior (like communicating the session token to/from the client in a HTTP header, or creating a distributed lock on the session token for the duration of the request) you are encouraged to create your own alternative middleware using the code in LoadAndSave() as a template. An example is given here.

Or for more fine-grained control you can load and save sessions within your individual handlers (or from anywhere in your application). See here for an example.

Configuring the Session Store

By default SCS uses an in-memory store for session data. This is convenient (no setup!) and very fast, but all session data will be lost when your application is stopped or restarted. Therefore it's useful for applications where data loss is an acceptable trade off for fast performance, or for prototyping and testing purposes. In most production applications you will want to use a persistent session store like PostgreSQL or MySQL instead.

The session stores currently included are shown in the table below. Please click the links for usage instructions and examples.

Package
badgerstore BadgerDB based session store
boltstore BoltDB based session store
memstore In-memory session store (default)
mysqlstore MySQL based session store
postgresstore PostgreSQL based session store (using the pq driver)
pgxstore PostgreSQL based session store (using the pgx driver)
redisstore Redis based session store
sqlite3store SQLite3 based session store

Custom session stores are also supported. Please see here for more information.

Using Custom Session Stores

scs.Store defines the interface for custom session stores. Any object that implements this interface can be set as the store when configuring the session.

type Store interface {
	// Delete should remove the session token and corresponding data from the
	// session store. If the token does not exist then Delete should be a no-op
	// and return nil (not an error).
	Delete(token string) (err error)

	// Find should return the data for a session token from the store. If the
	// session token is not found or is expired, the found return value should
	// be false (and the err return value should be nil). Similarly, tampered
	// or malformed tokens should result in a found return value of false and a
	// nil err value. The err return value should be used for system errors only.
	Find(token string) (b []byte, found bool, err error)

	// Commit should add the session token and data to the store, with the given
	// expiry time. If the session token already exists, then the data and
	// expiry time should be overwritten.
	Commit(token string, b []byte, expiry time.Time) (err error)
}

Preventing Session Fixation

To help prevent session fixation attacks you should renew the session token after any privilege level change. Commonly, this means that the session token must to be changed when a user logs in or out of your application. You can do this using the RenewToken() method like so:

func loginHandler(w http.ResponseWriter, r *http.Request) {
	userID := 123

	// First renew the session token...
	err := sessionManager.RenewToken(r.Context())
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// Then make the privilege-level change.
	sessionManager.Put(r.Context(), "userID", userID)
}

Multiple Sessions per Request

It is possible for an application to support multiple sessions per request, with different lifetime lengths and even different stores. Please see here for an example.

Compatibility

This package requires Go 1.12 or newer.

You may have some problems using this package with the Echo framework. If you do, then please consider using the Echo session manager instead.

Comments
  • Add Firestore support

    Add Firestore support

    I'd like to use Firestore as database for session management.

    I saw that Jens-Uwe already worked on Firestore support (see https://github.com/jum/scs/commit/dd23a3e6d1ff63ed70f5020efeea25d01dde50ad), but since there's no PR yet I thought we could track it in a separate issue until something is merged.

  • Echo compatibility

    Echo compatibility

    Well done @alexedwards on re-imagining scs. :1st_place_medal: This was a comprehensive rewrite. A number of items I appreciate:

    • Status
    • Efficient load/save with help from Status
    • Session and SessionCookie are global in scope
    • I also like that IdleTimeout and Lifetime were split from the old options.
    • go.mod added for go modules support

    For me, v2 solves a number of issues for which I had kept a permanent local fork of scs on my machine. (My fork was getting a bit dated too!)

    I've done some testing and so far so good. I had one issue with a private token but this example provides a powerful solution to my issue.

  • Improve storage to support exclusive locks

    Improve storage to support exclusive locks

    As I can see, the redis storage implementation doesn't support any locks. There's no documentation on mechanism to how to do this right. The current Store interface doesn't provide any locks interface even for custom implementations.

    So, I'd really like to see this change so one could implement their own locks (until someone makes some library).

    For example, there's only one implementation of locks I've found (a simple one): https://github.com/bsm/redis-lock

  • Provide example of using SCS with Echo

    Provide example of using SCS with Echo

    Should scs include a folder dedicated to middleware examples? It would be similar to stores but dedicated to middleware for scs.

    Or would it be better for scs to make reference to third party middleware implementers on its README?

    It's a classic question. Either direction works for me primarily b/c scs doesn't have many open issues. If scs was highly trafficked in terms of issues, I'd probably suggest breaking-out the middleware to third parties.

    I have some echo middleware to contribute - just let me know what you'd prefer. Others may find a landing spot for their ideas too. (see #16)

  • panic in getSessionDataFromContext makes this package hard to work with

    panic in getSessionDataFromContext makes this package hard to work with

    Hello,

    I was a happy user of the v1 of this package, and recently decided to upgrade to v2, however, I found some of the new behaviors extremely difficult to work with.

    One piece in particular was the fact that the package throws a panic if you try and load a session from a request that doesn't have a session attached: getSessionDataFromContext. Previously, I could attempt to load a session blindly, and based on the response from the function, I'd know if the request had a session attached. Now, I have to make absolutely sure that I've got the LoadAndSave middleware inserted all over the place.

    Panics thrown from userland are pretty unidiomatic in go, I feel, and the only way to really handle them is by doing the defer-recover dance, which is messy and error-prone. I'm likely going to migrate to another session-management library.

  • Feature Request: enumerate all sessions

    Feature Request: enumerate all sessions

    In our application, want to implement a feature that is to view all online users and terminate a user session. I tried to get all the sessions to complete this feature, but did not find any method.

    If using dbStore, I can get it directly from the database, but we use a memstore. I tried to read * MemStore directly, but his items field is private.

    Hope to add a interface that can enumerate all sessions, which is very useful.

  • Multiple Set-Cookie headers in response

    Multiple Set-Cookie headers in response

    Here is small example:

    package main
    
    import (
    	"net/http"
    
    	"github.com/alexedwards/scs"
    )
    
    var sessionManager = scs.NewCookieManager("u46IpCV9y5Vlur8YvODJEhgOY8m9JVE4")
    
    func main() {
    	mux := http.NewServeMux()
    	mux.HandleFunc("/auth", login)
    
    	http.ListenAndServe(":4000", sessionManager.Use(mux))
    }
    
    func login(w http.ResponseWriter, r *http.Request) {
    	session := sessionManager.Load(r)
            
            // authenticate user ...
    
    	err := session.RenewToken(w)
    	if err != nil {
    		http.Error(w, err.Error(), 500)
                    return
    	}
    
    	if err := session.PutInt(w, "userId", 1); err != nil {
    		http.Error(w, err.Error(), 500)
    		return
    	}
    
    	if err := session.PutBool(w, "isAdmin", true); err != nil {
    		http.Error(w, err.Error(), 500)
    		return
    	}
    }
    

    Here is /auth response:

    HTTP/1.1 200 OK
    Set-Cookie: session=rXoTm-uiZM8eYszKWKTbUP-D-SBV0Pdx8DyMpX7jL55yVcOwRxLROJSeDHeuW0iYifwVpUnEiXyhU_H-Vl-1-2LpnjHnOvx1TS1yYuoccQP6P56iEXyCzngQRt_UkHGG5Wva5-0; Path=/; HttpOnly
    Set-Cookie: session=6HjYzvYsALD_UvkBNlrheCKijlQDAAd2rMsWN_URq7uO12n2ng2t-7PYSmHSV8l3n0TKtb6_y0-DKuc--uikxCBJ-NuvR0a91vj7dauPu_TMrCGtfa1cOgLpr1R2MhTCDt4qUNwmBNEblJrViAiW; Path=/; HttpOnly
    Set-Cookie: session=ewJmYE3psbCYtX39Zgj-Utv5XTjCJ5SfbsO32daXPyOovU0y0O2OPQtv6QlL9Zv-yZ-XJuYqPvkZQ5tt_tjLqtpKdvohTOLAjLp7XO9yfVjV5rgtC6hG5b9W0Hb_8shOUdZdKZdM936IEAbaRkltlDOEYqBSbFcXoZIjJUKs; Path=/; HttpOnly
    Date: Thu, 26 Oct 2017 09:08:01 GMT
    Content-Length: 0
    Content-Type: text/plain; charset=utf-8
    

    As you can see there are three different Set-Cookie headers with the same cookie-name in response which is wrong according to RFC 6265:

    Servers SHOULD NOT include more than one Set-Cookie header field in the same response with the same cookie-name.

  • Implement pgxstore

    Implement pgxstore

    pgxstore is a session store for postgresql based on the github.com/jackc/pgx package. The implementation is almost identical to the existing postgresstore package, with the only difference being the postgresql driver.

  • go get github.com/alexedwards/scs/v2 fails

    go get github.com/alexedwards/scs/v2 fails

    go get github.com/alexedwards/scs/v2 package github.com/alexedwards/scs/v2: cannot find package "github.com/alexedwards/scs/v2" in any of: /usr/local/go/src/github.com/alexedwards/scs/v2 (from $GOROOT) /home/wise/go/src/github.com/alexedwards/scs/v2 (from $GOPATH)

    I'm moving a site I have in development to a new machine. I have the site working on another machine, I last set that one up maybe a week ago.

    What's the easiest way to use scs and postgresql?

  • Memcached support?

    Memcached support?

    Looking around for a new session manager, one which supports memcached.

    SCS looks interesting and is written for modern Go (unlike Gorilla), but doesn't (yet) support memcached.

    Any plans on memcached support? :smile:

  • Using scs with Echo

    Using scs with Echo

    Is there a way to use the middle ware provided by scs within echo? I try to do as below way, but failed. Can you give me some suggestion?

    package main
    
    import (
        "github.com/labstack/echo"
        "github.com/labstack/echo/engine/standard"
        "github.com/labstack/echo/middleware"
        "github.com/alexedwards/scs/session"
        "github.com/alexedwards/scs/engine/memstore"
        "net/http"
    )
    
    func main() {
        sessionManager := session.Manage(memstore.New(0))
        e := echo.New()
        e.Use(middleware.Logger())
        e.Use(middleware.Recover())
        e.Use(standard.WrapMiddleware(sessionManager))
            e.SetDebug(true)
    
        e.GET("/", func(c echo.Context) error {
            err := session.PutString(c.Request().(*standard.Request).Request, "username", "admin")
            if err != nil {
                c.Logger().Error("session.PutString:", err)
                return err
            }
    
            if msg, err := session.GetString(c.Request().(*standard.Request).Request, "username"); err != nil || len(msg) == 0 {
                c.Logger().Info("session.GetString:", msg)
            }
    
            return c.String(http.StatusOK, "Hello, World!")
        })
    
        e.Run(standard.New(":1323"))
    }
    
    
  • Add a method to modify the deadline of the sessionData

    Add a method to modify the deadline of the sessionData

    At the moment every session has a fixed lifetime which is set on the initialization of the session manager. There is no way to modify the deadline for an individual session after.

    It would be useful if a method can be exposed to set the expiry for cases when the deadline differs from the default or modified due to other conditions.

    func (s *SessionManager) Expire(ctx context.Context, expiry time.Time) {
    	sd := s.getSessionDataFromContext(ctx)
    
    	sd.mu.Lock()
    	defer sd.mu.Unlock()
    	sd.deadline = expiry
    	sd.status = Modified
    }
    

    Let me know if I should proceed with a PR

  • Prevent gorm from logging a warning when record is not found.

    Prevent gorm from logging a warning when record is not found.

    When using the First function, gorm will log that it failed to find a record. Since this case is valid in scs it makes no sense to log it. By using different syntax the same functionality is achieved but without the logging.

  • http.Flusher compatiability

    http.Flusher compatiability

    Hello,

    I want to implement Server Sent Events and am using this library: https://github.com/r3labs/sse The library expects the http.ResponseWriter to be able to cast to a http.Flusher for flushing.

    The http.Flusher documentation states:

    // The default HTTP/1.x and HTTP/2 ResponseWriter implementations
    // support Flusher, *but ResponseWriter wrappers may not*. Handlers
    // should always test for this ability at runtime.
    

    Your library is wrapping http.ResponseWriter and is breaking the library. Would it be possible for you to make the bufferedResponseWriter implement the http.Flusher?

    References: https://github.com/r3labs/sse/issues/130

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
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
Validate Django auth session in Golang

GoDjangoSession Valid for django 3.0.5 Usage: package main import ( "encoding/base64" "fmt" "session/auth" "github.com/Kuzyashin/GoDjangoSession"

Aug 23, 2022
Package gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends.

sessions gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends. The key features are: Simple API: us

Dec 28, 2022
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
Auth Middleware for session & white-listed routing

Auth Middleware for session & white-listed routing

Nov 4, 2021
🍪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
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
Ginx - Evilginx2 - A man-in-the-middle attack framework used for phishing login credentials along with session cookies
Ginx - Evilginx2 - A man-in-the-middle attack framework used for phishing login credentials along with session cookies

evilginx2 is a man-in-the-middle attack framework used for phishing login creden

Mar 19, 2022
Basic and Digest HTTP Authentication for golang http

HTTP Authentication implementation in Go This is an implementation of HTTP Basic and HTTP Digest authentication in Go language. It is designed as a si

Dec 22, 2022
HTTP-server-with-auth# HTTP Server With Authentication

HTTP-server-with-auth# HTTP Server With Authentication Introduction You are to use gin framework package and concurrency in golang and jwt-go to imple

Nov 9, 2022
simple-jwt-provider - Simple and lightweight provider which exhibits JWTs, supports login, password-reset (via mail) and user management.

Simple and lightweight JWT-Provider written in go (golang). It exhibits JWT for the in postgres persisted user, which can be managed via api. Also, a password-reset flow via mail verification is available. User specific custom-claims also available for jwt-generation and mail rendering.

Dec 18, 2022
Simple authentication and books management with GoFiber

Simple authentication and books management with GoFiber Simple authentication system with gofiber. Endpoints GET /api - Welcome message POST /api/auth

Nov 27, 2022
BK-IAM is a centralized permission management service provided by The Tencent BlueKing; based on ABAC

(English Documents Available) Overview 蓝鲸权限中心(BK-IAM)是蓝鲸智云提供的集中权限管理服务,支持基于蓝鲸开发框架的SaaS和企业第三方系统的权限控制接入,以及支持细粒度的权限管理。 架构设计 代码目录 Features 蓝鲸权限中心是基于 ABAC 强

Nov 16, 2022
Backend Development Rest Api Project for book management system. Used Features like redis, jwt token,validation and authorization.

Golang-restapi-project Simple Rest Api Project with Authentication, Autherization,Validation and Connection with redis File Structure ├── cache │ ├──

May 25, 2022
Golang based User creation and Management application. GORM, Fiber, JWT

User Creation and Management app (BACK-END) Auth Features: Create Clients (regular password + 6 one-time passwords + (optional) QR code and Secret for

Dec 2, 2022
The boss of http auth.
The boss of http auth.

Authboss Authboss is a modular authentication system for the web. It has several modules that represent authentication and authorization features that

Jan 6, 2023
HTTP Authentication middlewares

goji/httpauth httpauth currently provides HTTP Basic Authentication middleware for Go. It is compatible with Go's own net/http, goji, Gin & anything t

Dec 23, 2022
This package provides json web token (jwt) middleware for goLang http servers

jwt-auth jwt auth middleware in goLang. If you're interested in using sessions, checkout my sessions library! README Contents: Quickstart Performance

Dec 5, 2022