How a person signs in without a password — Discord, Apple, Google, Facebook, GitHub, Steam or a device id — and linking those identities to an existing account or removing them from one.
Split out of Gamend.Accounts, which still exposes every function here under
the same name.
Summary
Functions
Attach a device_id to an existing user record. Returns {:ok, user} or {:error, changeset} if the device_id is already used.
Finds a user by Apple ID or creates a new user from OAuth data.
Finds or creates a user associated with the given device_id.
Finds a user by Discord ID or creates a new user from OAuth data.
Finds a user by Facebook ID or creates a new user from OAuth data.
Finds a user by GitHub ID or creates a new user from OAuth data.
Finds a user by Google ID or creates a new user from OAuth data.
Finds a user by Steam ID or creates a new user from Steam OpenID data.
Link an OAuth provider to an existing user account. Updates the user via the provider's oauth changeset while being careful not to overwrite existing email or avatars.
Link a device_id to an existing user account. This allows the user to authenticate using the device_id in addition to their OAuth providers.
Unlink the device_id from a user's account.
Unlink an OAuth provider from a user's account.
Functions
@spec attach_device_to_user(Gamend.Accounts.User.t(), String.t()) :: {:ok, Gamend.Accounts.User.t()} | {:error, Ecto.Changeset.t()}
Attach a device_id to an existing user record. Returns {:ok, user} or {:error, changeset} if the device_id is already used.
@spec find_or_create_from_apple(map()) :: {:ok, Gamend.Accounts.User.t()} | {:error, Ecto.Changeset.t() | term()}
Finds a user by Apple ID or creates a new user from OAuth data.
Examples
iex> find_or_create_from_apple(%{apple_id: "123", email: "user@example.com"})
{:ok, %User{}}
@spec find_or_create_from_device(String.t(), map()) :: {:ok, Gamend.Accounts.User.t()} | {:error, :disabled | Ecto.Changeset.t() | term()}
Finds or creates a user associated with the given device_id.
If a user already exists with the device_id we return it. Otherwise we create an anonymous confirmed user and attach the device_id.
@spec find_or_create_from_discord(map()) :: {:ok, Gamend.Accounts.User.t()} | {:error, Ecto.Changeset.t() | term()}
Finds a user by Discord ID or creates a new user from OAuth data.
Examples
iex> find_or_create_from_discord(%{discord_id: "123", email: "user@example.com"})
{:ok, %User{}}
@spec find_or_create_from_facebook(map()) :: {:ok, Gamend.Accounts.User.t()} | {:error, Ecto.Changeset.t() | term()}
Finds a user by Facebook ID or creates a new user from OAuth data.
Examples
iex> find_or_create_from_facebook(%{facebook_id: "123", email: "user@example.com"})
{:ok, %User{}}
@spec find_or_create_from_github(map()) :: {:ok, Gamend.Accounts.User.t()} | {:error, Ecto.Changeset.t() | term()}
Finds a user by GitHub ID or creates a new user from OAuth data.
Examples
iex> find_or_create_from_github(%{github_id: "123", email: "user@example.com"})
{:ok, %User{}}
@spec find_or_create_from_google(map()) :: {:ok, Gamend.Accounts.User.t()} | {:error, Ecto.Changeset.t() | term()}
Finds a user by Google ID or creates a new user from OAuth data.
Examples
iex> find_or_create_from_google(%{google_id: "123", email: "user@example.com"})
{:ok, %User{}}
@spec find_or_create_from_steam(map()) :: {:ok, Gamend.Accounts.User.t()} | {:error, Ecto.Changeset.t() | term()}
Finds a user by Steam ID or creates a new user from Steam OpenID data.
Examples
iex> find_or_create_from_steam(%{steam_id: "12345", email: "user@example.com"})
{:ok, %User{}}
@spec link_account(Gamend.Accounts.User.t(), map(), atom(), (Gamend.Accounts.User.t(), map() -> Ecto.Changeset.t())) :: {:ok, Gamend.Accounts.User.t()} | {:error, Ecto.Changeset.t() | {:conflict, Gamend.Accounts.User.t()}}
Link an OAuth provider to an existing user account. Updates the user via the provider's oauth changeset while being careful not to overwrite existing email or avatars.
Example: link_account(user, %{discord_id: "123", profile_url: "https://..."}, :discord_id, &User.discord_oauth_changeset/2)
@spec link_device_id(Gamend.Accounts.User.t(), String.t()) :: {:ok, Gamend.Accounts.User.t()} | {:error, Ecto.Changeset.t()}
Link a device_id to an existing user account. This allows the user to authenticate using the device_id in addition to their OAuth providers.
Returns {:ok, user} on success or {:error, changeset} if the device_id is already used by another account.
@spec unlink_device_id(Gamend.Accounts.User.t()) :: {:ok, Gamend.Accounts.User.t()} | {:error, :last_auth_method | Ecto.Changeset.t()}
Unlink the device_id from a user's account.
Returns {:ok, user} when successful or {:error, reason}.
Guard: we only allow unlinking when the user will still have at least one authentication method remaining (OAuth provider or password). This prevents users losing all login methods unexpectedly.
@spec unlink_provider( Gamend.Accounts.User.t(), :discord | :apple | :google | :facebook | :github | :steam ) :: {:ok, Gamend.Accounts.User.t()} | {:error, :last_provider | Ecto.Changeset.t() | term()}
Unlink an OAuth provider from a user's account.
provider should be one of :discord, :apple, :google, :facebook, :github, :steam. This will return {:ok, user} when successful or {:error, reason}.
Guard: we only allow unlinking when the user will still have at least one other social provider remaining. This prevents users losing all social logins unexpectedly.