Zoonk.Accounts.UserToken (Zoonk v0.1.0-dev)

View Source

Represents authentication and verification tokens for users.

We send an OTP code to users when they login, sign up, or change their email. They can use this code to verify their identity and generate a session token used for authentication.

Fields

Field NameTypeDescription
tokenBinaryThe token used for authentication or verification.
contextStringThe context in which the token is used (e.g., "login").
sent_toStringThe email address or phone number to which the token was sent.
user_idIntegerThe ID from Zoonk.Accounts.User.
inserted_atDateTimeTimestamp when the token was created.
updated_atDateTimeTimestamp when the token was last updated.

Summary

Functions

Builds an OTP code and its hash to be delivered to the user's email.

Generates a token that will be stored in a signed place, such as session or cookie. As they are signed, those tokens do not need to be hashed.

Returns the token struct for the given token value and context.

Gets all tokens for the given user for the given contexts.

Deletes a list of tokens.

Returns the maximum number of OTP codes that can be issued per hour.

Checks if the OTP code is valid and returns its underlying lookup query.

Checks if the OTP code is valid and returns its underlying lookup query.

Checks if the token is valid and returns its underlying lookup query.

Functions

build_otp_code(user, context)

Builds an OTP code and its hash to be delivered to the user's email.

The non-hashed OTP code is sent to the user email while the hashed part is stored in the database. The original code cannot be reconstructed, which means anyone with read-only access to the database cannot directly use the code in the application to gain access. Furthermore, if the user changes their email in the system, the codes sent to the previous email are no longer valid.

Returns {:ok, otp_code} if the code can be generated, or {:error, :rate_limit_exceeded} if the user has exceeded the maximum number of OTP codes allowed per hour.

build_session_token(user)

Generates a token that will be stored in a signed place, such as session or cookie. As they are signed, those tokens do not need to be hashed.

The reason why we store session tokens in the database, even though Phoenix already provides a session cookie, is because Phoenix' default session cookies are not persisted, they are simply signed and potentially encrypted. This means they are valid indefinitely, unless you change the signing/encryption salt.

Therefore, storing them allows individual user sessions to be expired. The token system can also be extended to store additional data, such as the device used for logging in. You could then use this information to display all valid sessions and devices in the UI and allow users to explicitly expire any session they deem invalid.

by_token_and_context_query(token, context)

Returns the token struct for the given token value and context.

by_user_and_contexts_query(user, contexts)

Gets all tokens for the given user for the given contexts.

delete_all_query(tokens)

Deletes a list of tokens.

get_max_otp_codes_per_hour()

Returns the maximum number of OTP codes that can be issued per hour.

The purpose of this rate limit is to prevent brute force attacks and protect users from excessive OTP code attempts.

Example

iex> get_max_otp_codes_per_hour()
5

verify_change_email_code_query(otp_code, context)

Checks if the OTP code is valid and returns its underlying lookup query.

The query returns the user_token found by the token, if any.

This is used to validate requests to change the user email. The given OTP code is valid if it matches its hashed counterpart in the database and if it has not expired. The context must always start with "change:".

verify_otp_code_query(otp_code, email)

Checks if the OTP code is valid and returns its underlying lookup query.

If found, the query returns a tuple of the form {user, token}.

The given code is valid if it matches its hashed counterpart in the database. This function also checks if the code is being used within 15 minutes. The context of an OTP code is always "login".

verify_session_token_query(token)

Checks if the token is valid and returns its underlying lookup query.

The query returns the user found by the token, if any, along with the token's creation time.

The token is valid if it matches the value in the database and it has not expired (after @session_validity_in_days).