Ithibati.Schema.User (Ithibati v0.6.2)

Copy Markdown View Source

Adds an identifier and credential associations to an application-owned account schema.

defmodule MyApp.Accounts.User do
  use Ecto.Schema
  alias Ithibati.Schema.Identifier
  alias Ithibati.Schema.User

  use User, identifier: :email, format: Identifier.email_format()

  import Ecto.Changeset

  schema "users" do
    ithibati_account()

    field :name, :string
    timestamps(type: :utc_datetime_usec)
  end

  def changeset(user, attrs) do
    user
    |> identifier_changeset(attrs)
    |> cast(attrs, [:name])
  end
end

Options

  • :identifier — required literal atom naming the account's identifier field.
  • :format — optional regex checked after normalization. See Ithibati.Schema.Identifier.username_format/0 and Ithibati.Schema.Identifier.email_format/0.
  • :format_message — custom format-error message; requires :format. Defaults to Ecto's "has invalid format" message.
  • :constraint_name — optional identifier-index name. Used both when creating the index and when translating its constraint error into a changeset error.
  • :unique_index — defaults to true. Set false when the application creates the index; Ithibati still verifies that it guarantees uniqueness of the identifier column alone.

Except for :identifier, values can be module attributes declared before use. Explicit nil values are rejected. See Configuration and schemas for examples.

Generated fields and functions

Call ithibati_account/0 inside the schema block. It declares the identifier as :string and adds :passkeys, :recovery_codes and :sessions associations.

The macro generates identifier_changeset/2, the overridable passkey_display_name/1, and __ithibati__/1 for schema metadata. Identifier validation trims and lowercases the value, requires it, checks any supplied format and limits it to 254 graphemes.

Compilation fails if the schema omits ithibati_account/0 or defines its own identifier_changeset/2 or __ithibati__/1. The application creates the database column; Ithibati.Migration creates or checks its unique index.

Summary

Functions

Returns whether the module is loaded and exports the account-schema metadata function.

Returns %{name: identifier, display_name: display_name} for WebAuthn registration.

Returns whether the changeset contains a unique-constraint error on the identifier field.

Declares the identifier field and the associations Ithibati needs. Call it inside your schema block, in a module that has use Ithibati.Schema.User above it.

Functions

account_schema?(module)

Returns whether the module is loaded and exports the account-schema metadata function.

Use this predicate instead of checking Ithibati's generated marker directly. It identifies the schema integration; it does not validate the database table or its indexes.

credential_user(account)

Returns %{name: identifier, display_name: display_name} for WebAuthn registration.

For an account using Ithibati.Schema.User, the identifier comes from its declared field. The display name comes from passkey_display_name/1, falling back to the identifier when that function returns nil or false.

For a string identifier, both values are that string. This function does not normalize it. An account struct without the schema integration raises ArgumentError.

identifier_taken?(changeset)

Returns whether the changeset contains a unique-constraint error on the identifier field.

Use this after a failed repo insert or update to distinguish an identifier collision from format errors or uniqueness errors on other application fields. The changeset must belong to a schema using Ithibati.Schema.User; otherwise this raises ArgumentError.

A changeset that has not reached the database has no constraint error and returns false. The predicate checks the field's error metadata; it does not perform a uniqueness query or validate the index definition.

ithibati_account()

(macro)

Declares the identifier field and the associations Ithibati needs. Call it inside your schema block, in a module that has use Ithibati.Schema.User above it.