defmodule Dialup.Auth.SessionToken do @moduledoc """ Opaque session token generation and hashing. Raw tokens are sent to clients (cookie). Only SHA-256 hashes are stored in the database, following the `phx_gen_auth` model. """ @session_byte_size 32 @token_contexts %{ session: 60 * 60 * 24 * 60, confirm: 60 * 60 * 24 * 7, reset_password: 60 * 60, change_email: 60 * 60 * 24 * 7 } @doc "Generates a URL-safe opaque session token." @spec generate() :: binary() def generate do @session_byte_size |> :crypto.strong_rand_bytes() |> Base.url_encode64(padding: false) end @doc "Returns the TTL in seconds for a token context." @spec ttl(atom()) :: non_neg_integer() def ttl(context) when is_atom(context), do: Map.fetch!(@token_contexts, context) @doc "Hashes a raw token for database storage." @spec hash(binary()) :: binary() def hash(token) when is_binary(token) do :crypto.hash(:sha256, token) end @doc "Constant-time comparison of a raw token against a stored hash." @spec valid?(binary(), binary()) :: boolean() def valid?(token, stored_hash) when is_binary(token) and is_binary(stored_hash) do Plug.Crypto.secure_compare(hash(token), stored_hash) end end