simplesessions

package module
v3.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 1, 2024 License: MIT Imports: 8 Imported by: 7

README

simplesessions

simplesessions is a "pure" Go session library that is completely agnostic of HTTP libraries and frameworks, backend stores, and even cookie jars.

Why?

Most session libraries are highly opinionated and hard-wired to work with net/http handlers (or other 3rd party libraries like fasthttp) and take liberties on how session data should be encoded and stored. simplesessions takes a pragmatic approach, where everything from HTTP request and cookie handling to data encoding and session storage are plugged in as simple callback functions. Moreover, most session libraries treat data as strings losing type information. simplessions provides a way to maintain primitive types such as int, string etc.

Features

  1. Framework/network library agnostic.
  2. Simple API and with support for primitive data types. Complex types can be stored using own encoding/decoding.
  3. Pre-built redis/postgres/in-memory/securecookie stores that can be separately installed.
  4. Multiple session instances with custom handlers and different backend stores.

Installation

Install simplesessions and all available stores.

go get -u github.com/zerodha/simplesessions/v3

# Install the requrired store: memory|redis|postgres|securecookie
go get -u github.com/zerodha/simplesessions/v3/stores/redis
go get -u github.com/zerodha/simplesessions/v3/stores/postgres

Stores

Sessions can be stored to any backend by implementing the store interface. The following stores are bundled.

Usage

Check the examples directory for complete examples.

Connecting a store

Stores can be registered to a session instance by using Use method. Check individual Stores docs for more details.

sess := simplesessions.New(simplesessions.Options{})
sess.UseStore(store.New())

Connecting an HTTP handler

Any HTTP library can be connected to simplesessions by registering the get and set cookie hooks using SetCookieHooks(). The below example shows a simple net/http usecase. Another example showing fasthttp can be found here.

var sessMan *simplesessions.Manager

func getCookie(name string, r interface{}) (*http.Cookie, error) {
	// Get read interface registered using `Acquire` method in handlers.
	rd := r.(*http.Request)

	// Send cookie for received cookie name from request.
	// Note that other networking libs and frameworks should
	// also send back cookie in net/http cookie format.
	// If cookie is not found for given cookie name then
	// `http.ErrNoCookie` should be returned.
	// Cookie name is what you set while creating session manager
	// with custom options (`Options.CookieName`). Defaults to `session`.
	cookie, err := rd.Cookie(name)
	if err != nil {
		return nil, err
	}

	return cookie, nil
}

func setCookie(cookie *http.Cookie, w interface{}) error {
	// Get write interface registered using `Acquire` method in handlers.
	wr := w.(http.ResponseWriter)

	// net/http cookie is returned which can be
	// used to set cookie for current request.
	// Note that other network libraries or
	// framework will also receive cookie as
	// net/http cookie and it has to set cookie accordingly.
	http.SetCookie(wr, cookie)
	return nil
}

func handler(w http.ResponseWriter, r *http.Request) {
	// Use method `Acquire` to acquire a session before you access the session.
	// Acquire takes read, write interface and context respectively.
	// Read interface sent to callback registered with get cookie hook
	// and write interface is sent to callback registered with write cookie hook
	// set using `SetCookieHooks()` method.
	//
	// Optionally `context` can be sent which is usually request context where acquire
	// session will get previously loaded session. This is useful if you have multiple
	// middlewares accessing sessions. New sessions will be created in first middleware which
	// does `Acquire` and will be reused in other places.
	//
	// If `Options.EnableAutoCreate` is set to True then if session doesn't exist it will
	// be immediately created and returned. Bydefault its set to False so if session doesn't
	// exist then `ErrInvalidSession` error is returned.
	sess, err := sessMan.Acquire(nil, r, w)

	// If session doesn't exist then create new session.
	// In a traditional login flow you can create a new session once user completes the login flow.
	if err == simplesessions.ErrInvalidSession {
		sess, err = sessMan.NewSession(r, w)
	}

	// Use 'Set` or `SetMulti` to set a field for session.
	err = sess.Set("somekey", "somevalue")
	err = sess.SetMulti(map[string]interface{}{
		"k1": "v1",
		"k2": "v2",
	})

	// Use `Get` method to get a field from current session. The result will be an interface
	// so you can use helper methods like
	// `String', `Int`, `Int64`, `UInt64`, `Float64`, `Bytes`, `Bool`.
	val, err := sess.String(sess.Get("somekey"))
	fmt.Println("val=", val)

	// Use `GetAll` to get map of all fields from session.
	// The result is map of string and interface you can use helper methods to type cast it.
	all, err := sess.GetAll()
	fmt.Println("all=", all)

	// Use `GetMulti` to get values for given fields from session.
	// The result is map of string and interface you can use helper methods to type cast it.
	// If key is not there then store should ideally send `nil` value for given key.
	vals, err := sess.GetMulti("somekey", "someotherkey")
	fmt.Println("vals=", vals)

	// Use `Delete` to delete a field from session.
	err = sess.Delete("somekey")

	// Use `Clear` to empty the session but to keep the session alive.
	err = sess.Clear()

	// Use `Destroy` to clear session from store and cookie.
	err = sess.Destroy()

	fmt.Fprintf(w, "success")
}

func main() {
	// Create a session manager with custom options like cookie name,
	// cookie domain, is secure cookie etc. Check `Options` struct for more options.
	sessMan := simplesessions.New(simplesessions.Options{
		// If set to true then `Acquire()` method will create new session instead of throwing
		// `ErrInvalidSession` when the session doesn't exist. By default its set to false.
		EnableAutoCreate: false,
		Cookie: simplesessions.CookieOptions{
			// Name sets http cookie name. This is also sent as cookie name in `GetCookie` callback.
			Name: "session",
			// Domain sets hostname for the cookie. Domain specifies allowed hosts to receive the cookie.
			Domain: "example.com",
			// Path sets path for the cookie. Path indicates a URL path that must exist in the requested URL in order to send the cookie header.
			Path: "/",
			// IsSecure marks the cookie as secure cookie (only sent in HTTPS).
			IsSecure: true,
			// IsHTTPOnly marks the cookie as http only cookie. JS won't be able to access the cookie so prevents XSS attacks.
			IsHTTPOnly: true,
			// SameSite sets allows you to declare if your cookie should be restricted to a first-party or same-site context.
			SameSite: http.SameSiteDefaultMode,
			// Expires sets absolute expiration date and time for the cookie.
			// If both Expires and MaxAge are sent then MaxAge takes precedence over Expires.
			// Cookies without a Max-age or Expires attribute – are deleted when the current session ends
			// and some browsers use session restoring when restarting. This can cause session cookies to last indefinitely.
			Expires: time.Now().Add(time.Hour * 24),
			// Sets the cookie's expiration in seconds from the current time, internally its rounder off to nearest seconds.
			// If both Expires and MaxAge are sent then MaxAge takes precedence over Expires.
			// Cookies without a Max-age or Expires attribute – are deleted when the current session ends
			// and some browsers use session restoring when restarting. This can cause session cookies to last indefinitely.
			MaxAge: time.Hour * 24,
		},
	})

	// Create a new store instance and attach to session manager
	sessMan.UseStore(memory.New())
	// Register callbacks for read and write cookie.
	// Get cookie callback should get cookie based on cookie name and
	// sent back in net/http cookie format.
	// Set cookie callback should set cookie it received for received cookie name.
	sessMan.SetCookieHooks(getCookie, setCookie)

	// Initialize the handler.
	http.HandleFunc("/", handler)
}

Documentation

Index

Constants

View Source
const (

	// ContextName is the key used to store session in context passed to acquire method.
	ContextName ctxNameType = "_simple_session"
)

Variables

View Source
var (
	// ErrInvalidSession is raised when session is tried to access before setting it or its not set in store.
	// Handle this and create new session.
	// Store code = 1
	ErrInvalidSession = errors.New("simplesession: invalid session")

	// ErrNil is raised when returned value is nil.
	// Store code = 2
	ErrNil = errors.New("simplesession: nil returned")

	// ErrAssertType is raised when type assertion fails
	// Store code = 3
	ErrAssertType = errors.New("simplesession: invalid type assertion")
)

Functions

This section is empty.

Types

type CookieOptions

type CookieOptions struct {
	// Name sets http cookie name. This is also sent as cookie name in `GetCookie` callback.
	Name string

	// Domain sets hostname for the cookie. Domain specifies allowed hosts to receive the cookie.
	Domain string

	// Path sets path for the cookie. Path indicates a URL path that must exist in the requested URL in order to send the cookie header.
	Path string

	// IsSecure marks the cookie as secure cookie (only sent in HTTPS).
	IsSecure bool

	// IsHTTPOnly marks the cookie as http only cookie. JS won't be able to access the cookie so prevents XSS attacks.
	IsHTTPOnly bool

	// SameSite sets allows you to declare if your cookie should be restricted to a first-party or same-site context.
	SameSite http.SameSite

	// Expires sets absolute expiration date and time for the cookie.
	// If both Expires and MaxAge are sent then MaxAge takes precedence over Expires.
	// Cookies without a Max-age or Expires attribute – are deleted when the current session ends
	// and some browsers use session restoring when restarting. This can cause session cookies to last indefinitely.
	Expires time.Time

	// Sets the cookie's expiration in seconds from the current time, internally its rounder off to nearest seconds.
	// If both Expires and MaxAge are sent then MaxAge takes precedence over Expires.
	// Cookies without a Max-age or Expires attribute – are deleted when the current session ends
	// and some browsers use session restoring when restarting. This can cause session cookies to last indefinitely.
	MaxAge time.Duration
}

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager handles the storage and management of HTTP cookies.

func New

func New(opts Options) *Manager

New creates a new session manager for given options.

func (*Manager) Acquire

func (m *Manager) Acquire(c context.Context, r, w interface{}) (*Session, error)

Acquire retrieves a `Session` from the store using the current session cookie.

If session not found and `opt.EnableAutoCreate` is true, a new session is created and returned. If session not found and `opt.EnableAutoCreate` is false which is the default, it returns `ErrInvalidSession`.

`r` and `w` are request and response interfaces which is passed back in in GetCookie and SetCookie callbacks. Optionally, a context can be passed to get an already loaded session, useful in middleware chains.

func (*Manager) NewSession

func (m *Manager) NewSession(r, w interface{}) (*Session, error)

NewSession creates a new `Session` and updates the cookie with a new session ID, replacing any existing session ID if it exists.

func (*Manager) SetCookieHooks

func (m *Manager) SetCookieHooks(getCookie func(string, interface{}) (*http.Cookie, error), setCookie func(*http.Cookie, interface{}) error)

SetCookieHooks cane be used to get and set HTTP cookie for the session.

getCookie hook takes session ID and reader interface and returns http.Cookie and error. In a HTTP request context reader interface will be the http request object and it should obtain http.Cookie from the request object for the given cookie ID.

setCookie hook takes http.Cookie object and a writer interface and returns error. In a HTTP request context the write interface will be the http request object and it should write http request with the incoming cookie.

func (*Manager) SetSessionIDHooks

func (m *Manager) SetSessionIDHooks(generateID func() (string, error), validateID func(string) bool)

SetSessionIDHooks cane be used to generate and validate custom session ID. Bydefault alpha-numeric 32bit length session ID is used if its not set. - Generating custom session ID, which will be uses as the ID for storing sessions in the backend. - Validating custom session ID, which will be used to verify the ID before querying backend.

func (*Manager) UseStore

func (m *Manager) UseStore(str Store)

UseStore sets the session store to be used.

type Options

type Options struct {
	// If enabled, Acquire() will always create and return a new session if one doesn't already exist.
	// If disabled then new session can only be created using NewSession() method.
	EnableAutoCreate bool

	// Cookie ID length. Defaults to alphanumeric 32 characters.
	// Might not be applicable to some stores like SecureCookie.
	// Also not applicable if custom generateID and validateID is set.
	SessionIDLength int

	// Cookie options.
	Cookie CookieOptions
}

Options to configure manager and cookie.

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session represents a session object used for retrieving/setting session data and cookies.

func (*Session) Bool

func (s *Session) Bool(r interface{}, err error) (bool, error)

Bool is a helper to get values as Bool. If the value is Nil, ErrNil is returned, which means key doesn't exist.

func (*Session) Bytes

func (s *Session) Bytes(r interface{}, err error) ([]byte, error)

Bytes is a helper to get values as Bytes. If the value is Nil, ErrNil is returned, which means key doesn't exist.

func (*Session) Cache

func (s *Session) Cache() error

Cache loads session values into memory for quick access. Ideal for centralized session fetching, e.g., in middleware. Subsequent Get/GetMulti calls return cached values, avoiding store access. Use ResetCache() to ensure GetAll/Get/GetMulti fetches from the store.

func (*Session) Clear

func (s *Session) Clear() error

Clear empties the data for the given session id but doesn't clear the cookie. Use `Destroy()` to delete entire session from the store and clear the cookie.

func (*Session) ClearCookie

func (s *Session) ClearCookie() error

ClearCookie sets the cookie's expiry to one day prior to clear it.

func (*Session) Delete

func (s *Session) Delete(key ...string) error

Delete deletes a given list of fields from the session.

func (*Session) Destroy

func (s *Session) Destroy() error

Destroy deletes the session from backend and clears the cookie.

func (*Session) Float64

func (s *Session) Float64(r interface{}, err error) (float64, error)

Float64 is a helper to get values as Float64. If the value is Nil, ErrNil is returned, which means key doesn't exist.

func (*Session) Get

func (s *Session) Get(key string) (interface{}, error)

Get retrieves a value for the given key in the session. If the session is already loaded, it returns the value from the existing map. Otherwise, it fetches the value from the store.

func (*Session) GetAll

func (s *Session) GetAll() (map[string]interface{}, error)

GetAll gets all the fields for the given session id.

func (*Session) GetMulti

func (s *Session) GetMulti(key ...string) (map[string]interface{}, error)

GetMulti retrieves values for multiple session fields. If a field is not found in the store then its returned as nil.

func (*Session) ID

func (s *Session) ID() string

ID returns the acquired session ID. If cookie is not set then empty string is returned.

func (*Session) Int

func (s *Session) Int(r interface{}, err error) (int, error)

Int is a helper to get values as integer. If the value is Nil, ErrNil is returned, which means key doesn't exist.

func (*Session) Int64

func (s *Session) Int64(r interface{}, err error) (int64, error)

Int64 is a helper to get values as Int64. If the value is Nil, ErrNil is returned, which means key doesn't exist.

func (*Session) ResetCache

func (s *Session) ResetCache()

ResetCache clears loaded values, ensuring subsequent Get, GetAll, and GetMulti calls fetch from the store.

func (*Session) Set

func (s *Session) Set(key string, val interface{}) error

Set assigns a value to the given key in the session.

func (*Session) SetMulti

func (s *Session) SetMulti(data map[string]interface{}) error

SetMulti assigns multiple values to the session.

func (*Session) String

func (s *Session) String(r interface{}, err error) (string, error)

String is a helper to get values as String. If the value is Nil, ErrNil is returned, which means key doesn't exist.

func (*Session) UInt64

func (s *Session) UInt64(r interface{}, err error) (uint64, error)

UInt64 is a helper to get values as UInt64. If the value is Nil, ErrNil is returned, which means key doesn't exist.

func (*Session) WriteCookie

func (s *Session) WriteCookie(id string) error

WriteCookie writes the cookie for the given session ID. Uses all the cookie options set in Manager.

type Store

type Store interface {
	// Create creates new session in the store for the given session ID.
	Create(id string) (err error)

	// Get a value for the given key from session.
	Get(id, key string) (value interface{}, err error)

	// GetMulti gets a maps of multiple values for given keys from session.
	// If some fields are not found then return nil for that field.
	GetMulti(id string, keys ...string) (data map[string]interface{}, err error)

	// GetAll gets all key and value from session.
	GetAll(id string) (data map[string]interface{}, err error)

	// Set sets an value for a field in session.
	Set(id, key string, value interface{}) error

	// Set takes a map of kv pair and set the field in store.
	SetMulti(id string, data map[string]interface{}) error

	// Delete a given list of keys from session.
	Delete(id string, key ...string) error

	// Clear empties the session but doesn't delete it.
	Clear(id string) error

	// Destroy deletes the entire session.
	Destroy(id string) error

	// Helper method for typecasting/asserting.
	// Supposed to be used as a chain.
	// For example: sess.Int(sess.Get("id", "key"))
	// Take `error` and returns that if its not nil.
	// Take `interface{}` value and type assert or convert.
	// If its nil then return ErrNil.
	// If it can't type asserted/converted then return ErrAssertType.
	Int(interface{}, error) (int, error)
	Int64(interface{}, error) (int64, error)
	UInt64(interface{}, error) (uint64, error)
	Float64(interface{}, error) (float64, error)
	String(interface{}, error) (string, error)
	Bytes(interface{}, error) ([]byte, error)
	Bool(interface{}, error) (bool, error)
}

Store represents store interface. This interface can be implemented to create various backend stores for session.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL