documentation/tutorials/api-keys.md

# API Keys

## A note on API Keys

API keys are generated using `AshAuthentication.Strategies.ApiKey.GenerateApiKey`. See the module docs for more information.
The API key is generated using a random byte string and a prefix. The prefix is used to generate a key that is compliant with secret scanning. You can use this to set up an endpoint that will automatically revoke leaked tokens, which is an extremely powerful and useful security feature. We only store a hash of the api key. The plaintext api key is only available in `api_key.__metadata__.plaintext_api_key` immediately after creation.

See [the guide on Github](https://docs.github.com/en/code-security/secret-scanning/secret-scanning-partnership-program/secret-scanning-partner-program) for more information.

Api key expiration/validity is otherwise up to you. The configured `api_key_relationship` should include those rules in the filter.
For example:

```elixir
has_many :valid_api_keys, MyApp.Accounts.ApiKey do
  filter expr(valid)
end
```

## Installation

<!-- tabs-open -->

### With Igniter (recommended)

Use `mix ash_authentication.add_strategy api_key` to install this strategy, and modify the generated resource
to suit your needs.

### Manually

#### Create an API key resource

```elixir
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 do
      allow_nil? false
      sensitive? true
    end

    # In this example, all api keys have an expiration
    # Feel free to rework this however you please
    attribute :expires_at, :utc_datetime_usec do
      allow_nil? false
    end
  end

  relationships do
    belongs_to :user, MyApp.Accounts.User do
      allow_nil? false
    end
  end

  calculations do
    calculate :valid, :boolean, expr(expires_at > now())
  end

  identities do
    identity :unique_api_key, [:api_key_hash]
  end

  policies do
    # Allow AshAuthentication to work with api keys as necessary
    bypass always() do
      authorize_if AshAuthentication.Checks.AshAuthenticationInteraction
    end
  end
end
```

#### Add the strategy to your user

```elixir
authentication do
  ...
  strategies do
    api_key do
      api_key_relationship :valid_api_keys
    end
  end
end
```

#### Relate users to valid api keys

```elixir
relationships do
  has_many :valid_api_keys, MyApp.Accounts.ApiKey do
    filter expr(valid)
  end
end
```

#### Add the sign_in_with_api_key action

Add the action to your user resource

```elixir
read :sign_in_with_api_key do
  argument :api_key, :string, allow_nil?: false
  prepare AshAuthentication.Strategy.ApiKey.SignInPreparation
end
```

#### Use the plug in your router/plug pipeline

See `AshAuthentication.Strategies.ApiKey.Plug` for all available options.

In Phoenix, for example, you might add this plug to your
`:api` pipeline.

```elixir
pipeline :api do
  ...
  plug AshAuthentication.Strategy.ApiKey.Plug,
    resource: MyApp.Accounts.User
end
```

<!-- tabs-close -->