sessionup

package module
v1.4.1 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2021 License: MIT Imports: 9 Imported by: 16

README

sessionup 🚀

GoDoc Build status Test coverage Go Report Card

Simple, yet effective HTTP session management and identification package

Features

  • Effortless session management:
    • Initialization.
    • Request authentication.
    • Retrieval of all sessions.
    • Revokation of the current session.
    • Revokation of all other sessions.
    • Revokation of all sessions.
  • Optionally identifiable sessions (IP address, OS, browser).
  • Authentication via middleware.
  • Fully customizable, but with sane defaults.
  • Lightweight.
  • Straightforward API.
  • Allows custom session stores.

Installation

go get github.com/swithek/sessionup

Usage

The first thing you will need, in order to start creating and validating your sessions, is a Manager:

store := memstore.New(time.Minute * 5)
manager := sessionup.NewManager(store)

Out-of-the-box sessionup's Manager instance comes with recommended OWASP configuration options already set, but if you feel the need to customize the behaviour and the cookie values the Manager will use, you can easily provide your own options:

manager := sessionup.NewManager(store, sessionup.Secure(false), sessionup.ExpiresIn(time.Hour * 24))

During registration, login or whenever you want to create a fresh session, you have to call the Init method and provide a key by which the sessions will be grouped during revokation and retrieval. The key can be anything that defines the owner of the session well: ID, email, username, etc.

func login(w http.ResponseWriter, r *http.Request) {
      userID := ...
      if err := manager.Init(w, r, userID); err != nil {
            // handle error
      }
      // success
}

You can store additional information with your session as well.

func login(w http.ResponseWriter, r *http.Request) {
      userID := ...
      err := manager.Init(w, r, userID, sessionup.MetaEntry("permission", "write"), sessionup.MetaEntry("age", "111"))
      if err != nil {
            // handle error
      }
      // success
}

Public / Auth middlewares check whether the request has a cookie with a valid session ID and add the session to the request's context. Public, contrary to Auth, does not call the Manager's rejection function (also customizable), thus allowing the wrapped handler to execute successfully.

http.Handle("/", manager.Public(publicHandler))
http.Handle("/private", manager.Auth(privateHandler))

There's a FetchAll method, should you want to retrieve all sessions under the same key as the current context session:

func retrieveAll(w http.ResponseWriter, r *http.Request) {
      sessions, err := manager.FetchAll(r.Context())
      if err != nil {
            // handle error
      }
      // success
}

When the time comes for session termination, use Revoke method:

func logout(w http.ResponseWriter, r *http.Request) {	
      if err := manager.Revoke(r.Context(), w); err != nil {
            // handle error
      }
      // success
}

What if you want to revoke all sessions under the same key as the current context session? Use RevokeAll:

func revokeAll(w http.ResponseWriter, r *http.Request) {
      if err := manager.RevokeAll(r.Context(), w); err != nil {
            // handle error
      }
      // success
}

... and if you want to revoke all sessions under the same key as the current context session excluding the current context session, use RevokeOther:

func revokeOther(w http.ResponseWriter, r *http.Request) {
      if err := manager.RevokeOther(r.Context()); err != nil {
            // handle error
      }
      // success
}

Sessions & Cookies

On each Init method call, a new random session ID will be generated. Since only the generated ID and no sensitive data is being stored in the cookie, there is no need to encrypt anything. If you think that the generation functionality lacks randomness or has other issues, pass your custom ID generation function as an option when creating a new Manager.

Store implementations

Custom stores need to implement the Store interface to be used by the Manager.

Limitations

sessionup offers server-only session storing and management, since the functionality to revoke/retrieve session not in the incoming request is not possible with cookie stores.

Demo

You can see sessionup in action by trying out the demo in cmd/example/

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrUnauthorized is returned when no valid session is found.
	ErrUnauthorized = errors.New("unauthorized")

	// ErrNotOwner is returned when session's status is being modified
	// not by its owner.
	ErrNotOwner = errors.New("session can be managed only by its owner")
)
View Source
var (
	// ErrDuplicateID should be returned by Store implementations upon
	// ID collision.
	ErrDuplicateID = errors.New("duplicate ID")
)

Functions

func CookieName

func CookieName(n string) setter

CookieName sets the name of the cookie. Defaults to the value stored in defaultName.

func DefaultGenID

func DefaultGenID() string

DefaultGenID is the default ID generation function called during session creation.

func DefaultReject

func DefaultReject(err error) http.Handler

DefaultReject is the default rejection function called on error. It produces a response consisting of 401 status code and a JSON body with 'error' field.

func Domain

func Domain(d string) setter

Domain sets the 'Domain' attribute on the session cookie. Defaults to empty string. More at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Scope_of_cookies

func ExpiresIn

func ExpiresIn(e time.Duration) setter

ExpiresIn sets the duration which will be used to calculate the value of 'Expires' attribute on the session cookie. If unset, 'Expires' attribute will be omitted during cookie creation. By default it is not set. More about Expires at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Session_cookies

func GenID

func GenID(g func() string) setter

GenID sets the function which will be called when a new session is created and ID is being generated. Defaults to DefaultGenID function.

func HttpOnly

func HttpOnly(h bool) setter

HttpOnly sets the 'HttpOnly' attribute on the session cookie. Defaults to true. More at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Secure_and_HttpOnly_cookies

func NewContext added in v1.1.1

func NewContext(ctx context.Context, s Session) context.Context

NewContext creates a new context with the provided Session set as a context value.

func Path

func Path(p string) setter

Path sets the 'Path' attribute on the session cookie. Defaults to "/". More at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Scope_of_cookies

func Reject

func Reject(r func(error) http.Handler) setter

Reject sets the function which will be called on error in Auth middleware. Defaults to DefaultReject function.

func SameSite

func SameSite(s http.SameSite) setter

SameSite sets the 'SameSite' attribute on the session cookie. Defaults to http.SameSiteStrictMode. More at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_cookies

func Secure

func Secure(s bool) setter

Secure sets the 'Secure' attribute on the session cookie. Defaults to true. More at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Secure_and_HttpOnly_cookies

func Validate added in v1.2.0

func Validate(v bool) setter

Validate determines whether IP and User-Agent data should be checked on each request to authenticated routes or not.

func WithAgent

func WithAgent(w bool) setter

WithAgent determines whether User-Agent data should be extracted from the request or not. Defaults to true.

func WithIP

func WithIP(w bool) setter

WithIP determines whether IP should be extracted from the request or not. Defaults to true.

Types

type Manager

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

Manager holds the data needed to properly create sessions and set them in http responses, extract them from http requests, validate them and directly communicate with the store.

func NewManager

func NewManager(s Store, opts ...setter) *Manager

NewManager creates a new Manager with the provided store and options applied to it.

func (*Manager) Auth

func (m *Manager) Auth(next http.Handler) http.Handler

Auth wraps the provided handler, checks whether the session, associated to the ID stored in request's cookie, exists in the store or not and, if former is the case, adds it to the request's context. Wrapped handler will be activated only if there are no errors returned from the store, the session is found and its properties match the ones in the request (if validation is activated), otherwise, the manager's rejection function will be called.

func (*Manager) Clone

func (m *Manager) Clone(opts ...setter) *Manager

Clone copies the manager to its fresh copy and applies provided options.

func (*Manager) Defaults

func (m *Manager) Defaults()

Defaults sets all configuration options to reasonable defaults.

func (*Manager) FetchAll

func (m *Manager) FetchAll(ctx context.Context) ([]Session, error)

FetchAll retrieves all sessions of the same user key as session stored in the context currently has. Session with the same ID as the one stored in the context will have its 'Current' field set to true. If no sessions are found or the context session is not set, both return values will be nil.

func (*Manager) Init

func (m *Manager) Init(w http.ResponseWriter, r *http.Request, key string, mm ...Meta) error

Init creates a fresh session with the provided user key, inserts it in the store and sets the proper values of the cookie.

func (*Manager) Public

func (m *Manager) Public(next http.Handler) http.Handler

Public wraps the provided handler, checks whether the session, associated to the ID stored in request's cookie, exists in the store or not and, if former is the case, adds it to the request's context. If no valid cookie is provided, session doesn't exist, the properties of the request don't match the ones associated to the session (if validation is activated) or the store returns an error, wrapped handler will be activated nonetheless. Rejection function will be called only for non-http side effects (like error logging), but response/request control will not be passed to it.

func (*Manager) Revoke

func (m *Manager) Revoke(ctx context.Context, w http.ResponseWriter) error

Revoke deletes the current session, stored in the context, from the store and ensures cookie deletion. Function will be no-op and return nil, if context session is not set.

func (*Manager) RevokeAll

func (m *Manager) RevokeAll(ctx context.Context, w http.ResponseWriter) error

RevokeAll deletes all sessions of the same user key as session stored in the context currently has. This includes context session as well. Function will be no-op and return nil, if context session is not set.

func (*Manager) RevokeByID added in v1.1.0

func (m *Manager) RevokeByID(ctx context.Context, id string) error

RevokeByID deletes session by its ID. Function will be no-op and return nil, if no session is found.

func (*Manager) RevokeByIDExt added in v1.3.0

func (m *Manager) RevokeByIDExt(ctx context.Context, id string) error

RevokeByIDExt deletes session by its ID after checking if it belongs to the same user as the one in the context. Function will be no-op and return nil, if no session is found.

func (*Manager) RevokeByUserKey added in v1.1.0

func (m *Manager) RevokeByUserKey(ctx context.Context, key string) error

RevokeByUserKey deletes all sessions under the provided user key. This includes context session as well. Function will be no-op and return nil, if no sessions are found.

func (*Manager) RevokeOther

func (m *Manager) RevokeOther(ctx context.Context) error

RevokeOther deletes all sessions of the same user key as session stored in the context currently has. Context session will be excluded. Function will be no-op and return nil, if context session is not set.

type Meta added in v1.4.0

type Meta func(map[string]string)

Meta is a func that handles session's metadata map.

func MetaEntry added in v1.4.0

func MetaEntry(key, value string) Meta

MetaEntry adds a new entry into the session's metadata map.

type Session

type Session struct {
	// Current specifies whether this session's ID
	// matches the ID stored in the request's cookie or not.
	// NOTE: this field should be omitted by Store interface
	// implementations when inserting session into the underlying
	// data store.
	Current bool `json:"current"`

	// CreatedAt specifies a point in time when this session
	// was created.
	CreatedAt time.Time `json:"created_at"`

	// ExpiresAt specifies a point in time when this
	// session should become invalid and be deleted
	// from the store.
	ExpiresAt time.Time `json:"-"`

	// ID specifies a unique ID used to find this session
	// in the store.
	ID string `json:"id"`

	// UserKey specifies a non-unique key used to find all
	// sessions of the same user.
	UserKey string `json:"-"`

	// IP specifies an IP address that was used to create
	// this session
	IP net.IP `json:"ip"`

	// Agent specifies the User-Agent data that was used
	// to create this session.
	Agent struct {
		OS      string `json:"os"`
		Browser string `json:"browser"`
	} `json:"agent"`

	// Meta specifies a map of metadata associated with
	// the session.
	Meta map[string]string `json:"meta,omitempty"`
}

Session holds all the data needed to identify a session.

func FromContext

func FromContext(ctx context.Context) (Session, bool)

FromContext extracts Session from the context.

func (Session) IsValid added in v1.3.0

func (s Session) IsValid(r *http.Request) bool

IsValid checks whether the incoming request's properties match active session's properties or not.

type Store

type Store interface {
	// Create should insert the new provided session into the store and
	// ensure that it is deleted when expiration time due.
	// Error should be returned on ID collision or other system errors.
	Create(ctx context.Context, s Session) error

	// FetchByID should retrieve the session from the store by the
	// provided ID.
	// The second returned value indicates whether the session was found
	// or not (true == found), error should be nil if session is not found.
	// Error should be returned on system errors only.
	FetchByID(ctx context.Context, id string) (Session, bool, error)

	// FetchByUserKey should retrieve all sessions associated with the
	// provided user key. If none are found, both return values should
	// be nil.
	// Error should be returned on system errors only.
	FetchByUserKey(ctx context.Context, key string) ([]Session, error)

	// DeleteByID should delete the session from the store by the
	// provided ID.
	// If session is not found, this function should be no-op and
	// return nil.
	// Error should be returned on system errors only.
	DeleteByID(ctx context.Context, id string) error

	// DeleteByUserKey should delete all sessions associated with the
	// provided user key, except those whose IDs are provided as the
	// last argument.
	// If none are found, this function should be no-op and return nil.
	// Error should be returned on system errors only.
	DeleteByUserKey(ctx context.Context, key string, expID ...string) error
}

Store provides an easy access to the underlying data store, without exposing any of its internal logic, but providing all the mandatory methods accordingly.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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