OpenFeed (OpenFeed v0.1.0)

Copy Markdown View Source

An Elixir client for OpenFeed — Australian Consumer Data Right banking and energy data.

OpenFeed implements the FAPI 2.0 Security Profile. This library's job is to make that a detail you do not think about: PAR, PKCE S256, DPoP proofs on every call, private_key_jwt client authentication, and the key management that implies.

Using Ash? See ash_openfeed, which builds on this and adds grant persistence, token refresh and an installer.

Where to look

ForSee
ConfigurationOpenFeed.Config
KeysOpenFeed.KeyStore, mix openfeed.gen.key, mix openfeed.jwks
The consent flowOpenFeed.Auth
Reading dataOpenFeed.Sharing
ErrorsOpenFeed.Error

Setting up

Generate a key and register its public half with OpenFeed:

mix openfeed.gen.key --path priv/openfeed.jwk
mix openfeed.jwks --path priv/openfeed.jwk

Build a config and add this module to your supervision tree, which starts the OIDC discovery worker so metadata is fetched once rather than per call:

config =
  OpenFeed.Config.new!(
    client_id: System.fetch_env!("OPENFEED_CLIENT_ID"),
    redirect_uri: "https://my.app/openfeed/callback",
    key_store: {OpenFeed.KeyStore.File, path: "priv/openfeed.jwk"}
  )

children = [{OpenFeed, config}, MyApp.Repo, MyAppWeb.Endpoint]
# 1. Starting out — keep all three values in the session.
flow = OpenFeed.Auth.new_flow()
{:ok, url} = OpenFeed.authorize_url(config, Enum.to_list(flow))
redirect(conn, external: url)

# 2. At your callback, having checked `state` matches.
{:ok, tokens} =
  OpenFeed.exchange_code(config, code,
    nonce: flow.nonce,
    pkce_verifier: flow.pkce_verifier
  )

# 3. Later, when the access token is close to expiry.
{:ok, tokens} = OpenFeed.refresh(config, tokens.refresh_token, tokens.sub)

Reading data

{:ok, accounts} = OpenFeed.Sharing.banking_accounts(config, tokens.access_token)

{:ok, transactions} =
  OpenFeed.Sharing.banking_transactions(
    config,
    tokens.access_token,
    account["accountId"],
    oldest_date: Date.add(Date.utc_today(), -365)
  )

Branch on OpenFeed.Error's :kind rather than on HTTP status — in particular, :grant_revoked means stop and ask the consumer to reconnect, while :subject_mismatch is also a 403 but means something has gone wrong on your side.

Summary

Functions

authorize_url(config, opts)

See OpenFeed.Auth.authorize_url/2.

child_spec(config)

@spec child_spec(OpenFeed.Config.t()) :: Supervisor.child_spec()

Child spec for the OIDC discovery worker, so {OpenFeed, config} works directly in a supervision tree.

Delegates to OpenFeed.ProviderConfiguration.

client_credentials_token(config, scopes \\ [:grant_list])

See OpenFeed.Auth.client_credentials_token/2.

exchange_code(config, code, opts)

See OpenFeed.Auth.exchange_code/3.

introspect(config, access_token)

See OpenFeed.Auth.introspect/2.

new_flow()

See OpenFeed.Auth.new_flow/0.

refresh(config, refresh_token, expected_subject)

See OpenFeed.Auth.refresh/3.