Skip to content

Instantly share code, notes, and snippets.

@barnabasJ
Created July 29, 2025 13:45
Show Gist options
  • Save barnabasJ/187c92098cee68217608114845993c7d to your computer and use it in GitHub Desktop.
Save barnabasJ/187c92098cee68217608114845993c7d to your computer and use it in GitHub Desktop.
ash user for discord bot
defmodule Steward.Accounts.User do
@moduledoc """
User resource for account management.
Represents users of the system with authentication capabilities.
"""
use Ash.Resource,
otp_app: :steward,
domain: Steward.Accounts,
data_layer: AshPostgres.DataLayer,
authorizers: [Ash.Policy.Authorizer],
extensions: [AshAuthentication]
authentication do
add_ons do
log_out_everywhere do
apply_on_password_change? true
end
end
tokens do
enabled? true
token_resource Steward.Accounts.Token
signing_secret Steward.Secrets
store_all_tokens? true
require_token_presence_for_authentication? true
end
strategies do
oauth2 :discord do
client_id Steward.Secrets
client_secret Steward.Secrets
redirect_uri Steward.Secrets
base_url "https://discord.com/api/v10"
authorize_url "https://discord.com/oauth2/authorize"
token_url "https://discord.com/api/v10/oauth2/token"
user_url "https://discord.com/api/v10/users/@me"
authorization_params scope: "identify email guilds"
registration_enabled? true
identity_resource Steward.Accounts.UserIdentity
end
end
end
postgres do
table "users"
repo Steward.Repo
end
actions do
defaults [:read]
create :create do
description "Create a user with basic attributes (for testing)"
accept [:email, :discord_id, :discord_username, :discord_avatar]
end
read :get_by_subject do
description "Get a user by the subject claim in a JWT"
argument :subject, :string, allow_nil?: false
get? true
prepare AshAuthentication.Preparations.FilterBySubject
end
create :register_with_discord do
description "Register or sign in a user with Discord OAuth"
accept []
argument :user_info, :map, allow_nil?: false
argument :oauth_tokens, :map, allow_nil?: false
upsert? true
upsert_identity :discord_id
upsert_fields [:email, :discord_username, :discord_avatar]
change AshAuthentication.GenerateTokenChange
change AshAuthentication.Strategy.OAuth2.IdentityChange
change fn changeset, _context ->
user_info = Ash.Changeset.get_argument(changeset, :user_info)
changeset
|> Ash.Changeset.change_attribute(:email, user_info["email"])
|> Ash.Changeset.change_attribute(:discord_id, user_info["id"])
|> Ash.Changeset.change_attribute(:discord_username, user_info["username"])
|> Ash.Changeset.change_attribute(:discord_avatar, user_info["avatar"])
end
end
create :from_discord do
description "Create or update a user from Discord API data or struct"
primary? true
accept [:discord_id]
argument :discord_struct, :map, description: "Optional Nostrum.Struct.User to use instead of fetching"
upsert? true
upsert_identity :discord_id
upsert_fields [:discord_username, :discord_avatar]
change Steward.Accounts.User.Changes.FromDiscord
end
end
policies do
bypass AshAuthentication.Checks.AshAuthenticationInteraction do
authorize_if always()
end
bypass AshDiscord.Checks.AshDiscordInteraction do
authorize_if always()
end
bypass actor_attribute_equals(:role, :bot) do
authorize_if always()
end
policy always() do
forbid_if always()
end
end
attributes do
uuid_primary_key :id
attribute :email, :ci_string do
allow_nil? false
public? true
end
# Discord OAuth attributes
attribute :discord_id, :integer do
allow_nil? false
public? true
end
attribute :discord_username, :string do
allow_nil? false
public? true
end
attribute :discord_avatar, :string do
public? true
end
end
identities do
identity :email, [:email]
identity :discord_id, [:discord_id]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment