Everything this library needs to talk to OpenFeed, as an explicit struct.
This package is config-injected, not config-reading: nothing here reads
Application.get_env/2. Every entry point takes a OpenFeed.Config.
That is a deliberate design choice. It means the FAPI 2.0 surface can be
tested against a stub with no application environment at all, and it means an
application can hold more than one OpenFeed registration (one per tenant, say)
without this library needing to know. Deciding where the values come from —
runtime.exs, a secrets manager, a database row — is the caller's job.
Building one
OpenFeed.Config.new!(
client_id: System.fetch_env!("OPENFEED_CLIENT_ID"),
redirect_uri: "https://my.app/openfeed/callback",
key_store: {OpenFeed.KeyStore.File, path: "/run/secrets/openfeed.jwk"}
)Client authentication
OpenFeed offers two client profiles, and the difference matters:
:private_key_jwt(default) — the Recommended profile. You hold a keypair, register its public JWKS with OpenFeed, and no client secret is ever transmitted. The same key signs DPoP proofs. Requires a:key_store.:client_secret_basic— the Legacy profile. Requires:client_secret, and gets you bearer tokens rather than DPoP sender-constrained ones.
Prefer :private_key_jwt unless you have a reason not to.
Testing
:req_options is merged into every HTTP request this library makes, which is
the seam for stubbing:
OpenFeed.Config.new!(
client_id: "test",
issuer: "https://auth.example.test",
req_options: [plug: {Req.Test, MyStub}],
...
)
Summary
Functions
Default sharing-api base URL (https://api.openfeed.au).
Default issuer (https://auth.openfeed.au).
Build and validate a config.
Like new/1 but raises ArgumentError on invalid config.
The scopes to request, expanded to full scope strings.
Types
@type t() :: %OpenFeed.Config{ api_base: String.t(), auth_method: :private_key_jwt | :client_secret_basic, client_id: String.t() | nil, client_secret: String.t() | nil, issuer: String.t(), key_store: key_store() | nil, provider_name: atom(), redirect_uri: String.t() | nil, req_options: keyword(), scopes: [atom() | String.t()] }
Functions
@spec default_api_base() :: String.t()
Default sharing-api base URL (https://api.openfeed.au).
@spec default_issuer() :: String.t()
Default issuer (https://auth.openfeed.au).
Build and validate a config.
Returns {:error, {:invalid_config, message}} rather than raising, so
callers can surface configuration problems as ordinary errors.
Like new/1 but raises ArgumentError on invalid config.
The scopes to request, expanded to full scope strings.