README.md

# AshAtproto

Ash utilities built on top of [atex](https://tangled.org/comet.sh/atex) to make integrating with ATProto easier. It includes:

- An Ash extension that provides resource actions and attributes for an user.
- An `AshAuthentication` strategy to enable the OAuth flow.
- AN XRPC client to make requests using an user resource.

## Installation

The package can be installed by adding `ash_atproto` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:ash_atproto, "~> 0.1.0"}
  ]
end
```

## User resource extension

This extension modifies your resource to add the following attributes:

- `:did`: an user's DID. This will be used as a primary key.
- `:handle`: an user's handle. This is cached for easier access, but it can become stale.
- `:oauth_tokens`: a struct with the OAuth response data.

These attributes can be overridden if necessary, and new attributes can be added with no issues.

It also includes the following actions:

- `:register_with_atproto`: upsert used for both registers and sign ins.
- `:refresh`: updates the `:oauth_tokens` attribute.
- `:read`
- `:destroy`

### Example

```elixir
defmodule MyApp.Accounts.User do
  use Ash.Resource,
    otp_app: :my_app,
    domain: MyApp.Accounts,
    authorizers: [Ash.Policy.Authorizer],
    extensions: [AshAuthentication, AshAtproto.Auth, AshAtproto.UserResource],
    data_layer: AshPostgres.DataLayer

  authentication do
    tokens do
      enabled? true
      require_token_presence_for_authentication? true
      token_resource MyApp.Accounts.Token
      signing_secret MyApp.Secrets
      store_all_tokens? true
    end

    strategies do
      atproto do
        registration_enabled? true
        base_url MyApp.Secrets

        private_key MyApp.Secrets
        key_id(MyApp.Secrets)
      end
    end
  end

  postgres do
    table "users"
    repo MyApp.Repo
  end

  actions do
    read :get_by_subject do
      description "Get a user by the subject claim in a JWT"
      argument :subject, :string, allow_nil?: false
      get? true
      prepare AshAuthentication.Preparations.FilterBySubject
    end
  end

  policies do
    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
      authorize_if always()
    end

    policy always() do
      forbid_if always()
    end
  end
end
```

## AshAuthentication strategy

A strategy that enables seamless integration with the rest of [AshAuthentication](https://hexdocs.pm/ash_authentication/get-started.html). The atproto strategy is standalone, and can be used without the user resource extension (replacing it with `AshAuthentication.UserIdentity`, for example).

### Example

```elixir
use Ash.Resource,
  extensions: [AshAuthentication, AshAtproto.Auth],

strategies do
  atproto do
    registration_enabled? true
    base_url MyApp.Secrets

    private_key MyApp.Secrets
    key_id(MyApp.Secrets)
  end
end
```

Configuration values can be passed directly, but using [AshAuthentication.Secret](https://hexdocs.pm/ash_authentication/AshAuthentication.Secret.html) is recommended.

## OAuth client

An HTTP API client that follows the [XRPC](https://atproto.com/specs/xrpc) conventions. Will automatically refresh tokens if needed.

### Example

```elixir
user_resource = Ash.read_one!(MyApp.Accounts.User, authorize?: false)
{:ok, client} = AshAtproto.XRPC.OAuthClient.new(user_resource)

# Make XRPC requests
{:ok, response, client} =
  Atex.XRPC.get(client, "com.atproto.repo.listRecords",
    params: [repo: user.handle, collection: "app.bsky.graph.follow"]
  )
```

For more details, including lexicon code generation and typed parameters, check the [Atex.XRPC](https://hexdocs.pm/atex/Atex.XRPC.html) module.

Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) and published on [HexDocs](https://hexdocs.pm). Once published, the docs can be found at <https://hexdocs.pm/ash_atproto>.