PhoenixKit.Users.Auth (phoenix_kit v1.7.63)

Copy Markdown View Source

The Auth context for user authentication and management.

This module provides functions for user registration, authentication, password management, and email confirmation. It serves as the main interface for all user-related operations in PhoenixKit.

Core Functions

User Registration and Authentication

Password Management

Email Confirmation

  • deliver_user_confirmation_instructions/1 - Send confirmation email
  • confirm_user/1 - Confirm user account with token
  • update_user_email/2 - Change user email with confirmation

Session Management

Usage Examples

# Register a new user
{:ok, user} = PhoenixKit.Users.Auth.register_user(%{
  email: "user@example.com",
  password: "secure_password123"
})

# Authenticate user
case PhoenixKit.Users.Auth.get_user_by_email_and_password(email, password) do
  {:ok, user} -> {:ok, user}
  {:error, :invalid_credentials} -> {:error, :invalid_credentials}
  {:error, :rate_limit_exceeded} -> {:error, :rate_limit_exceeded}
end

# Send confirmation email
PhoenixKit.Users.Auth.deliver_user_confirmation_instructions(user)

Security Features

  • Passwords are hashed using bcrypt
  • Email confirmation prevents unauthorized account creation
  • Session tokens provide secure authentication
  • Password reset tokens expire for security
  • All sensitive operations are logged

Summary

Functions

Manually confirms a user account (admin function).

Manually unconfirms a user account (admin function).

Updates the user password as an admin (bypasses current password validation).

Emulates that the email will change without actually changing it in the database.

Assigns roles to existing users who don't have any PhoenixKit roles.

Bulk update multiple users with the same field values.

Calculate SHA256 hash of a file.

Checks if a user can be deleted by the current user.

Returns a changeset for tracking admin note changes.

Returns an %Ecto.Changeset{} for changing the user email.

Returns an %Ecto.Changeset{} for changing the user password.

Returns an %Ecto.Changeset{} for changing the user profile.

Returns an %Ecto.Changeset{} for tracking user changes.

Confirms a user by the given token.

Creates an admin note about a user.

Creates a guest user from checkout billing data.

Deletes an admin note.

Deletes all session tokens for the given user.

Deletes a user account with proper cascade handling and data anonymization.

Deletes a specific custom field for a user.

Deletes the signed token with the given context.

Delivers the confirmation email instructions to the given user.

Delivers the reset password email to the given user.

Delivers the update email instructions to the given user.

Demotes an admin user to regular user role.

Ensures the user is active by checking the is_active field.

Gets a single admin note by UUID.

Gets all active session tokens for the given user.

Gets the first admin user (Owner or Admin role).

Gets the UUID of the first admin user.

Gets the first user in the system (by insertion order).

Gets the UUID of the first user in the system.

Gets role statistics for dashboard display.

Gets the user token record for the given session token.

Gets a single user.

Gets a single user.

Gets a user by email.

Gets a user by email or username.

Gets the user by reset password token.

Gets the user with the given signed token.

Gets a user by username.

Gets a specific custom field value for a user.

Gets the display value for a custom field, resolving select field indexes to text.

Gets a user field value from either schema fields or custom fields.

Gets a user by UUID with minimal fields for selection interfaces.

Gets all active roles for a user.

Gets a user by UUID with preloaded roles.

Gets users by list of UUIDs.

Gets multiple users by their UUIDs.

Lists all admin notes for a user, ordered by most recent first.

Lists all roles.

Lists users with pagination and optional role filtering.

Promotes a user to admin role.

Registers a user with automatic role assignment.

Registers a user with IP geolocation data.

Removes a role from a user.

Resets the user password.

Searches users by email or name for selection interfaces.

Sets a specific custom field value for a user.

Toggles user confirmation status (admin function).

Updates an admin note.

Update a user's avatar by storing the file and saving the file ID.

Updates the user email using the given token.

Updates both schema and custom fields in a single call.

Updates user's preferred locale (dialect preference).

Updates the user password.

Updates a user's profile information.

Updates user status with Owner protection.

Checks if a user has a specific role.

Gets all users who have a specific role.

Verifies a session fingerprint against the stored token data.

Functions

admin_confirm_user(user)

Manually confirms a user account (admin function).

Examples

iex> admin_confirm_user(user)
{:ok, %User{}}

iex> admin_confirm_user(invalid_user)
{:error, %Ecto.Changeset{}}

admin_unconfirm_user(user)

Manually unconfirms a user account (admin function).

Examples

iex> admin_unconfirm_user(user)
{:ok, %User{}}

iex> admin_unconfirm_user(invalid_user)
{:error, %Ecto.Changeset{}}

admin_update_user_password(user, attrs, context \\ %{})

Updates the user password as an admin (bypasses current password validation).

Parameters

  • user - The user whose password is being updated
  • attrs - Password attributes (password, password_confirmation)
  • context - Optional context map containing:
    • :admin_user - The admin performing the action (for audit logging)
    • :ip_address - IP address of the admin (for audit logging)
    • :user_agent - User agent of the admin (for audit logging)

Examples

iex> admin_update_user_password(user, %{password: "new_password", password_confirmation: "new_password"})
{:ok, %User{}}

iex> admin_update_user_password(user, %{password: "new_password", password_confirmation: "new_password"}, %{admin_user: admin, ip_address: "192.168.1.1"})
{:ok, %User{}}

iex> admin_update_user_password(user, %{password: "short"})
{:error, %Ecto.Changeset{}}

apply_user_email(user, password, attrs)

Emulates that the email will change without actually changing it in the database.

Examples

iex> apply_user_email(user, "valid password", %{email: ...})
{:ok, %User{}}

iex> apply_user_email(user, "invalid password", %{email: ...})
{:error, %Ecto.Changeset{}}

assign_role(user, role_name, assigned_by \\ nil, opts \\ [])

Assigns a role to a user.

Examples

iex> assign_role(user, "Admin")
{:ok, %RoleAssignment{}}

iex> assign_role(user, "Admin", assigned_by_user)
{:ok, %RoleAssignment{}}

iex> assign_role(user, "NonexistentRole")
{:error, :role_not_found}

assign_roles_to_existing_users(opts \\ [])

Assigns roles to existing users who don't have any PhoenixKit roles.

This is useful for migration scenarios where PhoenixKit is installed into an existing application with users.

Examples

iex> assign_roles_to_existing_users()
{:ok, %{assigned_owner: 1, assigned_users: 5, total_processed: 6}}

bulk_update_user_fields(users, attrs)

Bulk update multiple users with the same field values.

This function updates multiple users at once with the same set of fields. Each user is updated independently, and the function returns a list of results showing which updates succeeded and which failed.

Both schema fields and custom fields can be updated in the same call.

Parameters

  • users - List of User structs to update
  • attrs - Map of field names to values (can include both schema and custom fields)

Returns

Returns {:ok, results} where results is a list of tuples:

  • {:ok, user} - Successfully updated user
  • {:error, changeset} - Failed update with error details

Examples

# Update multiple users with the same fields
iex> users = [user1, user2, user3]
iex> bulk_update_user_fields(users, %{status: "active", department: "Engineering"})
{:ok, [
  {:ok, %User{status: "active", custom_fields: %{"department" => "Engineering"}}},
  {:ok, %User{status: "active", custom_fields: %{"department" => "Engineering"}}},
  {:error, %Ecto.Changeset{}}
]}

# Update both schema and custom fields
iex> bulk_update_user_fields(users, %{
...>   first_name: "John",           # Schema field
...>   last_name: "Doe",             # Schema field
...>   custom_field_1: "value1",     # Custom field
...>   custom_field_2: "value2"      # Custom field
...> })
{:ok, [results...]}

calculate_file_hash(file_path)

Calculate SHA256 hash of a file.

Used internally for file integrity verification.

Parameters

  • file_path - Path to the file

Returns

  • String containing the lowercase hexadecimal SHA256 hash

can_delete_user?(user, current_user)

Checks if a user can be deleted by the current user.

Returns :ok if deletion is allowed, or {:error, reason} if not.

Examples

iex> can_delete_user?(user_to_delete, current_user)
:ok

iex> can_delete_user?(current_user, current_user)
{:error, :cannot_delete_self}

change_admin_note(note, attrs \\ %{})

Returns a changeset for tracking admin note changes.

Examples

iex> change_admin_note(note)
%Ecto.Changeset{}

change_user_email(user, attrs \\ %{})

Returns an %Ecto.Changeset{} for changing the user email.

Examples

iex> change_user_email(user)
%Ecto.Changeset{data: %User{}}

change_user_password(user, attrs \\ %{})

Returns an %Ecto.Changeset{} for changing the user password.

Examples

iex> change_user_password(user)
%Ecto.Changeset{data: %User{}}

change_user_profile(user, attrs \\ %{})

Returns an %Ecto.Changeset{} for changing the user profile.

Examples

iex> change_user_profile(user)
%Ecto.Changeset{data: %User{}}

change_user_registration(user, attrs \\ %{})

Returns an %Ecto.Changeset{} for tracking user changes.

Examples

iex> change_user_registration(user)
%Ecto.Changeset{data: %User{}}

confirm_user(token)

Confirms a user by the given token.

If the token matches, the user account is marked as confirmed and the token is deleted.

create_admin_note(user, author, attrs)

Creates an admin note about a user.

Parameters

  • user - The user being noted about
  • author - The admin creating the note
  • attrs - Map containing :content

Examples

iex> create_admin_note(user, author, %{content: "Important note"})
{:ok, %AdminNote{}}

iex> create_admin_note(user, author, %{content: ""})
{:error, %Ecto.Changeset{}}

create_guest_user(attrs)

Creates a guest user from checkout billing data.

This function is used during guest checkout to create a temporary user account. The user will have confirmed_at = nil until they verify their email address.

Parameters

  • attrs - Map with email (required), first_name, last_name

Returns

  • {:ok, user} - New user created successfully
  • {:error, :email_exists_confirmed} - Email belongs to confirmed user (should login)
  • {:error, :email_exists_unconfirmed, existing_user} - Reuse existing unconfirmed user
  • {:error, changeset} - Validation errors

Examples

iex> create_guest_user(%{email: "guest@example.com", first_name: "John"})
{:ok, %User{}}

iex> create_guest_user(%{email: "existing@confirmed.com"})
{:error, :email_exists_confirmed}

iex> create_guest_user(%{email: "existing@unconfirmed.com"})
{:error, :email_exists_unconfirmed, %User{}}

delete_admin_note(note)

Deletes an admin note.

Examples

iex> delete_admin_note(note)
{:ok, %AdminNote{}}

delete_all_user_session_tokens(user)

Deletes all session tokens for the given user.

This function is useful when you need to force logout a user from all sessions, for example when their roles change and they need fresh authentication.

delete_user(user, opts \\ %{})

Deletes a user account with proper cascade handling and data anonymization.

Protection Rules

  1. Cannot delete self - Prevents accidental self-deletion
  2. Cannot delete last Owner - System must always have at least one Owner
  3. Admin/Owner only - Only privileged users can delete accounts

Data Handling Strategy

Cascade Delete (automatic or manual)

  • User tokens (ON DELETE CASCADE in DB)
  • Role assignments (ON DELETE CASCADE in DB)
  • OAuth providers
  • Billing profiles
  • Shop carts
  • Admin notes

Anonymize (preserve data, remove PII)

  • Orders - SET NULL on user_uuid, preserve financial records
  • Posts - Keep content, set user_uuid to NULL, mark as deleted author
  • Comments - Keep content, set user_uuid to NULL, mark as deleted author
  • Tickets - Preserve for support history, anonymize
  • Email logs - Retain for compliance, anonymize
  • Files - Anonymize ownership

Parameters

  • user - The user to delete
  • opts - Options map containing:
    • :current_user - The user performing the deletion (required)
    • :ip_address - IP address for audit logging
    • :user_agent - User agent for audit logging

Returns

  • {:ok, %{deleted_user_uuid: uuid, anonymized_records: count}} - Success
  • {:error, :cannot_delete_self} - Cannot delete your own account
  • {:error, :cannot_delete_last_owner} - Cannot delete the last Owner
  • {:error, :insufficient_permissions} - Current user lacks permission
  • {:error, reason} - Other errors

Examples

iex> delete_user(user, %{current_user: admin_user})
{:ok, %{deleted_user_uuid: "some-uuid", anonymized_records: 15}}

iex> delete_user(user, %{current_user: user})
{:error, :cannot_delete_self}

iex> delete_user(last_owner, %{current_user: admin_user})
{:error, :cannot_delete_last_owner}

iex> delete_user(admin_user, %{current_user: non_owner_admin})
{:error, :insufficient_permissions}

delete_user_custom_field(user, key)

Deletes a specific custom field for a user.

Removes the key from the custom_fields map.

Examples

iex> delete_user_custom_field(user, "phone")
{:ok, %User{}}

delete_user_session_token(token)

Deletes the signed token with the given context.

deliver_user_confirmation_instructions(user, confirmation_url_fun)

Delivers the confirmation email instructions to the given user.

Examples

iex> deliver_user_confirmation_instructions(user, &PhoenixKit.Utils.Routes.url("/users/confirm/#{&1}"))
{:ok, %{to: ..., body: ...}}

iex> deliver_user_confirmation_instructions(confirmed_user, &PhoenixKit.Utils.Routes.url("/users/confirm/#{&1}"))
{:error, :already_confirmed}

deliver_user_reset_password_instructions(user, reset_password_url_fun)

Delivers the reset password email to the given user.

This function includes rate limiting protection to prevent mass password reset attacks. After exceeding the rate limit (default: 3 requests per 5 minutes), subsequent requests will be rejected with {:error, :rate_limit_exceeded}.

Examples

iex> deliver_user_reset_password_instructions(user, &PhoenixKit.Utils.Routes.url("/users/reset-password/#{&1}"))
{:ok, %{to: ..., body: ...}}

iex> deliver_user_reset_password_instructions(user, &PhoenixKit.Utils.Routes.url("/users/reset-password/#{&1}"))
{:error, :rate_limit_exceeded}

deliver_user_update_email_instructions(user, current_email, update_email_url_fun)

Delivers the update email instructions to the given user.

Examples

iex> deliver_user_update_email_instructions(user, current_email, &PhoenixKit.Utils.Routes.url("/dashboard/settings/confirm_email/#{&1}"))
{:ok, %{to: ..., body: ...}}

demote_to_user(user)

Demotes an admin user to regular user role.

Examples

iex> demote_to_user(user)
{:ok, %RoleAssignment{}}

ensure_active_user(user)

Ensures the user is active by checking the is_active field.

Returns nil for inactive users and logs a warning. Returns the user for active users or nil input.

Examples

iex> ensure_active_user(%User{is_active: true})
%User{is_active: true}

iex> ensure_active_user(%User{is_active: false, uuid: "some-uuid"})
nil

iex> ensure_active_user(nil)
nil

generate_user_session_token(user, opts \\ [])

Generates a session token.

Options

  • :fingerprint - Optional %SessionFingerprint{} struct with :ip_address and :user_agent_hash

Examples

# Without fingerprinting (backward compatible)
token = generate_user_session_token(user)

# With fingerprinting
fingerprint = PhoenixKit.Utils.SessionFingerprint.create_fingerprint(conn)
token = generate_user_session_token(user, fingerprint: fingerprint)

get_admin_note(uuid)

Gets a single admin note by UUID.

Preloads the author information.

Examples

iex> get_admin_note("01924...")
%AdminNote{}

iex> get_admin_note("nonexistent")
nil

get_all_user_session_tokens(user)

Gets all active session tokens for the given user.

This is useful for finding all active sessions to broadcast logout messages.

get_first_admin()

Gets the first admin user (Owner or Admin role).

Useful for programmatic operations that require a user ID, such as creating entities via scripts or seeds.

Returns the first Owner if one exists, otherwise the first Admin, otherwise nil.

Examples

iex> get_first_admin()
%User{id: 1, email: "admin@example.com"}

iex> get_first_admin()
nil  # No admin users exist

get_first_admin_uuid()

Gets the UUID of the first admin user.

Convenience function that returns just the user UUID, useful for setting created_by_uuid fields programmatically.

Examples

iex> get_first_admin_uuid()
"019b5704-3680-7b95-9d82-ef16127f1fd2"

iex> get_first_admin_uuid()
nil  # No admin users exist

get_first_user()

Gets the first user in the system (by insertion order).

Returns the earliest registered user (by UUID, which is time-ordered via UUIDv7). Useful as a fallback when no specific admin is needed.

Examples

iex> get_first_user()
%User{uuid: "some-uuid"}

get_first_user_uuid()

Gets the UUID of the first user in the system.

Convenience function for getting a user UUID for created_by fields.

Deprecated name kept for backwards compatibility - returns UUID now. Prefer get_first_user_uuid/0 for new code.

Examples

iex> get_first_user_uuid()
"01924..."

get_role_stats()

Gets role statistics for dashboard display.

Examples

iex> get_role_stats()
%{
  total_users: 10,
  owner_count: 1,
  admin_count: 2,
  user_count: 7
}

get_session_token_record(token)

Gets the user token record for the given session token.

This is useful for accessing fingerprint data stored with the token.

Examples

iex> get_session_token_record("valid_token")
%UserToken{ip_address: "192.168.1.1", user_agent_hash: "abc123"}

iex> get_session_token_record("invalid_token")
nil

get_user(uuid)

Gets a single user.

Returns nil if the user does not exist.

Examples

iex> get_user(123)
%User{}

iex> get_user(456)
nil

get_user!(uuid)

Gets a single user.

Raises Ecto.NoResultsError if the User does not exist.

Examples

iex> get_user!(123)
%User{}

iex> get_user!(456)
** (Ecto.NoResultsError)

get_user_by_email(email)

Gets a user by email.

Examples

iex> get_user_by_email("foo@example.com")
%User{}

iex> get_user_by_email("unknown@example.com")
nil

get_user_by_email_and_password(email, password, ip_address \\ nil)

Gets a user by email and password.

This function includes rate limiting protection to prevent brute-force attacks. After exceeding the rate limit (default: 5 attempts per minute), subsequent attempts will be rejected with {:error, :rate_limit_exceeded}.

Examples

iex> get_user_by_email_and_password("foo@example.com", "correct_password")
{:ok, %User{}}

iex> get_user_by_email_and_password("foo@example.com", "invalid_password")
{:error, :invalid_credentials}

iex> get_user_by_email_and_password("foo@example.com", "password", "192.168.1.1")
{:ok, %User{}}

get_user_by_email_or_username(email_or_username)

Gets a user by email or username.

Checks if the input contains "@" to determine whether to search by email or username.

Examples

iex> get_user_by_email_or_username("user@example.com")
%User{}

iex> get_user_by_email_or_username("johndoe")
%User{}

iex> get_user_by_email_or_username("unknown")
nil

get_user_by_email_or_username_and_password(email_or_username, password, ip_address \\ nil)

Gets a user by email or username and password.

Allows users to log in using either their email address or username. If the input contains "@", it's treated as an email; otherwise, as a username. Username lookup is case-insensitive for better UX.

This function includes rate limiting protection to prevent brute-force attacks.

Examples

iex> get_user_by_email_or_username_and_password("foo@example.com", "correct_password")
{:ok, %User{}}

iex> get_user_by_email_or_username_and_password("johndoe", "correct_password")
{:ok, %User{}}

iex> get_user_by_email_or_username_and_password("JohnDoe", "correct_password")
{:ok, %User{}}  # Case-insensitive username lookup

iex> get_user_by_email_or_username_and_password("unknown", "password")
{:error, :invalid_credentials}

get_user_by_reset_password_token(token)

Gets the user by reset password token.

Examples

iex> get_user_by_reset_password_token("validtoken")
%User{}

iex> get_user_by_reset_password_token("invalidtoken")
nil

get_user_by_session_token(token)

Gets the user with the given signed token.

get_user_by_username(username)

Gets a user by username.

Examples

iex> get_user_by_username("johndoe")
%User{}

iex> get_user_by_username("unknown")
nil

get_user_custom_field(user, key)

Gets a specific custom field value for a user.

Returns the value if the key exists, or nil otherwise.

Examples

iex> get_user_custom_field(user, "phone")
"555-1234"

iex> get_user_custom_field(user, "nonexistent")
nil

get_user_custom_field_display(user, field_key)

Gets the display value for a custom field, resolving select field indexes to text.

For select/radio/checkbox fields that store index values (0, 1, 2...), this function returns the actual option text. For other fields, returns the raw value.

Examples

iex> get_user_custom_field_display(user, "favorite_color")
"Blue"  # even though stored value is "1"

iex> get_user_custom_field_display(user, "phone")
"555-1234"  # non-select field returns raw value

get_user_field(user, field)

Gets a user field value from either schema fields or custom fields.

This unified accessor provides O(1) performance by checking struct fields first using Map.has_key?/2, then falling back to custom_fields JSONB.

Certain sensitive fields are excluded for security:

  • password, current_password (virtual fields)
  • hashed_password (use authentication functions instead)

Examples

# Standard schema fields (O(1) struct access)
iex> get_user_field(user, "email")
"user@example.com"

iex> get_user_field(user, :first_name)
"John"

# Custom fields (O(1) JSONB lookup)
iex> get_user_field(user, "phone")
"555-1234"

# Nonexistent returns nil
iex> get_user_field(user, "nonexistent")
nil

# Excluded sensitive fields return nil
iex> get_user_field(user, "hashed_password")
nil

Performance

  • Standard fields: ~0.5μs (direct struct access)
  • Custom fields: ~1-2μs (JSONB lookup)
  • No performance penalty from checking both locations

get_user_for_selection(user_uuid)

Gets a user by UUID with minimal fields for selection interfaces.

Returns a user map with uuid, email, first_name, and last_name fields. Returns nil if user is not found.

Examples

iex> PhoenixKit.Users.Auth.get_user_for_selection("01924...")
%{uuid: "01924...", email: "user@example.com", first_name: "John", last_name: "Doe"}

iex> PhoenixKit.Users.Auth.get_user_for_selection("nonexistent")
nil

get_user_roles(user)

Gets all active roles for a user.

Examples

iex> get_user_roles(user)
["Admin", "User"]

iex> get_user_roles(user_with_no_roles)
[]

get_user_with_roles(uuid)

Gets a user by UUID with preloaded roles.

Examples

iex> get_user_with_roles("01924...")
%User{roles: [%Role{}, %Role{}]}

iex> get_user_with_roles("nonexistent")
nil

get_users_by_ids(ids)

Gets users by list of UUIDs.

Returns list of users with all fields including custom_fields. Useful for batch loading users when you have a list of UUIDs.

Examples

iex> get_users_by_ids(["01924...", "01925..."])
[%User{uuid: "01924...", ...}, %User{uuid: "01925...", ...}]

iex> get_users_by_ids([])
[]

get_users_by_uuids(uuids)

Gets multiple users by their UUIDs.

list_admin_notes(user)

Lists all admin notes for a user, ordered by most recent first.

Preloads the author information for display.

Examples

iex> list_admin_notes(user)
[%AdminNote{}, ...]

list_roles()

Lists all roles.

Examples

iex> list_roles()
[%Role{}, %Role{}, %Role{}]

list_users_paginated(opts \\ [])

Lists users with pagination and optional role filtering.

Examples

iex> list_users_paginated(page: 1, page_size: 10)
%{users: [%User{}], total_count: 50, total_pages: 5}

iex> list_users_paginated(page: 1, page_size: 10, role: "Admin")
%{users: [%User{}], total_count: 3, total_pages: 1}

promote_to_admin(user, assigned_by \\ nil)

Promotes a user to admin role.

Examples

iex> promote_to_admin(user)
{:ok, %RoleAssignment{}}

iex> promote_to_admin(user, assigned_by_user)
{:ok, %RoleAssignment{}}

register_user(attrs, ip_address \\ nil)

Registers a user with automatic role assignment.

Role assignment is handled by Elixir application logic:

  • First user receives Owner role
  • Subsequent users receive User role
  • Uses database transactions to prevent race conditions

This function includes rate limiting protection to prevent spam account creation. Rate limits apply per email address and optionally per IP address.

Examples

iex> register_user(%{field: value})
{:ok, %User{}}

iex> register_user(%{field: bad_value})
{:error, %Ecto.Changeset{}}

iex> register_user(%{email: "user@example.com"}, "192.168.1.1")
{:ok, %User{}}

register_user_with_geolocation(attrs, ip_address)

Registers a user with IP geolocation data.

This function attempts to look up geographical location information based on the provided IP address and includes it in the user registration. If geolocation lookup fails, the user is still registered with just the IP address.

This function automatically applies rate limiting based on the IP address.

Examples

iex> register_user_with_geolocation(%{email: "user@example.com", password: "password"}, "192.168.1.1")
{:ok, %User{registration_ip: "192.168.1.1", registration_country: "United States"}}

iex> register_user_with_geolocation(%{email: "invalid"}, "192.168.1.1")
{:error, %Ecto.Changeset{}}

remove_role(user, role_name, opts \\ [])

Removes a role from a user.

Examples

iex> remove_role(user, "Admin")
{:ok, %RoleAssignment{}}

iex> remove_role(user, "NonexistentRole")
{:error, :assignment_not_found}

reset_user_password(user, attrs)

Resets the user password.

Examples

iex> reset_user_password(user, %{password: "new long password", password_confirmation: "new long password"})
{:ok, %User{}}

iex> reset_user_password(user, %{password: "valid", password_confirmation: "not the same"})
{:error, %Ecto.Changeset{}}

search_users(search_term)

Searches users by email or name for selection interfaces.

Returns a list of users matching the search term, limited to 10 results for performance. Useful for autocomplete/typeahead interfaces.

Examples

iex> PhoenixKit.Users.Auth.search_users("john")
[%User{email: "john@example.com", first_name: "John"}, ...]

iex> PhoenixKit.Users.Auth.search_users("")
[]

set_user_custom_field(user, key, value)

Sets a specific custom field value for a user.

Updates a single key in the custom_fields map while preserving other fields.

Examples

iex> set_user_custom_field(user, "phone", "555-1234")
{:ok, %User{}}

iex> set_user_custom_field(user, "department", "Product")
{:ok, %User{}}

toggle_user_confirmation(user)

Toggles user confirmation status (admin function).

Examples

iex> toggle_user_confirmation(confirmed_user)
{:ok, %User{confirmed_at: nil}}

iex> toggle_user_confirmation(unconfirmed_user)
{:ok, %User{confirmed_at: ~N[2023-01-01 12:00:00]}}

update_admin_note(note, attrs)

Updates an admin note.

Only the content can be updated.

Examples

iex> update_admin_note(note, %{content: "Updated note"})
{:ok, %AdminNote{}}

iex> update_admin_note(note, %{content: ""})
{:error, %Ecto.Changeset{}}

update_user_avatar(user, file_path, filename, user_uuid \\ nil)

Update a user's avatar by storing the file and saving the file ID.

This function handles the complete avatar upload workflow:

  1. Stores the file in configured storage buckets
  2. Automatically queues background job for variant generation
  3. Saves the file ID to the user's custom_fields

This is a convenience function that combines file storage with user update. Can be called from any context (LiveView, controllers, scripts, etc.) outside of the PhoenixKit project.

Parameters

  • user - The User struct to update
  • file_path - Path to the uploaded file (temporary location)
  • filename - Original filename for the upload
  • user_uuid - The user UUID owning this file (defaults to user.uuid)

Returns

  • {:ok, user} - Avatar saved successfully
  • {:error, reason} - File storage or update failed

Examples

# Store avatar in default location with automatic variant generation
{:ok, updated_user} = Auth.update_user_avatar(user, "/tmp/upload_xyz", "avatar.jpg")

# Store with explicit user_uuid (for custom workflows)
{:ok, updated_user} = Auth.update_user_avatar(user, "/tmp/upload_xyz", "avatar.jpg", custom_user_uuid)

Automatically Generated Variants

The storage layer automatically generates these image variants:

  • original - Full-size image
  • large - 800x800px
  • medium - 400x400px
  • small - 200x200px
  • thumbnail - 100x100px

update_user_custom_fields(user, custom_fields)

Updates user custom fields.

Custom fields are stored as JSONB and can contain arbitrary key-value pairs for extending user data without schema changes.

Examples

iex> update_user_custom_fields(user, %{"phone" => "555-1234", "department" => "Engineering"})
{:ok, %User{}}

iex> update_user_custom_fields(user, "invalid")
{:error, %Ecto.Changeset{}}

update_user_email(user, token)

Updates the user email using the given token.

If the token matches, the user email is updated and the token is deleted. The confirmed_at date is also updated to the current time.

update_user_fields(user, attrs)

Updates both schema and custom fields in a single call.

This is a unified update function that automatically splits the provided attributes into schema fields and custom fields, updating both appropriately.

Schema Fields

  • first_name, last_name, email, username, user_timezone

Custom Fields

  • Any other keys are treated as custom fields

Examples

iex> update_user_fields(user, %{
...>   "first_name" => "John",
...>   "email" => "john@example.com",
...>   "phone" => "555-1234",
...>   "department" => "Engineering"
...> })
{:ok, %User{}}

iex> update_user_fields(user, %{email: "invalid"})
{:error, %Ecto.Changeset{}}

update_user_locale_preference(user, preferred_locale)

Updates user's preferred locale (dialect preference).

This allows users to select specific language dialects (e.g., en-GB, en-US) while URLs continue to use base codes (e.g., /en/). The locale is stored in the custom_fields JSONB column.

Uses update_user_custom_fields/2 internally for consistency.

Examples

iex> update_user_locale_preference(user, "en-GB")
{:ok, %User{custom_fields: %{"preferred_locale" => "en-GB", ...}}}

iex> update_user_locale_preference(user, "invalid")
{:error, "must be a valid locale format (e.g., en-US, es-MX)"}

iex> update_user_locale_preference(user, nil)
{:ok, %User{...}}  # Clears the preference

update_user_password(user, password, attrs)

Updates the user password.

Examples

iex> update_user_password(user, "valid password", %{password: ...})
{:ok, %User{}}

iex> update_user_password(user, "invalid password", %{password: ...})
{:error, %Ecto.Changeset{}}

update_user_profile(user, attrs)

Updates a user's profile information.

Examples

iex> update_user_profile(user, %{first_name: "John", last_name: "Doe"})
{:ok, %User{}}

iex> update_user_profile(user, %{first_name: ""})
{:error, %Ecto.Changeset{}}

update_user_status(user, attrs)

Updates user status with Owner protection.

Prevents deactivation of the last Owner to maintain system security.

Parameters

  • user: User to update
  • attrs: Status attributes (typically %{"is_active" => true/false})

Examples

iex> update_user_status(user, %{"is_active" => false})
{:ok, %User{}}

iex> update_user_status(last_owner, %{"is_active" => false})
{:error, :cannot_deactivate_last_owner}

user_has_role?(user, role_name)

Checks if a user has a specific role.

Examples

iex> user_has_role?(user, "Admin")
true

iex> user_has_role?(user, "Owner")
false

users_with_role(role_name)

Gets all users who have a specific role.

Examples

iex> users_with_role("Admin")
[%User{}, %User{}]

iex> users_with_role("NonexistentRole")
[]

verify_session_fingerprint(conn, token)

Verifies a session fingerprint against the stored token data.

Returns:

  • :ok if fingerprint matches or fingerprinting is disabled
  • {:warning, reason} if there's a partial mismatch (IP or UA changed)
  • {:error, :fingerprint_mismatch} if both IP and UA changed

Examples

iex> verify_session_fingerprint(conn, token)
:ok

iex> verify_session_fingerprint(conn, token)
{:warning, :ip_mismatch}