Session Tokens
#
IntroductionThis document assumes that you have alread read the Quick Start page.
In FoalTS, web sessions are temporary states associated with a specific user. They are identified by a token and are mainly used to keep users authenticated between several HTTP requests (the client sends the token on each request to authenticate the user).
A session usually begins when the user logs in and ends after a period of inactivity or when the user logs out. By inactivity, we mean that the server no longer receives requests from the authenticated user for a certain period of time.
#
Get Started#
Provide a SecretIn order to use sessions, you must provide a base64-encoded secret in either:
a configuration file
Example with config/default.yml
or in a
.env
file or in an environment variable:
You can generate such a secret with the CLI command:
#
Choose a Session StoreThen you have to choose where the temporary session state will be stored. FoalTS provides several session stores for this. For example, you can use the TypeORMStore
to save the sessions in your SQL database or the RedisStore
to save them in a redis cache.
These session stores are services and can therefore be injected into your controllers and services as such:
#
Create the Session and Get the Token (Log In)Sessions are created using the method createAndSaveSessionFromUser
of the session store. It takes one parameter: an object that must have an id
attribute (the user id). At login time, the user is usually retrieved upstream when checking credentials.
The session token then can be read with the method getToken()
to send it back to the client. This token identifies the session.
#
Use the Session Token to Retrieve the SessionOn each subsequent request, the client must send this token in order to retrieve the session and authenticate the user. It must be included in the Authorization
header using the bearer scheme (unless you use cookies, see section below).
The hooks @TokenRequired
and @TokenOptional
will then check the token and retrieve the associated session and user.
If the header Authorization
is not found or does not use the bearer
scheme, the hook @TokenRequired
returns an error 400 - BAD REQUEST. The @TokenOptional
hook does nothing.
If the token is present and not valid or if the associated session has expired, both hooks return an error 401 - UNAUTHORIZED.
In other cases, the hooks retrieve the session from the store and assign it to the Context.session
property. As for the session user ID, it is assigned to Context.user
.
If you want the ctx.user
to be an object or an instance of the User
class, you must pass to the hook user
option a function whose signature is:
The hooks will assign the value it returns to ctx.user
.
For example, you can use the fetchUser
function to retrieve the user from the database:
Note: The hooks @TokenRequired
and @TokenOptional
are responsible for extending the session life each time a request is received.
Alternatively, you can also manually verify a session token and read its associated session. The code below shows how to do so.
#
Destroy the Session (Log Out)Sessions are can be destroyed (i.e users can be logged out) using the destroy
method of the session store.
#
Usage with CookiesBe aware that if you use cookies, your application must provide a CSRF defense.
FoalTS sessions can also be used with cookies. The hook cookie
option and the setSessionCookie
and removeSessionCookie
funtions are dedicated to this use.
The cookie default directives can be override in the configuration.
Example with config/default.yml
Instead of having 400 and 401 HTTP errors, you can also define redirections.
#
Specify the Name of the Session Store in the ConfigurationIn order to avoid duplicates, the name of the session package can also be provided in the configuration.
The configuration also supports relative paths. See abstract services.
#
Update the Session ContentWhen receiving an HTTP request, the hooks @TokenRequired
and @TokenOptional
convert the session token (if it exists and is valid) into a Session
instance retrieved from the session store. This object is assigned to the Context.session
property and is accessible in the remaining hooks and in the controller method.
You can access and modify the session content with the set
and get
methods.
#
Session Expiration TimeoutsSession states are by definition temporary. They have two expiration timeouts.
The first one is the inactivity (or idle) timeout. If the session is not updated or its lifetime is not extended, the session is destroyed. The @TokenRequired
and @TokenOptional
take care of extending the session lifetime on each request. Its default value is 15 minutes.
The second is the absolute timeout. Whatever the activity is, the session will expire after a fixed period of time. The default value is one week.
These values can be override with the configuration keys settings.session.expirationTimeouts.inactivity
and settings.session.expirationTimeouts.absolute
. The time periods must be specified in seconds.
Example with config/default.yml
Example with .env
#
Revoking SessionsSessions can be revoked (i.e. destroyed) using the methods destroy
and clear
of the session stores. The examples below show how to use these methods in shell scripts.
#
Revoking One SessionCreate a new file named src/scripts/revoke-session.ts
.
Build the script.
Run the script.
#
Revoking All SessionsCreate a new file named src/scripts/revoke-all-sessions.ts
.
Build the script.
Run the script.
#
Specifying Globally the Session StoreAvailable in Foal v1.11.0 onwards.
In order to avoid passing the session store to the hooks each time, you can provide it via the configuration.
default.yml
#
Session StoresFoalTS currently offers three built-in session stores: TypeORMStore
, MongoDBStore
RedisStore
. Others will come in the future. If you need a specific one, you can submit a Github issue or even create your own store (see section below).
#
TypeORMStore (SQL Databases: Postgres, MySQL, SQLite, etc)This store uses the default TypeORM connection which is usually specified in ormconfig.{json|yml|js}
. This means that session states are saved in your SQL database (using the table foal_session
).
Due to the nature of SQL databases, not all expired sessions are deleted by default (we cannot define a time period after which a SQL row must be deleted). However, the session store provides us with a cleanUpExpiredSessions
function to manually delete all expired sessions. It is recommended to periodically call this method using, for example, a shell script.
src/scripts/clean-up-expired-sessions.ts
Build the script.
Run the script.
#
RedisStoreIn order to use this store, you must provide the redis URI in either:
a configuration file
Example with config/default.yml
or in a
.env
file or in an environment variable:
#
MongoDBStoreThis store saves your session states in a MongoDB database (using the collection foalSessions
). In order to use it, you must provide the MongoDB URI in either:
a configuration file
Example with config/default.yml
or in a
.env
file or in an environment variable:
Due to the nature of MongoDB databases, not all expired sessions are deleted by default (we cannot define a time period after which a document must be deleted). However, the session store provides us with a cleanUpExpiredSessions
function to manually delete all expired sessions. It is recommended to periodically call this method using, for example, a shell script.
src/scripts/clean-up-expired-sessions.ts
Build the script.
Run the script.
#
Custom StoreIf necessary, you can also create your own session store. This one must inherit the abstract class SessionStore
.
Here is the description of each method:
createAndSaveSession: Create and save a new session.
This method MUST call the
generateSessionID
method to generate the session ID.This method MUST call the
applySessionOptions
method to extend the sessionContent.
update: Update and extend the lifetime of a session.
Depending on the implementation, the internal behavior can be similar to "update" or "upsert".
destroy: Delete a session, whether it exists or not.
read: Read a session from its ID.
Return
undefined
if the session does not exist or has expired.extendLifeTime: Extend the lifetime of a session from its ID. The duration is the inactivity timeout.
If the session does not exist, the method does not throw an error.
clear: Clear all sessions.
cleanUpExpiredSessions: Some session stores may need to run periodically background jobs to cleanup expired sessions. This method deletes all expired sessions.