Skip to content

SessionStorage

rivo edited this page Jan 6, 2018 · 1 revision

The default of the sessions package is to keep session data in RAM. This is obviously not desired in production so you will want to connect it to your own session database. This package does not make many assumptions about the database's technology so you may use file based storage, an SQL database, or no-SQL data stores.

You only need to change one package variable: sessions.Persistence. It is of the type PersistenceLayer (Godoc), an interface with six functions which we describe below. If you intend to implement all of them, it makes sense to create your own type which implements PersistenceLayer. Sometimes only a few of the functions need to be implemented. In that case, a better option is to instantiate the ExtendablePersistenceLayer struct (Godoc). The fields of this struct are six functions which may be nil. ExtendablePersistenceLayer implements PersistenceLayer in such a way that it calls its respective function if the the field is not nil or does nothing if the field is nil.

Example:

sessions.Persistence = sessions.ExtendablePersistenceLayer{
	LoadSessionFunc: func(id string) (*sessions.Session, error) {
		// ... Generate a session ...
		return session, nil
	},
}

For the following sections, refer also to the Godoc documentation.

Session Storage and Retrieval

LoadSession and Deserialization

LoadSession() retrieves a Session object for a given session ID. The function must be thread-safe. It is very common that the function is called for session IDs for which no sessions exist yet. This is not an error. The function then simply returns a nil object (and a nil error).

The Session type provides a number of ways to recover Session objects from byte slices or strings. It implements the Gob Decoder as well as the JSON Unmarshaler interfaces. Storing sessions as JSON has the advantage that the string representation is human-readable. However, the lack of types in the JSON representation leads to the conversion not being bijective. That is, you may not get the exact same Go object when converting to JSON and back. This will not matter if you don't use session storage and if user IDs are strings (integers will convert back to float64). Therefore, it is recommended to use the gob package to store and retrieve Session objects.

Example:

func LoadSession(id string) (*sessions.Session, error) {
	// ... load session bytes b from database ...
	r := bytes.NewReader(b)
	decoder := gob.NewDecoder(r)
	var session sessions.Session
	if err := decoder.Decode(&session); err != nil {
		return nil, err
	}
	return &session, nil
}

SaveSession and Serialization

SaveSession() stores the session in your session database. Your implementation should insert the session if it doesn't exist yet in your database and update it to the provided session object if it already exists. Just as with LoadSession(), we can use JSON or Gob to encode the Session object but the same restrictions apply for JSON.

DeleteSession and Cleanup

DeleteSession() deletes a session with the given session ID from your session database. This ensures that sessions are not reused after they expire.

Because the sessions package always checks if a session is still valid, theoretically, you could choose not to implement DeleteSession(). But as a precaution, it is recommended. (Also, if you use the Session.Destroy() function, it may not work as expected if DeleteSession() does not do anything.)

Note that the sessions package does not purge expired sessions from your database that have not been accessed. It is recommended that you schedule a regular session cleanup script that deletes those sessions from your database that have expired.

Functions for Users

UserSessions

The UserSessions() function returns all session IDs of a specific user. Users are identified by a user ID (which may be of any type as long as it is == comparable). Sessions have a field for user IDs, used to attach a user to it (logging in) or detaching them (logging out). Sometimes, it is desirable to log a user out of all of their sessions. For this to work, the UserSessions() should identify all sessions a user owns.

If you don't work with the sessions.User type or don't need the functionality to log all users out, you may choose not to implement this function.

LoadUser

LoadUser() loads a sessions.User object from the database given a user ID. A session itself only stores a user ID but can return a user object, that's where this function is needed.

If you don't access a session's user, you don't need to implement this function.

RoleHierarchy

This is work in progress and may change or be removed in the future.