Sqlite.Auth.Username

Manage usernames for users in the database.

Migrations

All migrations are meant to be plug-and-play with the gren-sqlite-migrate package:

Sqlite.Migrate.migrate database
    { secureContext = secureContext }
    (Sqlite.Auth.Username.migrationV1 ++ yourOtherMigrations)

If you are using a different method of migration, the individual SQL statements can be extracted from each migration's statements field.

migrationV1 : { name : String, statements : Array String }

Initial migration required for functions in this module to operate.

CREATE TABLE IF NOT EXISTS usernames (
    username TEXT PRIMARY KEY NOT NULL,
    user_id TEXT NOT NULL,
    created_at INTEGER NOT NULL,
    updated_at INTEGER NOT NULL,
    FOREIGN KEY (user_id)
        REFERENCES users(id)
        ON DELETE CASCADE
);

username is, of course, the username of the user as a String. user_id references the user (in the Sqlite.Auth.User module). Both created_at and updated_at are posix timestamp values. updated_at is updated appropriately whenever a username is changed.

This schema allows each user to have many usernames. If that's not desirable for your application, you can add an additional migration to add a unique index.

CREATE UNIQUE INDEX IF NOT EXISTS usernames_user_id_unique
    ON usernames (user_id);

Creating a username

You must construct a Username before being able to save it for a user or find a user by that username. These functions ensure usernames are properly formatted before being saved to or searched within the database.

If you want to manipulate a username in any way, you must do so to the raw String before calling fromString. That includes any content policies of your application (removing specific characters, enforcing character limits, checking for off-limit names or phrases, etc.). The normalize function only trims, rejects empty strings, and lowercases the username.

type Username state

A username, with a phantom type state of either Raw or Normalized.

type Raw

A raw, non-normalized username.

type Normalized

A normalized username that is ready to be saved to the database.

fromString : String -> Username Raw

Turn a String into a raw Username, ready to normalize.

normalize : Username Raw -> Task {} (Username Normalized)

Ensures the username has been trimmed, is not empty, and all characters are lowercase. This function does not remove internal spaces.

toString : Username state -> String

Get the underlying username String.

Working with usernames

new : Database -> Username Normalized -> User -> Task Error User

Add a username to the given User, returning the user for further pipeline steps. The passed username must be normalized using the normalize function.

change :
Database
-> { from : Username Normalized, to : Username Normalized }
-> Verified
-> Task Error Verified

Change one of a user's usernames to a new one. from names the username being replaced and to the replacement. With the default schema, a user may hold several usernames, so only the row matching both the user and from changes.

This operation requires a Verified user. A user can be verified by validating their password (Sqlite.Auth.Password.validate) or their current session (Sqlite.Auth.Session.verify).

user : Database -> Username Normalized -> Task Error (Maybe User)

Get the user who owns the given username. Returns Nothing if the given username is not associated with any user.

list :
Database
-> User
-> Task Error (Array ( Username Normalized))

Get all the usernames associated with a user.

delete :
Database
-> Username Normalized
-> Verified
-> Task Error {}

Remove one username of a user.

This operation requires a Verified user. A user can be verified by validating their password (Sqlite.Auth.Password.validate) or their current session (Sqlite.Auth.Session.verify).

deleteAll : Database -> Verified -> Task Error {}

Remove all of a user's usernames. This may leave the user in a less-than-ideal scenario if usernames are the only method of identifying themselves when logging in. Use with caution!

This operation requires a Verified user. A user can be verified by validating their password (Sqlite.Auth.Password.validate) or their current session (Sqlite.Auth.Session.verify).