Repository: team-alembic/ash_authentication
Pull request #1005: chore: Add usage-rules
Author: login: chazwatkins
Created: 2025-05-26T03:21:00Z | Merged: 2025-05-27T23:12:28Z

- Add `usage-rules.md`
- Update  `mix.exs` files to include `usage-rules.md`


Diff:
diff --git a/mix.exs b/mix.exs
index 0a9079917..a9eafbdf3 100644
--- a/mix.exs
+++ b/mix.exs
@@ -43,7 +43,7 @@ defmodule AshAuthentication.MixProject do
         "Phoenix Support" => "https://github.com/team-alembic/ash_authentication_phoenix"
       },
       source_url: "https://github.com/team-alembic/ash_authentication",
-      files: ~w[lib .formatter.exs mix.exs README* LICENSE* CHANGELOG* documentation]
+      files: ~w[lib .formatter.exs mix.exs README* LICENSE* CHANGELOG* documentation usage-rules.md]
     ]
   end
 
diff --git a/usage-rules.md b/usage-rules.md
new file mode 100644
index 000000000..58babf6ca
--- /dev/null
+++ b/usage-rules.md
@@ -0,0 +1,342 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link, api_key authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
+
+## Key Principles
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource optional for OAuth2 (required for multiple providers per user)
+- API keys require strict policy controls and expiration management
+- Use prefixes for API keys to enable secret scanning compliance
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**API Key** - Token-based authentication for APIs
+- Requires: API key resource, relationship to user, sign-in action
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Requires: custom actions, secrets
+- Optional: UserIdentity resource (for multiple providers per user)
+
+## Password Strategy
+
+```elixir
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+
+```elixir
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## API Key Strategy
+
+```elixir
+# 1. Create API key resource
+defmodule MyApp.Accounts.ApiKey do
+  use Ash.Resource,
+    data_layer: AshPostgres.DataLayer,
+    authorizers: [Ash.Policy.Authorizer]
+
+  actions do
+    defaults [:read, :destroy]
+
+    create :create do
+      primary? true
+      accept [:user_id, :expires_at]
+      change {AshAuthentication.Strategy.ApiKey.GenerateApiKey, prefix: :myapp, hash: :api_key_hash}
+    end
+  end
+
+  attributes do
+    uuid_primary_key :id
+    attribute :api_key_hash, :binary, allow_nil?: false, sensitive?: true
+    attribute :expires_at, :utc_datetime_usec, allow_nil?: false
+  end
+
+  relationships do
+    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
+  end
+
+  calculations do
+    calculate :valid, :boolean, expr(expires_at > now())
+  end
+
+  identities do
+    identity :unique_api_key, [:api_key_hash]
+  end
+
+  policies do
+    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+      authorize_if always()
+    end
+  end
+end
+
+# 2. Add strategy to user resource
+authentication do
+  strategies do
+    api_key do
+      api_key_relationship :valid_api_keys
+      api_key_hash_attribute :api_key_hash
+    end
+  end
+end
+
+# 3. Add relationship to user
+relationships do
+  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
+    filter expr(valid)
+  end
+end
+
+# 4. Add sign-in action to user
+actions do
+  read :sign_in_with_api_key do
+    argument :api_key, :string, allow_nil?: false
+    prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
+  end
+end
+```
+
+**Security considerations:**
+- API keys are hashed for storage security
+- Use policies to restrict API key access to specific actions
+- Check `user.__metadata__[:using_api_key?]` to detect API key authentication
+- Access the API key via `user.__metadata__[:api_key]` for permission checks
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+**Optional for all OAuth2:**
+- UserIdentity resource (for multiple providers per user)
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    
+    # If UserIdentity resource is being used
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
+```
+
+## Add-ons
+
+### Confirmation
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  add_ons do
+    confirmation :confirm do
+      monitor_fields [:email]
+      sender MyApp.ConfirmationSender
+    end
+  end
+end
+```
+
+### Log Out Everywhere
+```elixir
+authentication do
+  tokens do
+    store_all_tokens? true
+  end
+
+  add_ons do
+    log_out_everywhere do
+      apply_on_password_change? true
+    end
+  end
+end
+```
+
+## Working with Authentication
+
+### Strategy Protocol
+```elixir
+# Get and use strategies
+strategy = AshAuthentication.Info.strategy!(MyApp.User, :password)
+{:ok, user} = AshAuthentication.Strategy.action(strategy, :sign_in, params)
+
+# List strategies
+strategies = AshAuthentication.Info.strategies(MyApp.User)
+```
+
+### Token Operations
+```elixir
+# User/subject conversion
+subject = AshAuthentication.user_to_subject(user)
+{:ok, user} = AshAuthentication.subject_to_user(subject, MyApp.User)
+
+# Token management
+AshAuthentication.TokenResource.revoke(MyApp.Token, token)
+```
+
+### Policies
+```elixir
+policies do
+  bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+    authorize_if always()
+  end
+end
+```
+
+## Common Implementation Patterns
+
+### Pattern: Multiple Authentication Methods
+When users need multiple ways to authenticate:
+
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+    end
+
+    github do
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+```
+
+### Pattern: OAuth2 with User Registration
+When new users can register via OAuth2:
+
+```elixir
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :email
+
+    change AshAuthentication.GenerateTokenChange
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+
+      changeset
+      |> Ash.Changeset.change_attribute(:email, user_info["email"])
+      |> Ash.Changeset.change_attribute(:name, user_info["name"])
+    end
+  end
+end
+```
+
+### Pattern: Custom Token Configuration
+When you need specific token behavior:
+
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+    signing_secret MyApp.Secrets
+    token_lifetime {24, :hours}
+    store_all_tokens? true  # For logout-everywhere functionality
+    require_token_presence_for_authentication? false
+  end
+end
+```
\ No newline at end of file


Reviews:
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:24:11Z
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:24:35Z
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:24:45Z
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:25:07Z
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:26:15Z
user: login: chazwatkins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T03:26:40Z
user: login: chazwatkins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T03:27:00Z
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:27:11Z
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:28:16Z
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:28:35Z
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:28:58Z
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:29:32Z
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:30:36Z
user: login: chazwatkins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T03:52:56Z
user: login: zachdaniel
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-26T04:00:13Z
user: login: jimsynz
body: Only minor tweaks and a suggestion that we change something in core to make life a little more ergonomic.
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T22:30:32Z
user: login: chazwatkins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T22:37:32Z
user: login: chazwatkins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T22:41:30Z
user: login: chazwatkins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T22:44:05Z
user: login: zachdaniel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-26T23:21:37Z
user: login: chazwatkins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-27T06:44:32Z
user: login: jimsynz
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-27T23:12:18Z

Review comments:
diff_hunk: @@ -0,0 +1,46 @@
+# Working with LLMs
path: documentation/topics/development/working-with-llms.md
user: login: zachdaniel
body: If we duplicate this guide we'll have to maintain it in multiple places. Best just to have it in Ash core I think.
created_at: 2025-05-26T03:24:11Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.)
+
+## Key Principles
+- Use `mix ash_authentication.add_strategy` for password/magic_link
path: usage-rules.md
user: login: zachdaniel
body: This supports api key as well.
created_at: 2025-05-26T03:24:35Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
path: usage-rules.md
user: login: zachdaniel
body: api key strategy also.
created_at: 2025-05-26T03:24:45Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.)
path: usage-rules.md
user: login: zachdaniel
body: ```suggestion
- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
```
created_at: 2025-05-26T03:25:07Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.)
+
+## Key Principles
+- Use `mix ash_authentication.add_strategy` for password/magic_link
+- Manual setup required for OAuth2 providers
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource required for OAuth2 strategies
path: usage-rules.md
user: login: zachdaniel
body: ```suggestion
- UserIdentity resource required for OAuth2 strategies
```

@jimsynz is this true? I don't think it is.
created_at: 2025-05-26T03:26:08Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,46 @@
+# Working with LLMs
path: documentation/topics/development/working-with-llms.md
user: login: chazwatkins
body: I can always count on you to be awake to review PRs.  haha

I'll remove it
created_at: 2025-05-26T03:26:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.)
+
+## Key Principles
+- Use `mix ash_authentication.add_strategy` for password/magic_link
path: usage-rules.md
user: login: chazwatkins
body: I'll make sure to add it.
created_at: 2025-05-26T03:27:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.)
+
+## Key Principles
+- Use `mix ash_authentication.add_strategy` for password/magic_link
+- Manual setup required for OAuth2 providers
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource required for OAuth2 strategies
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Basic Setup
path: usage-rules.md
user: login: zachdaniel
body: lets remove all mention of setup here. AshAuthentication should already be installed when these rules are present, and so things should have gone through the igniter installer. I think this will mostly confuse it.
created_at: 2025-05-26T03:27:10Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.)
+
+## Key Principles
+- Use `mix ash_authentication.add_strategy` for password/magic_link
+- Manual setup required for OAuth2 providers
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource required for OAuth2 strategies
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Basic Setup
+
+Add the AshAuthentication extension to your user resource:
+
+```elixir
+defmodule MyApp.Accounts.User do
+  use Ash.Resource,
+    extensions: [AshAuthentication],
+    domain: MyApp.Accounts
+
+  attributes do
+    uuid_primary_key :id
+    attribute :email, :ci_string, allow_nil?: false
+    attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+  end
+
+  authentication do
+    strategies do
+      password :password do
+        identity_field :email
+        hashed_password_field :hashed_password
+      end
+    end
+  end
+
+  identities do
+    identity :unique_email, [:email]
+  end
+end
+```
+
+## Installation
+
+### Quick Start (Igniter - Recommended)
+```bash
+mix igniter.install ash_authentication
+mix ash_authentication.add_strategy password magic_link
+mix ash.migrate
+```
+
+### Manual Setup (when customization needed)
+```elixir
+# 1. Add supervisor to application.ex
+{AshAuthentication.Supervisor, otp_app: :my_app}
+
+# 2. Token resource
+defmodule MyApp.Accounts.Token do
+  use Ash.Resource,
+    extensions: [AshAuthentication.TokenResource],
+    domain: MyApp.Accounts
+end
+
+# 3. UserIdentity resource (for OAuth2)
+defmodule MyApp.Accounts.UserIdentity do
+  use Ash.Resource,
+    extensions: [AshAuthentication.UserIdentity],
+    domain: MyApp.Accounts
+
+  user_identity do
+    user_resource MyApp.Accounts.User
+  end
+end
+```
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Mix task: `mix ash_authentication.add_strategy password`
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Mix task: `mix ash_authentication.add_strategy magic_link`
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Manual setup only - not supported by mix tasks
+- Requires: UserIdentity resource, custom actions, secrets
+
+## Password Strategy
+```elixir
+# Mix task handles this automatically
+mix ash_authentication.add_strategy password
+
+# Manual setup:
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+```elixir
+# Mix task handles this automatically
+mix ash_authentication.add_strategy magic_link
+
+# Manual setup:
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
+```
+
+## Add-ons
+
+### Confirmation
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  add_ons do
+    confirmation :confirm do
+      monitor_fields [:email]
+      sender MyApp.ConfirmationSender
+    end
+  end
+end
+```
+
+### Log Out Everywhere
+```elixir
+authentication do
+  tokens do
+    store_all_tokens? true
+  end
+
+  add_ons do
+    log_out_everywhere do
+      apply_on_password_change? true
+    end
+  end
+end
+```
+
+## Mix Tasks
+
+### Adding Strategies
path: usage-rules.md
user: login: zachdaniel
body: Feels like `add_strategy` is represented in a lot of places, maybe an opportunity to condense into one place under a strategies section above?
created_at: 2025-05-26T03:28:16Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.)
+
+## Key Principles
+- Use `mix ash_authentication.add_strategy` for password/magic_link
+- Manual setup required for OAuth2 providers
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource required for OAuth2 strategies
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Basic Setup
+
+Add the AshAuthentication extension to your user resource:
+
+```elixir
+defmodule MyApp.Accounts.User do
+  use Ash.Resource,
+    extensions: [AshAuthentication],
+    domain: MyApp.Accounts
+
+  attributes do
+    uuid_primary_key :id
+    attribute :email, :ci_string, allow_nil?: false
+    attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+  end
+
+  authentication do
+    strategies do
+      password :password do
+        identity_field :email
+        hashed_password_field :hashed_password
+      end
+    end
+  end
+
+  identities do
+    identity :unique_email, [:email]
+  end
+end
+```
+
+## Installation
+
+### Quick Start (Igniter - Recommended)
+```bash
+mix igniter.install ash_authentication
+mix ash_authentication.add_strategy password magic_link
+mix ash.migrate
+```
+
+### Manual Setup (when customization needed)
+```elixir
+# 1. Add supervisor to application.ex
+{AshAuthentication.Supervisor, otp_app: :my_app}
+
+# 2. Token resource
+defmodule MyApp.Accounts.Token do
+  use Ash.Resource,
+    extensions: [AshAuthentication.TokenResource],
+    domain: MyApp.Accounts
+end
+
+# 3. UserIdentity resource (for OAuth2)
+defmodule MyApp.Accounts.UserIdentity do
+  use Ash.Resource,
+    extensions: [AshAuthentication.UserIdentity],
+    domain: MyApp.Accounts
+
+  user_identity do
+    user_resource MyApp.Accounts.User
+  end
+end
+```
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Mix task: `mix ash_authentication.add_strategy password`
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Mix task: `mix ash_authentication.add_strategy magic_link`
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Manual setup only - not supported by mix tasks
+- Requires: UserIdentity resource, custom actions, secrets
+
+## Password Strategy
+```elixir
+# Mix task handles this automatically
+mix ash_authentication.add_strategy password
+
+# Manual setup:
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+```elixir
+# Mix task handles this automatically
+mix ash_authentication.add_strategy magic_link
+
+# Manual setup:
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
+```
+
+## Add-ons
+
+### Confirmation
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  add_ons do
+    confirmation :confirm do
+      monitor_fields [:email]
+      sender MyApp.ConfirmationSender
+    end
+  end
+end
+```
+
+### Log Out Everywhere
+```elixir
+authentication do
+  tokens do
+    store_all_tokens? true
+  end
+
+  add_ons do
+    log_out_everywhere do
+      apply_on_password_change? true
+    end
+  end
+end
+```
+
+## Mix Tasks
+
+### Adding Strategies
+**LLM Rule:** Only use when strategy doesn't exist. Check first: `AshAuthentication.Info.strategies/1`
+
+```bash
+# Supported strategies: password, magic_link
+mix ash_authentication.add_strategy password
+mix ash_authentication.add_strategy magic_link
+mix ash_authentication.add_strategy password magic_link  # multiple
+
+# Custom user resource
+mix ash_authentication.add_strategy password --user MyApp.Accounts.User
+
+# OAuth2 NOT supported - requires manual setup
+```
+
+**What mix tasks do:**
path: usage-rules.md
user: login: zachdaniel
body: it will figure it out 😆 we can probably leave these out.
created_at: 2025-05-26T03:28:35Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.)
+
+## Key Principles
+- Use `mix ash_authentication.add_strategy` for password/magic_link
+- Manual setup required for OAuth2 providers
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource required for OAuth2 strategies
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Basic Setup
+
+Add the AshAuthentication extension to your user resource:
+
+```elixir
+defmodule MyApp.Accounts.User do
+  use Ash.Resource,
+    extensions: [AshAuthentication],
+    domain: MyApp.Accounts
+
+  attributes do
+    uuid_primary_key :id
+    attribute :email, :ci_string, allow_nil?: false
+    attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+  end
+
+  authentication do
+    strategies do
+      password :password do
+        identity_field :email
+        hashed_password_field :hashed_password
+      end
+    end
+  end
+
+  identities do
+    identity :unique_email, [:email]
+  end
+end
+```
+
+## Installation
+
+### Quick Start (Igniter - Recommended)
+```bash
+mix igniter.install ash_authentication
+mix ash_authentication.add_strategy password magic_link
+mix ash.migrate
+```
+
+### Manual Setup (when customization needed)
+```elixir
+# 1. Add supervisor to application.ex
+{AshAuthentication.Supervisor, otp_app: :my_app}
+
+# 2. Token resource
+defmodule MyApp.Accounts.Token do
+  use Ash.Resource,
+    extensions: [AshAuthentication.TokenResource],
+    domain: MyApp.Accounts
+end
+
+# 3. UserIdentity resource (for OAuth2)
+defmodule MyApp.Accounts.UserIdentity do
+  use Ash.Resource,
+    extensions: [AshAuthentication.UserIdentity],
+    domain: MyApp.Accounts
+
+  user_identity do
+    user_resource MyApp.Accounts.User
+  end
+end
+```
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Mix task: `mix ash_authentication.add_strategy password`
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Mix task: `mix ash_authentication.add_strategy magic_link`
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Manual setup only - not supported by mix tasks
+- Requires: UserIdentity resource, custom actions, secrets
+
+## Password Strategy
+```elixir
+# Mix task handles this automatically
+mix ash_authentication.add_strategy password
+
+# Manual setup:
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+```elixir
+# Mix task handles this automatically
+mix ash_authentication.add_strategy magic_link
+
+# Manual setup:
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
+```
+
+## Add-ons
+
+### Confirmation
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  add_ons do
+    confirmation :confirm do
+      monitor_fields [:email]
+      sender MyApp.ConfirmationSender
+    end
+  end
+end
+```
+
+### Log Out Everywhere
+```elixir
+authentication do
+  tokens do
+    store_all_tokens? true
+  end
+
+  add_ons do
+    log_out_everywhere do
+      apply_on_password_change? true
+    end
+  end
+end
+```
+
+## Mix Tasks
+
+### Adding Strategies
+**LLM Rule:** Only use when strategy doesn't exist. Check first: `AshAuthentication.Info.strategies/1`
+
+```bash
+# Supported strategies: password, magic_link
+mix ash_authentication.add_strategy password
+mix ash_authentication.add_strategy magic_link
+mix ash_authentication.add_strategy password magic_link  # multiple
+
+# Custom user resource
+mix ash_authentication.add_strategy password --user MyApp.Accounts.User
+
+# OAuth2 NOT supported - requires manual setup
+```
+
+**What mix tasks do:**
+- Add required attributes and identities
+- Configure strategy in authentication block
+- Generate actions and sender modules
+- Add dependencies (bcrypt_elixir for password)
+- Run ash.codegen
+
+## Working with Authentication
+
+### Strategy Protocol
+```elixir
+# Get and use strategies
+strategy = AshAuthentication.Info.strategy!(MyApp.User, :password)
+{:ok, user} = AshAuthentication.Strategy.action(strategy, :sign_in, params)
+
+# List strategies
+strategies = AshAuthentication.Info.strategies(MyApp.User)
+```
+
+### Token Operations
+```elixir
+# User/subject conversion
+subject = AshAuthentication.user_to_subject(user)
+{:ok, user} = AshAuthentication.subject_to_user(subject, MyApp.User)
+
+# Token management
+AshAuthentication.TokenResource.revoke(MyApp.Token, token)
+```
+
+### Web Integration
+```elixir
+# Create auth plug
+defmodule MyAppWeb.AuthPlug do
path: usage-rules.md
user: login: zachdaniel
body: this stuff is also done by the installer.
created_at: 2025-05-26T03:28:58Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.)
+
+## Key Principles
+- Use `mix ash_authentication.add_strategy` for password/magic_link
+- Manual setup required for OAuth2 providers
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource required for OAuth2 strategies
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Basic Setup
+
+Add the AshAuthentication extension to your user resource:
+
+```elixir
+defmodule MyApp.Accounts.User do
+  use Ash.Resource,
+    extensions: [AshAuthentication],
+    domain: MyApp.Accounts
+
+  attributes do
+    uuid_primary_key :id
+    attribute :email, :ci_string, allow_nil?: false
+    attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+  end
+
+  authentication do
+    strategies do
+      password :password do
+        identity_field :email
+        hashed_password_field :hashed_password
+      end
+    end
+  end
+
+  identities do
+    identity :unique_email, [:email]
+  end
+end
+```
+
+## Installation
+
+### Quick Start (Igniter - Recommended)
+```bash
+mix igniter.install ash_authentication
+mix ash_authentication.add_strategy password magic_link
+mix ash.migrate
+```
+
+### Manual Setup (when customization needed)
+```elixir
+# 1. Add supervisor to application.ex
+{AshAuthentication.Supervisor, otp_app: :my_app}
+
+# 2. Token resource
+defmodule MyApp.Accounts.Token do
+  use Ash.Resource,
+    extensions: [AshAuthentication.TokenResource],
+    domain: MyApp.Accounts
+end
+
+# 3. UserIdentity resource (for OAuth2)
+defmodule MyApp.Accounts.UserIdentity do
+  use Ash.Resource,
+    extensions: [AshAuthentication.UserIdentity],
+    domain: MyApp.Accounts
+
+  user_identity do
+    user_resource MyApp.Accounts.User
+  end
+end
+```
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Mix task: `mix ash_authentication.add_strategy password`
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Mix task: `mix ash_authentication.add_strategy magic_link`
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Manual setup only - not supported by mix tasks
+- Requires: UserIdentity resource, custom actions, secrets
+
+## Password Strategy
+```elixir
+# Mix task handles this automatically
+mix ash_authentication.add_strategy password
+
+# Manual setup:
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+```elixir
+# Mix task handles this automatically
+mix ash_authentication.add_strategy magic_link
+
+# Manual setup:
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
+```
+
+## Add-ons
+
+### Confirmation
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  add_ons do
+    confirmation :confirm do
+      monitor_fields [:email]
+      sender MyApp.ConfirmationSender
+    end
+  end
+end
+```
+
+### Log Out Everywhere
+```elixir
+authentication do
+  tokens do
+    store_all_tokens? true
+  end
+
+  add_ons do
+    log_out_everywhere do
+      apply_on_password_change? true
+    end
+  end
+end
+```
+
+## Mix Tasks
+
+### Adding Strategies
+**LLM Rule:** Only use when strategy doesn't exist. Check first: `AshAuthentication.Info.strategies/1`
+
+```bash
+# Supported strategies: password, magic_link
+mix ash_authentication.add_strategy password
+mix ash_authentication.add_strategy magic_link
+mix ash_authentication.add_strategy password magic_link  # multiple
+
+# Custom user resource
+mix ash_authentication.add_strategy password --user MyApp.Accounts.User
+
+# OAuth2 NOT supported - requires manual setup
+```
+
+**What mix tasks do:**
+- Add required attributes and identities
+- Configure strategy in authentication block
+- Generate actions and sender modules
+- Add dependencies (bcrypt_elixir for password)
+- Run ash.codegen
+
+## Working with Authentication
+
+### Strategy Protocol
+```elixir
+# Get and use strategies
+strategy = AshAuthentication.Info.strategy!(MyApp.User, :password)
+{:ok, user} = AshAuthentication.Strategy.action(strategy, :sign_in, params)
+
+# List strategies
+strategies = AshAuthentication.Info.strategies(MyApp.User)
+```
+
+### Token Operations
+```elixir
+# User/subject conversion
+subject = AshAuthentication.user_to_subject(user)
+{:ok, user} = AshAuthentication.subject_to_user(subject, MyApp.User)
+
+# Token management
+AshAuthentication.TokenResource.revoke(MyApp.Token, token)
+```
+
+### Web Integration
+```elixir
+# Create auth plug
+defmodule MyAppWeb.AuthPlug do
+  use AshAuthentication.Plug, otp_app: :my_app
+end
+
+# Add to router
+forward "/auth", MyAppWeb.AuthPlug
+```
+
+### Policies
+```elixir
+policies do
+  bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+    authorize_if always()
+  end
+end
+```
+
+## Common Implementation Patterns
+
+### Pattern: Multiple Authentication Methods
+When users need multiple ways to authenticate:
+
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+    end
+
+    github do
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+```
+
+### Pattern: OAuth2 with User Registration
+When new users can register via OAuth2:
+
+```elixir
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :email
+
+    change AshAuthentication.GenerateTokenChange
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+
+      changeset
+      |> Ash.Changeset.change_attribute(:email, user_info["email"])
+      |> Ash.Changeset.change_attribute(:name, user_info["name"])
+    end
+  end
+end
+```
+
+### Pattern: Multitenant Authentication
+When using Ash multitenancy:
+
+```elixir
+# Pass tenant in strategy actions
+{:ok, user} = AshAuthentication.Strategy.action(strategy, :sign_in, params, tenant: tenant)
path: usage-rules.md
user: login: zachdaniel
body: this manual providing of tenant probably won't be that common. Lets just remove the multi tenant section for now.
created_at: 2025-05-26T03:29:32Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.)
+
+## Key Principles
+- Use `mix ash_authentication.add_strategy` for password/magic_link
+- Manual setup required for OAuth2 providers
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource required for OAuth2 strategies
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Basic Setup
+
+Add the AshAuthentication extension to your user resource:
+
+```elixir
+defmodule MyApp.Accounts.User do
+  use Ash.Resource,
+    extensions: [AshAuthentication],
+    domain: MyApp.Accounts
+
+  attributes do
+    uuid_primary_key :id
+    attribute :email, :ci_string, allow_nil?: false
+    attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+  end
+
+  authentication do
+    strategies do
+      password :password do
+        identity_field :email
+        hashed_password_field :hashed_password
+      end
+    end
+  end
+
+  identities do
+    identity :unique_email, [:email]
+  end
+end
+```
+
+## Installation
+
+### Quick Start (Igniter - Recommended)
+```bash
+mix igniter.install ash_authentication
+mix ash_authentication.add_strategy password magic_link
+mix ash.migrate
+```
+
+### Manual Setup (when customization needed)
+```elixir
+# 1. Add supervisor to application.ex
+{AshAuthentication.Supervisor, otp_app: :my_app}
+
+# 2. Token resource
+defmodule MyApp.Accounts.Token do
+  use Ash.Resource,
+    extensions: [AshAuthentication.TokenResource],
+    domain: MyApp.Accounts
+end
+
+# 3. UserIdentity resource (for OAuth2)
+defmodule MyApp.Accounts.UserIdentity do
+  use Ash.Resource,
+    extensions: [AshAuthentication.UserIdentity],
+    domain: MyApp.Accounts
+
+  user_identity do
+    user_resource MyApp.Accounts.User
+  end
+end
+```
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Mix task: `mix ash_authentication.add_strategy password`
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Mix task: `mix ash_authentication.add_strategy magic_link`
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Manual setup only - not supported by mix tasks
+- Requires: UserIdentity resource, custom actions, secrets
+
+## Password Strategy
+```elixir
+# Mix task handles this automatically
+mix ash_authentication.add_strategy password
+
+# Manual setup:
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+```elixir
+# Mix task handles this automatically
+mix ash_authentication.add_strategy magic_link
+
+# Manual setup:
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
+```
+
+## Add-ons
+
+### Confirmation
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  add_ons do
+    confirmation :confirm do
+      monitor_fields [:email]
+      sender MyApp.ConfirmationSender
+    end
+  end
+end
+```
+
+### Log Out Everywhere
+```elixir
+authentication do
+  tokens do
+    store_all_tokens? true
+  end
+
+  add_ons do
+    log_out_everywhere do
+      apply_on_password_change? true
+    end
+  end
+end
+```
+
+## Mix Tasks
+
+### Adding Strategies
+**LLM Rule:** Only use when strategy doesn't exist. Check first: `AshAuthentication.Info.strategies/1`
+
+```bash
+# Supported strategies: password, magic_link
+mix ash_authentication.add_strategy password
+mix ash_authentication.add_strategy magic_link
+mix ash_authentication.add_strategy password magic_link  # multiple
+
+# Custom user resource
+mix ash_authentication.add_strategy password --user MyApp.Accounts.User
+
+# OAuth2 NOT supported - requires manual setup
+```
+
+**What mix tasks do:**
+- Add required attributes and identities
+- Configure strategy in authentication block
+- Generate actions and sender modules
+- Add dependencies (bcrypt_elixir for password)
+- Run ash.codegen
+
+## Working with Authentication
+
+### Strategy Protocol
+```elixir
+# Get and use strategies
+strategy = AshAuthentication.Info.strategy!(MyApp.User, :password)
+{:ok, user} = AshAuthentication.Strategy.action(strategy, :sign_in, params)
+
+# List strategies
+strategies = AshAuthentication.Info.strategies(MyApp.User)
+```
+
+### Token Operations
+```elixir
+# User/subject conversion
+subject = AshAuthentication.user_to_subject(user)
+{:ok, user} = AshAuthentication.subject_to_user(subject, MyApp.User)
+
+# Token management
+AshAuthentication.TokenResource.revoke(MyApp.Token, token)
+```
+
+### Web Integration
+```elixir
+# Create auth plug
+defmodule MyAppWeb.AuthPlug do
+  use AshAuthentication.Plug, otp_app: :my_app
+end
+
+# Add to router
+forward "/auth", MyAppWeb.AuthPlug
+```
+
+### Policies
+```elixir
+policies do
+  bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+    authorize_if always()
+  end
+end
+```
+
+## Common Implementation Patterns
+
+### Pattern: Multiple Authentication Methods
+When users need multiple ways to authenticate:
+
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+    end
+
+    github do
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+```
+
+### Pattern: OAuth2 with User Registration
+When new users can register via OAuth2:
+
+```elixir
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :email
+
+    change AshAuthentication.GenerateTokenChange
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+
+      changeset
+      |> Ash.Changeset.change_attribute(:email, user_info["email"])
+      |> Ash.Changeset.change_attribute(:name, user_info["name"])
+    end
+  end
+end
+```
+
+### Pattern: Multitenant Authentication
+When using Ash multitenancy:
+
+```elixir
+# Pass tenant in strategy actions
+{:ok, user} = AshAuthentication.Strategy.action(strategy, :sign_in, params, tenant: tenant)
+
+# Tenant is available in sender functions
+sender fn user_or_email, token, opts ->
+  tenant = opts[:tenant]
+  MyApp.Emails.deliver_magic_link(user_or_email, token, tenant)
+end
+```
+
+### Pattern: Custom Token Configuration
+When you need specific token behavior:
+
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+    signing_secret MyApp.Secrets
+    token_lifetime {24, :hours}
+    store_all_tokens? true  # For logout-everywhere functionality
+    require_token_presence_for_authentication? false
+  end
+end
+```
+
+## Troubleshooting
path: usage-rules.md
user: login: zachdaniel
body: lets delete everything from here down.
created_at: 2025-05-26T03:30:36Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,534 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.)
+
+## Key Principles
+- Use `mix ash_authentication.add_strategy` for password/magic_link
+- Manual setup required for OAuth2 providers
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource required for OAuth2 strategies
path: usage-rules.md
user: login: chazwatkins
body: looks like it is optional.  We'll let @jimsynz confirm though.
created_at: 2025-05-26T03:52:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,338 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link, api_key authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
+
+## Key Principles
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource optional for OAuth2 (required for multiple providers per user)
+- API keys require strict policy controls and expiration management
+- Use prefixes for API keys to enable secret scanning compliance
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**API Key** - Token-based authentication for APIs
+- Requires: API key resource, relationship to user, sign-in action
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Requires: custom actions, secrets
+- Optional: UserIdentity resource (for multiple providers per user)
+
+## Password Strategy
+
+```elixir
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+
+```elixir
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## API Key Strategy
+
+```elixir
+# 1. Create API key resource
+defmodule MyApp.Accounts.ApiKey do
+  use Ash.Resource,
+    data_layer: AshPostgres.DataLayer,
+    authorizers: [Ash.Policy.Authorizer]
+
+  actions do
+    defaults [:read, :destroy]
+
+    create :create do
+      primary? true
+      accept [:user_id, :expires_at]
+      change {AshAuthentication.Strategy.ApiKey.GenerateApiKey, prefix: :myapp, hash: :api_key_hash}
+    end
+  end
+
+  attributes do
+    uuid_primary_key :id
+    attribute :api_key_hash, :binary, allow_nil?: false, sensitive?: true
+    attribute :expires_at, :utc_datetime_usec, allow_nil?: false
+  end
+
+  relationships do
+    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
+  end
+
+  calculations do
+    calculate :valid, :boolean, expr(expires_at > now())
+  end
+
+  identities do
+    identity :unique_api_key, [:api_key_hash]
+  end
+
+  policies do
+    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+      authorize_if always()
+    end
+  end
+end
+
+# 2. Add strategy to user resource
+authentication do
+  strategies do
+    api_key do
+      api_key_relationship :valid_api_keys
+      api_key_hash_attribute :api_key_hash
+    end
+  end
+end
+
+# 3. Add relationship to user
+relationships do
+  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
+    filter expr(valid)
+  end
+end
+
+# 4. Add sign-in action to user
+actions do
+  read :sign_in_with_api_key do
+    argument :api_key, :string, allow_nil?: false
+    prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
+  end
+end
+```
+
+**Security considerations:**
+- API keys are hashed for storage security
+- Use policies to restrict API key access to specific actions
+- Check `user.__metadata__[:using_api_key?]` to detect API key authentication
+- Access the API key via `user.__metadata__[:api_key]` for permission checks
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
path: usage-rules.md
user: login: jimsynz
body: ```suggestion
```
created_at: 2025-05-26T22:21:49Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,338 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link, api_key authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
+
+## Key Principles
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource optional for OAuth2 (required for multiple providers per user)
+- API keys require strict policy controls and expiration management
+- Use prefixes for API keys to enable secret scanning compliance
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**API Key** - Token-based authentication for APIs
+- Requires: API key resource, relationship to user, sign-in action
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Requires: custom actions, secrets
+- Optional: UserIdentity resource (for multiple providers per user)
+
+## Password Strategy
+
+```elixir
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+
+```elixir
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## API Key Strategy
+
+```elixir
+# 1. Create API key resource
+defmodule MyApp.Accounts.ApiKey do
+  use Ash.Resource,
+    data_layer: AshPostgres.DataLayer,
+    authorizers: [Ash.Policy.Authorizer]
+
+  actions do
+    defaults [:read, :destroy]
+
+    create :create do
+      primary? true
+      accept [:user_id, :expires_at]
+      change {AshAuthentication.Strategy.ApiKey.GenerateApiKey, prefix: :myapp, hash: :api_key_hash}
+    end
+  end
+
+  attributes do
+    uuid_primary_key :id
+    attribute :api_key_hash, :binary, allow_nil?: false, sensitive?: true
+    attribute :expires_at, :utc_datetime_usec, allow_nil?: false
+  end
+
+  relationships do
+    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
+  end
+
+  calculations do
+    calculate :valid, :boolean, expr(expires_at > now())
+  end
+
+  identities do
+    identity :unique_api_key, [:api_key_hash]
+  end
+
+  policies do
+    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+      authorize_if always()
+    end
+  end
+end
+
+# 2. Add strategy to user resource
+authentication do
+  strategies do
+    api_key do
+      api_key_relationship :valid_api_keys
+      api_key_hash_attribute :api_key_hash
+    end
+  end
+end
+
+# 3. Add relationship to user
+relationships do
+  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
+    filter expr(valid)
+  end
+end
+
+# 4. Add sign-in action to user
+actions do
+  read :sign_in_with_api_key do
+    argument :api_key, :string, allow_nil?: false
+    prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
+  end
+end
+```
+
+**Security considerations:**
+- API keys are hashed for storage security
+- Use policies to restrict API key access to specific actions
+- Check `user.__metadata__[:using_api_key?]` to detect API key authentication
+- Access the API key via `user.__metadata__[:api_key]` for permission checks
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
path: usage-rules.md
user: login: jimsynz
body: ```suggestion
    # If UserIdentity resource is being used
    change AshAuthentication.Strategy.OAuth2.IdentityChange
```
created_at: 2025-05-26T22:23:15Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,338 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link, api_key authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
+
+## Key Principles
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource optional for OAuth2 (required for multiple providers per user)
+- API keys require strict policy controls and expiration management
+- Use prefixes for API keys to enable secret scanning compliance
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**API Key** - Token-based authentication for APIs
+- Requires: API key resource, relationship to user, sign-in action
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Requires: custom actions, secrets
+- Optional: UserIdentity resource (for multiple providers per user)
+
+## Password Strategy
+
+```elixir
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+
+```elixir
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## API Key Strategy
+
+```elixir
+# 1. Create API key resource
+defmodule MyApp.Accounts.ApiKey do
+  use Ash.Resource,
+    data_layer: AshPostgres.DataLayer,
+    authorizers: [Ash.Policy.Authorizer]
+
+  actions do
+    defaults [:read, :destroy]
+
+    create :create do
+      primary? true
+      accept [:user_id, :expires_at]
+      change {AshAuthentication.Strategy.ApiKey.GenerateApiKey, prefix: :myapp, hash: :api_key_hash}
+    end
+  end
+
+  attributes do
+    uuid_primary_key :id
+    attribute :api_key_hash, :binary, allow_nil?: false, sensitive?: true
+    attribute :expires_at, :utc_datetime_usec, allow_nil?: false
+  end
+
+  relationships do
+    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
+  end
+
+  calculations do
+    calculate :valid, :boolean, expr(expires_at > now())
+  end
+
+  identities do
+    identity :unique_api_key, [:api_key_hash]
+  end
+
+  policies do
+    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+      authorize_if always()
+    end
+  end
+end
+
+# 2. Add strategy to user resource
+authentication do
+  strategies do
+    api_key do
+      api_key_relationship :valid_api_keys
+      api_key_hash_attribute :api_key_hash
+    end
+  end
+end
+
+# 3. Add relationship to user
+relationships do
+  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
+    filter expr(valid)
+  end
+end
+
+# 4. Add sign-in action to user
+actions do
+  read :sign_in_with_api_key do
+    argument :api_key, :string, allow_nil?: false
+    prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
+  end
+end
+```
+
+**Security considerations:**
+- API keys are hashed for storage security
+- Use policies to restrict API key access to specific actions
+- Check `user.__metadata__[:using_api_key?]` to detect API key authentication
+- Access the API key via `user.__metadata__[:api_key]` for permission checks
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
path: usage-rules.md
user: login: jimsynz
body: I'm not sure if we should be encouraging the big code generator in the sky to make anonymous functions in DSLs. We've seen that very negatively affect compile time performance.
created_at: 2025-05-26T22:24:13Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,338 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link, api_key authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
+
+## Key Principles
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource optional for OAuth2 (required for multiple providers per user)
+- API keys require strict policy controls and expiration management
+- Use prefixes for API keys to enable secret scanning compliance
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**API Key** - Token-based authentication for APIs
+- Requires: API key resource, relationship to user, sign-in action
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Requires: custom actions, secrets
+- Optional: UserIdentity resource (for multiple providers per user)
+
+## Password Strategy
+
+```elixir
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+
+```elixir
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## API Key Strategy
+
+```elixir
+# 1. Create API key resource
+defmodule MyApp.Accounts.ApiKey do
+  use Ash.Resource,
+    data_layer: AshPostgres.DataLayer,
+    authorizers: [Ash.Policy.Authorizer]
+
+  actions do
+    defaults [:read, :destroy]
+
+    create :create do
+      primary? true
+      accept [:user_id, :expires_at]
+      change {AshAuthentication.Strategy.ApiKey.GenerateApiKey, prefix: :myapp, hash: :api_key_hash}
+    end
+  end
+
+  attributes do
+    uuid_primary_key :id
+    attribute :api_key_hash, :binary, allow_nil?: false, sensitive?: true
+    attribute :expires_at, :utc_datetime_usec, allow_nil?: false
+  end
+
+  relationships do
+    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
+  end
+
+  calculations do
+    calculate :valid, :boolean, expr(expires_at > now())
+  end
+
+  identities do
+    identity :unique_api_key, [:api_key_hash]
+  end
+
+  policies do
+    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+      authorize_if always()
+    end
+  end
+end
+
+# 2. Add strategy to user resource
+authentication do
+  strategies do
+    api_key do
+      api_key_relationship :valid_api_keys
+      api_key_hash_attribute :api_key_hash
+    end
+  end
+end
+
+# 3. Add relationship to user
+relationships do
+  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
+    filter expr(valid)
+  end
+end
+
+# 4. Add sign-in action to user
+actions do
+  read :sign_in_with_api_key do
+    argument :api_key, :string, allow_nil?: false
+    prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
+  end
+end
+```
+
+**Security considerations:**
+- API keys are hashed for storage security
+- Use policies to restrict API key access to specific actions
+- Check `user.__metadata__[:using_api_key?]` to detect API key authentication
+- Access the API key via `user.__metadata__[:api_key]` for permission checks
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
+```
+
+## Add-ons
+
+### Confirmation
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  add_ons do
+    confirmation :confirm do
+      monitor_fields [:email]
+      sender MyApp.ConfirmationSender
+    end
+  end
+end
+```
+
+### Log Out Everywhere
+```elixir
+authentication do
+  tokens do
+    store_all_tokens? true
+  end
+
+  add_ons do
+    log_out_everywhere do
+      apply_on_password_change? true
+    end
+  end
+end
+```
+
+## Working with Authentication
+
+### Strategy Protocol
+```elixir
+# Get and use strategies
+strategy = AshAuthentication.Info.strategy!(MyApp.User, :password)
+{:ok, user} = AshAuthentication.Strategy.action(strategy, :sign_in, params)
+
+# List strategies
+strategies = AshAuthentication.Info.strategies(MyApp.User)
+```
+
+### Token Operations
+```elixir
+# User/subject conversion
+subject = AshAuthentication.user_to_subject(user)
+{:ok, user} = AshAuthentication.subject_to_user(subject, MyApp.User)
+
+# Token management
+AshAuthentication.TokenResource.revoke(MyApp.Token, token)
+```
+
+### Policies
+```elixir
+policies do
+  bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+    authorize_if always()
+  end
+end
+```
+
+## Common Implementation Patterns
+
+### Pattern: Multiple Authentication Methods
+When users need multiple ways to authenticate:
+
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+    end
+
+    github do
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+```
+
+### Pattern: OAuth2 with User Registration
+When new users can register via OAuth2:
+
+```elixir
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :email
+
+    change AshAuthentication.GenerateTokenChange
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+
+      changeset
+      |> Ash.Changeset.change_attribute(:email, user_info["email"])
+      |> Ash.Changeset.change_attribute(:name, user_info["name"])
+    end
+  end
+end
+```
+
+### Pattern: Custom Token Configuration
+When you need specific token behavior:
+
+```elixir
path: usage-rules.md
user: login: jimsynz
body: perhaps we could add a new option to the `Ash.Expr.arg/1`template that can retrieve a sub-path?

```elixir
change set_attribute(:email, arg(:user_info, path: ["email"]))
change set_attribute(:name, arg(:user_info, path: ["name"]))
```
created_at: 2025-05-26T22:29:58Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,338 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link, api_key authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
+
+## Key Principles
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource optional for OAuth2 (required for multiple providers per user)
+- API keys require strict policy controls and expiration management
+- Use prefixes for API keys to enable secret scanning compliance
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**API Key** - Token-based authentication for APIs
+- Requires: API key resource, relationship to user, sign-in action
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Requires: custom actions, secrets
+- Optional: UserIdentity resource (for multiple providers per user)
+
+## Password Strategy
+
+```elixir
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+
+```elixir
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## API Key Strategy
+
+```elixir
+# 1. Create API key resource
+defmodule MyApp.Accounts.ApiKey do
+  use Ash.Resource,
+    data_layer: AshPostgres.DataLayer,
+    authorizers: [Ash.Policy.Authorizer]
+
+  actions do
+    defaults [:read, :destroy]
+
+    create :create do
+      primary? true
+      accept [:user_id, :expires_at]
+      change {AshAuthentication.Strategy.ApiKey.GenerateApiKey, prefix: :myapp, hash: :api_key_hash}
+    end
+  end
+
+  attributes do
+    uuid_primary_key :id
+    attribute :api_key_hash, :binary, allow_nil?: false, sensitive?: true
+    attribute :expires_at, :utc_datetime_usec, allow_nil?: false
+  end
+
+  relationships do
+    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
+  end
+
+  calculations do
+    calculate :valid, :boolean, expr(expires_at > now())
+  end
+
+  identities do
+    identity :unique_api_key, [:api_key_hash]
+  end
+
+  policies do
+    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+      authorize_if always()
+    end
+  end
+end
+
+# 2. Add strategy to user resource
+authentication do
+  strategies do
+    api_key do
+      api_key_relationship :valid_api_keys
+      api_key_hash_attribute :api_key_hash
+    end
+  end
+end
+
+# 3. Add relationship to user
+relationships do
+  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
+    filter expr(valid)
+  end
+end
+
+# 4. Add sign-in action to user
+actions do
+  read :sign_in_with_api_key do
+    argument :api_key, :string, allow_nil?: false
+    prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
+  end
+end
+```
+
+**Security considerations:**
+- API keys are hashed for storage security
+- Use policies to restrict API key access to specific actions
+- Check `user.__metadata__[:using_api_key?]` to detect API key authentication
+- Access the API key via `user.__metadata__[:api_key]` for permission checks
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
path: usage-rules.md
user: login: chazwatkins
body: Would you like me to direct it to create change modules instead?
created_at: 2025-05-26T22:37:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,338 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link, api_key authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
+
+## Key Principles
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource optional for OAuth2 (required for multiple providers per user)
+- API keys require strict policy controls and expiration management
+- Use prefixes for API keys to enable secret scanning compliance
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**API Key** - Token-based authentication for APIs
+- Requires: API key resource, relationship to user, sign-in action
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Requires: custom actions, secrets
+- Optional: UserIdentity resource (for multiple providers per user)
+
+## Password Strategy
+
+```elixir
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+
+```elixir
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## API Key Strategy
+
+```elixir
+# 1. Create API key resource
+defmodule MyApp.Accounts.ApiKey do
+  use Ash.Resource,
+    data_layer: AshPostgres.DataLayer,
+    authorizers: [Ash.Policy.Authorizer]
+
+  actions do
+    defaults [:read, :destroy]
+
+    create :create do
+      primary? true
+      accept [:user_id, :expires_at]
+      change {AshAuthentication.Strategy.ApiKey.GenerateApiKey, prefix: :myapp, hash: :api_key_hash}
+    end
+  end
+
+  attributes do
+    uuid_primary_key :id
+    attribute :api_key_hash, :binary, allow_nil?: false, sensitive?: true
+    attribute :expires_at, :utc_datetime_usec, allow_nil?: false
+  end
+
+  relationships do
+    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
+  end
+
+  calculations do
+    calculate :valid, :boolean, expr(expires_at > now())
+  end
+
+  identities do
+    identity :unique_api_key, [:api_key_hash]
+  end
+
+  policies do
+    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+      authorize_if always()
+    end
+  end
+end
+
+# 2. Add strategy to user resource
+authentication do
+  strategies do
+    api_key do
+      api_key_relationship :valid_api_keys
+      api_key_hash_attribute :api_key_hash
+    end
+  end
+end
+
+# 3. Add relationship to user
+relationships do
+  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
+    filter expr(valid)
+  end
+end
+
+# 4. Add sign-in action to user
+actions do
+  read :sign_in_with_api_key do
+    argument :api_key, :string, allow_nil?: false
+    prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
+  end
+end
+```
+
+**Security considerations:**
+- API keys are hashed for storage security
+- Use policies to restrict API key access to specific actions
+- Check `user.__metadata__[:using_api_key?]` to detect API key authentication
+- Access the API key via `user.__metadata__[:api_key]` for permission checks
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
+```
+
+## Add-ons
+
+### Confirmation
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  add_ons do
+    confirmation :confirm do
+      monitor_fields [:email]
+      sender MyApp.ConfirmationSender
+    end
+  end
+end
+```
+
+### Log Out Everywhere
+```elixir
+authentication do
+  tokens do
+    store_all_tokens? true
+  end
+
+  add_ons do
+    log_out_everywhere do
+      apply_on_password_change? true
+    end
+  end
+end
+```
+
+## Working with Authentication
+
+### Strategy Protocol
+```elixir
+# Get and use strategies
+strategy = AshAuthentication.Info.strategy!(MyApp.User, :password)
+{:ok, user} = AshAuthentication.Strategy.action(strategy, :sign_in, params)
+
+# List strategies
+strategies = AshAuthentication.Info.strategies(MyApp.User)
+```
+
+### Token Operations
+```elixir
+# User/subject conversion
+subject = AshAuthentication.user_to_subject(user)
+{:ok, user} = AshAuthentication.subject_to_user(subject, MyApp.User)
+
+# Token management
+AshAuthentication.TokenResource.revoke(MyApp.Token, token)
+```
+
+### Policies
+```elixir
+policies do
+  bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+    authorize_if always()
+  end
+end
+```
+
+## Common Implementation Patterns
+
+### Pattern: Multiple Authentication Methods
+When users need multiple ways to authenticate:
+
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+    end
+
+    github do
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+```
+
+### Pattern: OAuth2 with User Registration
+When new users can register via OAuth2:
+
+```elixir
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :email
+
+    change AshAuthentication.GenerateTokenChange
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+
+      changeset
+      |> Ash.Changeset.change_attribute(:email, user_info["email"])
+      |> Ash.Changeset.change_attribute(:name, user_info["name"])
+    end
+  end
+end
+```
+
+### Pattern: Custom Token Configuration
+When you need specific token behavior:
+
+```elixir
path: usage-rules.md
user: login: chazwatkins
body: That would be awesome actually.  Anytime you want to access data on an arg, you have to go with an anon function or module.
created_at: 2025-05-26T22:41:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,338 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link, api_key authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
+
+## Key Principles
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource optional for OAuth2 (required for multiple providers per user)
+- API keys require strict policy controls and expiration management
+- Use prefixes for API keys to enable secret scanning compliance
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**API Key** - Token-based authentication for APIs
+- Requires: API key resource, relationship to user, sign-in action
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Requires: custom actions, secrets
+- Optional: UserIdentity resource (for multiple providers per user)
+
+## Password Strategy
+
+```elixir
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+
+```elixir
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## API Key Strategy
+
+```elixir
+# 1. Create API key resource
+defmodule MyApp.Accounts.ApiKey do
+  use Ash.Resource,
+    data_layer: AshPostgres.DataLayer,
+    authorizers: [Ash.Policy.Authorizer]
+
+  actions do
+    defaults [:read, :destroy]
+
+    create :create do
+      primary? true
+      accept [:user_id, :expires_at]
+      change {AshAuthentication.Strategy.ApiKey.GenerateApiKey, prefix: :myapp, hash: :api_key_hash}
+    end
+  end
+
+  attributes do
+    uuid_primary_key :id
+    attribute :api_key_hash, :binary, allow_nil?: false, sensitive?: true
+    attribute :expires_at, :utc_datetime_usec, allow_nil?: false
+  end
+
+  relationships do
+    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
+  end
+
+  calculations do
+    calculate :valid, :boolean, expr(expires_at > now())
+  end
+
+  identities do
+    identity :unique_api_key, [:api_key_hash]
+  end
+
+  policies do
+    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+      authorize_if always()
+    end
+  end
+end
+
+# 2. Add strategy to user resource
+authentication do
+  strategies do
+    api_key do
+      api_key_relationship :valid_api_keys
+      api_key_hash_attribute :api_key_hash
+    end
+  end
+end
+
+# 3. Add relationship to user
+relationships do
+  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
+    filter expr(valid)
+  end
+end
+
+# 4. Add sign-in action to user
+actions do
+  read :sign_in_with_api_key do
+    argument :api_key, :string, allow_nil?: false
+    prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
+  end
+end
+```
+
+**Security considerations:**
+- API keys are hashed for storage security
+- Use policies to restrict API key access to specific actions
+- Check `user.__metadata__[:using_api_key?]` to detect API key authentication
+- Access the API key via `user.__metadata__[:api_key]` for permission checks
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
path: usage-rules.md
user: login: chazwatkins
body: Also, if the anon functions impact compile time performance this much, we should probably direct all users to use modules instead and have a deprecation warning exclusively.  I didn't realize the anon functions had a negative impact on compilation.
created_at: 2025-05-26T22:44:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,338 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link, api_key authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
+
+## Key Principles
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource optional for OAuth2 (required for multiple providers per user)
+- API keys require strict policy controls and expiration management
+- Use prefixes for API keys to enable secret scanning compliance
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**API Key** - Token-based authentication for APIs
+- Requires: API key resource, relationship to user, sign-in action
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Requires: custom actions, secrets
+- Optional: UserIdentity resource (for multiple providers per user)
+
+## Password Strategy
+
+```elixir
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+
+```elixir
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## API Key Strategy
+
+```elixir
+# 1. Create API key resource
+defmodule MyApp.Accounts.ApiKey do
+  use Ash.Resource,
+    data_layer: AshPostgres.DataLayer,
+    authorizers: [Ash.Policy.Authorizer]
+
+  actions do
+    defaults [:read, :destroy]
+
+    create :create do
+      primary? true
+      accept [:user_id, :expires_at]
+      change {AshAuthentication.Strategy.ApiKey.GenerateApiKey, prefix: :myapp, hash: :api_key_hash}
+    end
+  end
+
+  attributes do
+    uuid_primary_key :id
+    attribute :api_key_hash, :binary, allow_nil?: false, sensitive?: true
+    attribute :expires_at, :utc_datetime_usec, allow_nil?: false
+  end
+
+  relationships do
+    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
+  end
+
+  calculations do
+    calculate :valid, :boolean, expr(expires_at > now())
+  end
+
+  identities do
+    identity :unique_api_key, [:api_key_hash]
+  end
+
+  policies do
+    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+      authorize_if always()
+    end
+  end
+end
+
+# 2. Add strategy to user resource
+authentication do
+  strategies do
+    api_key do
+      api_key_relationship :valid_api_keys
+      api_key_hash_attribute :api_key_hash
+    end
+  end
+end
+
+# 3. Add relationship to user
+relationships do
+  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
+    filter expr(valid)
+  end
+end
+
+# 4. Add sign-in action to user
+actions do
+  read :sign_in_with_api_key do
+    argument :api_key, :string, allow_nil?: false
+    prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
+  end
+end
+```
+
+**Security considerations:**
+- API keys are hashed for storage security
+- Use policies to restrict API key access to specific actions
+- Check `user.__metadata__[:using_api_key?]` to detect API key authentication
+- Access the API key via `user.__metadata__[:api_key]` for permission checks
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
path: usage-rules.md
user: login: zachdaniel
body: I don't actually think it's that meaningful of an impact anymore. But in all of our rules we should still show change modules, because ultimately its more idiomatic (or at least its our team's recommendation) to prefer to write modules instead of inline functions, to keep the resource reasonably sized.
created_at: 2025-05-26T23:21:37Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,338 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link, api_key authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
+
+## Key Principles
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource optional for OAuth2 (required for multiple providers per user)
+- API keys require strict policy controls and expiration management
+- Use prefixes for API keys to enable secret scanning compliance
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**API Key** - Token-based authentication for APIs
+- Requires: API key resource, relationship to user, sign-in action
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Requires: custom actions, secrets
+- Optional: UserIdentity resource (for multiple providers per user)
+
+## Password Strategy
+
+```elixir
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+
+```elixir
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## API Key Strategy
+
+```elixir
+# 1. Create API key resource
+defmodule MyApp.Accounts.ApiKey do
+  use Ash.Resource,
+    data_layer: AshPostgres.DataLayer,
+    authorizers: [Ash.Policy.Authorizer]
+
+  actions do
+    defaults [:read, :destroy]
+
+    create :create do
+      primary? true
+      accept [:user_id, :expires_at]
+      change {AshAuthentication.Strategy.ApiKey.GenerateApiKey, prefix: :myapp, hash: :api_key_hash}
+    end
+  end
+
+  attributes do
+    uuid_primary_key :id
+    attribute :api_key_hash, :binary, allow_nil?: false, sensitive?: true
+    attribute :expires_at, :utc_datetime_usec, allow_nil?: false
+  end
+
+  relationships do
+    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
+  end
+
+  calculations do
+    calculate :valid, :boolean, expr(expires_at > now())
+  end
+
+  identities do
+    identity :unique_api_key, [:api_key_hash]
+  end
+
+  policies do
+    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+      authorize_if always()
+    end
+  end
+end
+
+# 2. Add strategy to user resource
+authentication do
+  strategies do
+    api_key do
+      api_key_relationship :valid_api_keys
+      api_key_hash_attribute :api_key_hash
+    end
+  end
+end
+
+# 3. Add relationship to user
+relationships do
+  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
+    filter expr(valid)
+  end
+end
+
+# 4. Add sign-in action to user
+actions do
+  read :sign_in_with_api_key do
+    argument :api_key, :string, allow_nil?: false
+    prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
+  end
+end
+```
+
+**Security considerations:**
+- API keys are hashed for storage security
+- Use policies to restrict API key access to specific actions
+- Check `user.__metadata__[:using_api_key?]` to detect API key authentication
+- Access the API key via `user.__metadata__[:api_key]` for permission checks
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
path: usage-rules.md
user: login: chazwatkins
body: I'll convert the anon function examples to change modules
created_at: 2025-05-27T06:44:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,338 @@
+# AshAuthentication Usage Rules
+
+## Core Concepts
+- **Strategies**: password, OAuth2, magic_link, api_key authentication methods
+- **Tokens**: JWT for stateless authentication
+- **UserIdentity**: links users to OAuth2 providers
+- **Add-ons**: confirmation, logout-everywhere functionality
+- **Actions**: auto-generated by strategies (register, sign_in, etc.), can be overridden on the resource
+
+## Key Principles
+- Always use secrets management - never hardcode credentials
+- Enable tokens for magic_link, confirmation, OAuth2
+- UserIdentity resource optional for OAuth2 (required for multiple providers per user)
+- API keys require strict policy controls and expiration management
+- Use prefixes for API keys to enable secret scanning compliance
+- Check existing strategies: `AshAuthentication.Info.strategies/1`
+
+## Strategy Selection
+
+**Password** - Email/password authentication
+- Requires: `:email`, `:hashed_password` attributes, unique identity
+
+**Magic Link** - Passwordless email authentication
+- Requires: `:email` attribute, sender implementation, tokens enabled
+
+**API Key** - Token-based authentication for APIs
+- Requires: API key resource, relationship to user, sign-in action
+
+**OAuth2** - Social/enterprise login (GitHub, Google, Auth0, Apple, OIDC, Slack)
+- Requires: custom actions, secrets
+- Optional: UserIdentity resource (for multiple providers per user)
+
+## Password Strategy
+
+```elixir
+authentication do
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+      resettable do
+        sender MyApp.PasswordResetSender
+      end
+    end
+  end
+end
+
+# Required attributes:
+attributes do
+  attribute :email, :ci_string, allow_nil?: false, public?: true
+  attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
+end
+
+identities do
+  identity :unique_email, [:email]
+end
+```
+
+## Magic Link Strategy
+
+```elixir
+authentication do
+  strategies do
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+
+# Sender implementation required:
+defmodule MyApp.MagicLinkSender do
+  use AshAuthentication.Sender
+
+  def send(user_or_email, token, _opts) do
+    MyApp.Emails.deliver_magic_link(user_or_email, token)
+  end
+end
+```
+
+## API Key Strategy
+
+```elixir
+# 1. Create API key resource
+defmodule MyApp.Accounts.ApiKey do
+  use Ash.Resource,
+    data_layer: AshPostgres.DataLayer,
+    authorizers: [Ash.Policy.Authorizer]
+
+  actions do
+    defaults [:read, :destroy]
+
+    create :create do
+      primary? true
+      accept [:user_id, :expires_at]
+      change {AshAuthentication.Strategy.ApiKey.GenerateApiKey, prefix: :myapp, hash: :api_key_hash}
+    end
+  end
+
+  attributes do
+    uuid_primary_key :id
+    attribute :api_key_hash, :binary, allow_nil?: false, sensitive?: true
+    attribute :expires_at, :utc_datetime_usec, allow_nil?: false
+  end
+
+  relationships do
+    belongs_to :user, MyApp.Accounts.User, allow_nil?: false
+  end
+
+  calculations do
+    calculate :valid, :boolean, expr(expires_at > now())
+  end
+
+  identities do
+    identity :unique_api_key, [:api_key_hash]
+  end
+
+  policies do
+    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+      authorize_if always()
+    end
+  end
+end
+
+# 2. Add strategy to user resource
+authentication do
+  strategies do
+    api_key do
+      api_key_relationship :valid_api_keys
+      api_key_hash_attribute :api_key_hash
+    end
+  end
+end
+
+# 3. Add relationship to user
+relationships do
+  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
+    filter expr(valid)
+  end
+end
+
+# 4. Add sign-in action to user
+actions do
+  read :sign_in_with_api_key do
+    argument :api_key, :string, allow_nil?: false
+    prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
+  end
+end
+```
+
+**Security considerations:**
+- API keys are hashed for storage security
+- Use policies to restrict API key access to specific actions
+- Check `user.__metadata__[:using_api_key?]` to detect API key authentication
+- Access the API key via `user.__metadata__[:api_key]` for permission checks
+
+## OAuth2 Strategies
+
+**Supported providers:** github, google, auth0, apple, oidc, slack
+
+**Required for all OAuth2:**
+- UserIdentity resource
+- Custom `register_with_[provider]` action
+- Secrets management
+- Tokens enabled
+
+### OAuth2 Configuration Pattern
+```elixir
+# Strategy configuration
+authentication do
+  strategies do
+    github do  # or google, auth0, apple, oidc, slack
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      # auth0 also needs: base_url
+      # apple also needs: team_id, private_key_id, private_key_path
+      # oidc also needs: openid_configuration_uri
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+  end
+end
+
+# Required action (replace 'github' with provider name)
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :unique_email
+
+    change AshAuthentication.GenerateTokenChange
+    change AshAuthentication.Strategy.OAuth2.IdentityChange
+
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+      Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
+    end
+  end
+end
+```
+
+## Add-ons
+
+### Confirmation
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  add_ons do
+    confirmation :confirm do
+      monitor_fields [:email]
+      sender MyApp.ConfirmationSender
+    end
+  end
+end
+```
+
+### Log Out Everywhere
+```elixir
+authentication do
+  tokens do
+    store_all_tokens? true
+  end
+
+  add_ons do
+    log_out_everywhere do
+      apply_on_password_change? true
+    end
+  end
+end
+```
+
+## Working with Authentication
+
+### Strategy Protocol
+```elixir
+# Get and use strategies
+strategy = AshAuthentication.Info.strategy!(MyApp.User, :password)
+{:ok, user} = AshAuthentication.Strategy.action(strategy, :sign_in, params)
+
+# List strategies
+strategies = AshAuthentication.Info.strategies(MyApp.User)
+```
+
+### Token Operations
+```elixir
+# User/subject conversion
+subject = AshAuthentication.user_to_subject(user)
+{:ok, user} = AshAuthentication.subject_to_user(subject, MyApp.User)
+
+# Token management
+AshAuthentication.TokenResource.revoke(MyApp.Token, token)
+```
+
+### Policies
+```elixir
+policies do
+  bypass AshAuthentication.Checks.AshAuthenticationInteraction do
+    authorize_if always()
+  end
+end
+```
+
+## Common Implementation Patterns
+
+### Pattern: Multiple Authentication Methods
+When users need multiple ways to authenticate:
+
+```elixir
+authentication do
+  tokens do
+    enabled? true
+    token_resource MyApp.Accounts.Token
+  end
+
+  strategies do
+    password :password do
+      identity_field :email
+      hashed_password_field :hashed_password
+    end
+
+    github do
+      client_id MyApp.Secrets
+      client_secret MyApp.Secrets
+      redirect_uri MyApp.Secrets
+      identity_resource MyApp.Accounts.UserIdentity
+    end
+
+    magic_link do
+      identity_field :email
+      sender MyApp.MagicLinkSender
+    end
+  end
+end
+```
+
+### Pattern: OAuth2 with User Registration
+When new users can register via OAuth2:
+
+```elixir
+actions do
+  create :register_with_github do
+    argument :user_info, :map, allow_nil?: false
+    argument :oauth_tokens, :map, allow_nil?: false
+    upsert? true
+    upsert_identity :email
+
+    change AshAuthentication.GenerateTokenChange
+    change fn changeset, _ctx ->
+      user_info = Ash.Changeset.get_argument(changeset, :user_info)
+
+      changeset
+      |> Ash.Changeset.change_attribute(:email, user_info["email"])
+      |> Ash.Changeset.change_attribute(:name, user_info["name"])
+    end
+  end
+end
+```
+
+### Pattern: Custom Token Configuration
+When you need specific token behavior:
+
+```elixir
path: usage-rules.md
user: login: jimsynz
body: Okay, well after talking this over with @zachdaniel we have some ideas for how to make Ash's change DSL more expressive without having to drop down to custom changes so much but let's not block this PR waiting for a theoretical future feature.
created_at: 2025-05-27T23:12:17Z
author_association: COLLABORATOR

PR comments:
user: login: zachdaniel
created_at: 2025-05-26T03:32:08Z
body: As you can see I've taken a hatched to thinks in terms of how I've reviewed it. Ultimately the mentality I've taken w/ this stuff is "if it could possibly confuse an LLM and isn't 100% adding valuable context, then cut it". We can always add things back in over time when we see what mistakes get made.
author_association: COLLABORATOR
user: login: chazwatkins
created_at: 2025-05-26T03:42:29Z
body: > ### Adding Strategies

100% - I had trimmed it from 1200 lines to 600 before submitting.  Keeping it small and only to what the LLM needs to know makes sense.
author_association: CONTRIBUTOR
user: login: chazwatkins
created_at: 2025-05-26T03:44:57Z
body: Ok, I think I covered all the changes you requested
author_association: CONTRIBUTOR
user: login: zachdaniel
created_at: 2025-05-26T04:00:30Z
body: @jimsynz LGTM but you should do a pass when you get a chance 🙇 
author_association: COLLABORATOR