AshOpenFeed.KeyStore.Ash (AshOpenFeed v0.1.0)

Copy Markdown View Source

Stores the OpenFeed signing key as a row in an Ash resource.

Multi-node safe, because every node reads the same row. This is the default the installer picks, for that reason.

The resource

mix ash_openfeed.install generates one. By hand it is:

defmodule MyApp.OpenFeed.Key do
  use Ash.Resource,
    domain: MyApp.OpenFeed,
    data_layer: AshPostgres.DataLayer

  postgres do
    table "openfeed_keys"
    repo MyApp.Repo
  end

  actions do
    defaults [:read, :destroy, create: :*, update: :*]
  end

  attributes do
    uuid_primary_key :id
    attribute :name, :string, allow_nil?: false, default: "default"
    attribute :jwk, :string, allow_nil?: false, sensitive?: true
    timestamps()
  end

  identities do
    identity :unique_name, [:name]
  end
end

Configure it

openfeed do
  key_store {AshOpenFeed.KeyStore.Ash, resource: MyApp.OpenFeed.Key}
end

Options

  • :resource (required) — the Ash resource holding keys.
  • :name — which key row. Defaults to "default". Use distinct names to hold a key per tenant.
  • :tenant, :actor, :authorize? — passed to Ash.read/2.
  • :cache — see OpenFeed.KeyStore. Defaults to true, which matters here because otherwise every DPoP proof is a database round trip.

Encrypt it

A private key in a database table deserves encryption more than the tokens do, because it is the thing that makes a stolen token useless. sensitive? keeps it out of inspect output but does not encrypt it:

use Ash.Resource, extensions: [AshCloak.Resource]

cloak do
  vault MyApp.Vault
  attributes [:jwk]
end

If your vault key and your database live in the same blast radius, consider OpenFeed.KeyStore.Env instead, so the signing key is not recoverable from a database backup alone.