Documentation ¶
Overview ¶
Package qsess implements web sessions, with a user-definable session data type, support for cookies and tokens, session revocation by user id, and back-ends for goleveldb, Cassandra/Syclla, PostgreSQL, MySQL, and a simple, in-memory store.
It is independent of, but integrates easily with, routers, middleware frameworks, http.Request.Context(), etc. It has zero dependencies beyond the standard library. (Database back-ends, which reside in sub-packages, depend on their respective database drivers, and any dependencies those drivers require.)
var qsStore *qsess.Store func main() { ... qsStore, err = qsess.NewMapStore([]byte("encryption-key------------------")) ... } func loginHandler(w http.ResponseWriter, r *http.Request) { ... // authenticate s := qsStore.NewSession(userID) // fill in session data err := s.Save(w) ... } func dosomethingHandler(w http.ResponseWriter, r *http.Request) { sess, ttl, err := qsStore.GetSession(w, r) ... // if session data has been modified, or it's time to refresh sess.Save(w) ... } func logoutHandler(w http.ResponseWriter, r *http.Request) { sess, _, err := qsStore.GetSession(w, r) ... err = sess.Delete(w) ... }
Session data is persisted in the server and accessed via Session.Data. (Storing session data only in clients, with a "stateless" back-end, is not supported.)
If the only session data you require is a user id, you can ignore Session.Data entirely. Just provide the user id as a byte slice to Store.NewSession and get it via Session.UserID. Set Store.NewSessData to nil, to avoid allocating an empty VarMap for every session.
If you require session data beyond just a user id, it is recommended that you supply a data type and serializer, using Store.NewSessData. (See qstest/benchmark_test.go for examples.) If you do not, the default session data type, VarMap, is a map[interface{}]interface{} with gob serialization (which is very slow, compared to the alternatives in qstest/benchmark_test.go).
If AuthType is TokenAuth, session references are transmitted to/from the client as tokens, rather than cookies. Tokens are opaque, base64-encoded strings, but they can be wrapped in structured formats, like JWT, by user code. To send tokens to clients, user code must either set Store.SendToken and Store.DeleteToken or call Session.Token to obtain tokens and manage token communication explicitly. To receive tokens from clients, GetSession, by default, reads tokens from request headers of the form, "Authorization: Bearer <token>". This can be overridden by supplying a GetToken callback.
By default, cookies and tokens are encrypted and authenticated, using AES-GCM. This can be overridden by supplying Encrypt and Decrypt functions.
Multiple Stores can be used simultaneously. For example, one Store can be used to implement login sessions via cookies, while another is used to generate and track sign-up email verification tokens.
Sessions are automatically deleted if not Saved within their expiration times. This package does not refresh sessions (i.e. reset their expiration times), except for the implicit refresh that happens whenever Save is called. User code learns the remaining time-to-live whenever it calls GetSession, and it can perform a refresh simply by calling Save. (See MwRequireSess in package qctx for an example of this.)
When a user changes a password, you can revoke all active sessions for the user by calling DeleteByUserID. To use this optional capability, you must supply a user id each time you create a session. User ids are application-defined and are not interpreted or modified by session code. They are persisted to the database and available for the life of the session, by calling UserID.
Index ¶
Constants ¶
const ( DefaultAuthType = CookieAuth DefaultMaxAgeSecs = 24 * 60 * 60 DefaultMinRefreshSecs = 1 * 60 * 60 DefaultCookieName = "qsess_session" DefaultCookieDomain = "" DefaultCookiePath = "/" DefaultCookieSecure = false DefaultCookieHTTPOnly = true DefaultCookieSameSite = http.SameSiteDefaultMode )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AuthTypeEnum ¶
type AuthTypeEnum int
AuthTypeEnum specifies how references to sessions are stored in clients - in cookies or in tokens.
const ( CookieAuth AuthTypeEnum = iota TokenAuth )
func ParseAuthType ¶
func ParseAuthType(s string) (AuthTypeEnum, error)
func (AuthTypeEnum) String ¶
func (a AuthTypeEnum) String() string
type SessBackEnd ¶
type SessBackEnd interface { // Save might have to create and save a new id, so id is passed by reference. Save(sessID *[]byte, data []byte, userID []byte, maxAgeSecs int, minRefreshSecs int) error // Get and Delete take a userID argument, but it is ignored except in // the rare case of a back-end that requires uidToClient = true. Get(sessID []byte, uID []byte) (data []byte, userID []byte, timeToLiveSecs int, maxAgeSecs int, minRefreshSecs int, err error) Delete(sessID []byte, uID []byte) error DeleteByUserID(userID []byte) error }
A back-end consists of an implementation of SessBackEnd and a Store constructor. Session ids are back-end-defined and generated by SessBackEnd.Save, are not interpreted or modified by code outside of the back-end, and are not visible to users. User ids are application-defined. Back-ends track them for DeleteByUserId.
type SessData ¶
SessData is an interface for per-session data storage. The default session data type is VarMap. It can be replaced with a custom data type by setting Store.NewSessData. See qstest/benchmark_test.go for examples.
type Session ¶
type Session struct { Data SessData // MaxAgeSecs and MinRefreshSecs are initialized to the corresponding // values in the Store. User code may set them to different values, // for example, to implement a "keep me logged in" option. // See Store for more information on how these values are used. MaxAgeSecs int MinRefreshSecs int // contains filtered or unexported fields }
func (*Session) Delete ¶
func (s *Session) Delete(w http.ResponseWriter) error
Delete deletes a session, by deleting its database record. If you're using cookies, the corresponding cookie is also deleted. If you're using tokens, and you've registered an implementation of DeleteToken, Delete will call it to delete the token from the cient.
func (*Session) DeleteByUserID ¶
func (s *Session) DeleteByUserID(w http.ResponseWriter) error
DeleteByUserID deletes all sessions for the given session's user id. If you are going to use DeleteByUserID, you must supply non-empty userIDs to NewSession.
It's OK to invoke DeleteByUserID on a newly-created session object, on which Save has never been called.
func (*Session) Save ¶
func (s *Session) Save(w http.ResponseWriter) error
Save writes a session's data to the database and refreshes its expiration time.
If AuthType is CookieAuth, Save writes a cookie containing the session id into the response headers. A call to Save must precede any calls to ResponseWriter.Write or ResponseWriter.WriteHeader, otherwise, the cookie will not make it to the client.
If AuthType is TokenAuth, you must either supply an implementation of SendToken or make other arrangements for sending the token to the client.
type Store ¶
type Store struct { // To use a custom session data type, register a constructor for an // implementation of SessData here. // NewSessData can also be set to nil, if the only session data you need // is a user id (which can be managed via NewSession and UserID). NewSessData func() SessData // SessMaxAgeSecs is the session expiration period. It must be positive. // It can be overridden in individual sessions by setting Session.MaxAgeSecs. MaxAgeSecs int // SessMinRefreshSecs enables applications to reduce refresh overhead, // by not automatically refreshing the session expiration time at // every request. Package qsess does not perform session refresh itself; // it maintains this value for use by applications and/or middleware. // For a usage example, see qctx.MwRequireSess. // It can be overridden in individual sessions via Session.MinRefreshSecs. // By convention, if MinRefreshSecs is negative, no refresh should be performed. MinRefreshSecs int // parameters for cookie creation CookieName string CookieDomain string CookiePath string CookieSecure bool CookieHTTPOnly bool CookieSameSite http.SameSite // AuthType specifies how sessions are stored in the client (cookies or tokens). AuthType AuthTypeEnum // callbacks for sending/receiving tokens to/from the client. SendToken func(token string, timeToLiveSecs int, w http.ResponseWriter) error DeleteToken func(w http.ResponseWriter) error GetToken func(w http.ResponseWriter, r *http.Request) (token string, err error) // Bring-your-own-crypto by registering Encrypt and Decrypt functions. Encrypt func(data []byte) ([]byte, error) Decrypt func(data []byte) ([]byte, error) // SessionSaved is an optional callback, which enables you to keep track // of users' last-visited time. This has no effect on session expiration; // it is purely for use by application code. You must supply a userID // with NewSession, for this to be useful. SessionSaved func(UserID []byte, timestamp time.Time) error // for back-ends that create a goroutine to prune expired sessions PruneInterval chan int // value is interval in seconds PruneKill chan int // kill pruner goroutine, value doesn't matter // contains filtered or unexported fields }
func NewMapStore ¶
NewMapStore creates a new session store, using a simple, in-memory map, with no persistence.
cipherkeys are one or more 32-byte encryption keys, to be used with AES-GCM. For encryption, only the first key is used; for decryption all keys are tried (allowing key rotation).
Additional configuration options can be set by manipulating fields in the returned qsess.Store.
func NewStore ¶
func NewStore(backend SessBackEnd, uidToClient bool, cipherkeys ...[]byte) (*Store, error)
NewStore is exported only for use by back-ends. Users should never call NewStore; instead, they should call back-end-specific Store constructors.
func (*Store) BackEnd ¶
func (st *Store) BackEnd() SessBackEnd
BackEnd is exported only for use by tests.
func (*Store) GetSession ¶
func (st *Store) GetSession(w http.ResponseWriter, r *http.Request) (s *Session, timeToLiveSecs int, e error)
GetSession determines if the current HTTP request headers contain a cookie or token for an active session and, if so, returns a valid *Session, otherwise it returns a non-nil error.
func (*Store) GetTokenSession ¶
GetTokenSession determines if the given token refers to an active session and, if so, returns a valid *Session, otherwise it returns a non-nil error.
func (*Store) NewSession ¶
NewSession creates a new session object. It is not persisted to until Session.Save() is called.
userID is an application-defined user id. If you are using DeleteByUserId, you must supply a nonempty userID. Even if you are not using DeleteByUserId, you may supply a userID, and it will be persisted by the back-end for the life of the session and will be accessible by calling UserID. Otherwise, userID can be empty or nil.
Directories ¶
Path | Synopsis |
---|---|
Package qscql is a Cassandra/Scylla back-end for qsess.
|
Package qscql is a Cassandra/Scylla back-end for qsess. |
Package qsldb is a goleveldb back-end for qsess.
|
Package qsldb is a goleveldb back-end for qsess. |
Package qsmy is a MySQL back-end for qsess.
|
Package qsmy is a MySQL back-end for qsess. |
Package qspgx is a back-end for qsess which uses PostgreSQL, accessed via the pgx package.
|
Package qspgx is a back-end for qsess which uses PostgreSQL, accessed via the pgx package. |
Package qstest contains shared test code for use by qsess back-ends.
|
Package qstest contains shared test code for use by qsess back-ends. |