Sqlite.Auth.Session
Issue, verify, and revoke users' login sessions.
A session is a login token tied to a user. The raw token is handed to the client once, when the session is issued. Only its SHA-256 hash is stored, so a stolen database cannot be replayed as live sessions.
Migrations
All migrations are meant to be plug-and-play with the gren-sqlite-migrate package:
Sqlite.Migrate.migrate database
{ secureContext = secureContext }
(Sqlite.Auth.Session.migrationV1 ++ yourOtherMigrations)
If you are using a different method of migration, the individual SQL statements
can be extracted from each migration's statements field.
Initial migration required for functions in this module to operate.
CREATE TABLE IF NOT EXISTS sessions (
session_token TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
created_at INTEGER NOT NULL,
expires_at INTEGER NOT NULL,
last_used_at INTEGER NOT NULL,
revoked_at INTEGER,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
session_tokenis the primary key and stores the SHA-256 hash of the token.user_idreferences the user's id.created_atis a posix timestamp, andexpires_atis the posix timestamp the session stops being valid.last_used_atis a posix timestamp of the last successful verification, used to enforce the idle timeout.revoked_atis the posix timestamp the session was revoked, or null while live.
Working with sessions
A row from the sessions table.
Options for sessions. lifetimeMillis is the maximum length the token is
valid for before expiring. idleTimeoutMillis is the maximum length the token
can exist without being used before expiring.
The default, recommended, options for sessions.
{ lifetimeMillis = 30 * 24 * 60 * 60 * 1000 -- 30 days
, idleTimeoutMillis = 7 * 24 * 60 * 60 * 1000 -- 7 days
}
Issue a session for a Verified user. You can
obtain a Verified with a credential check such as what
Sqlite.Auth.Password.validate produces.
This function returns two things: the session and the token.
The session is the row saved to the database representing the session. This includes the hashed session token. This value is returned to be inspected, never to be sent to the client.
The token value is the raw token (not hashed). This value should be sent safely to the user's browser. It's highly recommended you send the token to the client as a cookie that is HttpOnly, Secure, and has SameSite=Lax rules. Without these rules, or if the raw token is saved via IndexedDB, localStorage, or other methods, it's vulnerable to being stolen by attacks.
Verify a presented token, succeeding with the Session and
the user whose session has been verified. A revoked, expired, or idle session,
or a token matching no session at all — fails the task with the matching
VerifyError.
The returned, Verified, user lets a live session authorize actions without
re-checking a credential:
Session.verify database secureContext options { token = token }
|> Task.andThen .verified
|> Task.andThen (Username.change database { from = old, to = new })
It's recommended session tokens are verified for each authenticated request, ensuring the user performing a specific action is able and allowed to do so.
Why a presented token failed to verify.
StorageErroris an underlying error in the Sqlite database.TokenNotFoundis when the token matches no session row at all.Revokedis when the session has been revoked.Expiredis when the session is past its absolute lifetime.IdleTimeoutis when the session went unused for longer thanidleTimeoutMillis.
List all of a given user's live sessions. Live is defined as sessions that have not yet been revoked or are beyond their lifetimes.
Revoke a session by its raw token. If the given token does not exist, the
Task will succeed, but no changes will be made to the database.
Revoke every live session a user holds.