Skip to content

feat: emit :reauthorization_required telemetry event - #6

Closed
helording wants to merge 15 commits into
NexPB:masterfrom
helording:reauthorization-required-telemetry-upstream
Closed

feat: emit :reauthorization_required telemetry event#6
helording wants to merge 15 commits into
NexPB:masterfrom
helording:reauthorization-required-telemetry-upstream

Conversation

@helording

Copy link
Copy Markdown
Contributor

Stacked on #3 (keepalive-refresher). This branch is built on top of #3, so the diff currently shows #3's commits too — please review/merge after #3, at which point this reduces to just the commit below. The substantive change is the single commit "feat: emit :reauthorization_required telemetry event".

What

Adds a dedicated telemetry event, [:ex_shopify_app, :access_token, :refresh, :reauthorization_required], that fires whenever an access-token operation resolves to :reauthorization_required — i.e. the token chain can no longer be refreshed and the merchant must relaunch the app to reauthorize.

Why

This pairs naturally with the heartbeat refresher in #3: the heartbeat keeps healthy chains alive, but a chain whose refresh token has already crossed the 90-day cliff can only be recovered by the merchant re-authorizing. :reauthorization_required is the one outcome a host app needs to act on (prompt re-auth, alert, flag the shop), but until now it was easy to miss:

  • valid_token/2 short-circuits on an already-expired refresh token before refresh_token/2, so it emitted no telemetry at all.
  • On the refresh paths, the signal was only buried in the :stop event's :result metadata, alongside every successful refresh — there was no event a handler could subscribe to directly.

How

A single public emitter, Telemetry.reauthorization_required/1, is now called on every path that yields the result:

  • valid_token/2's early short-circuit (explicit call, since it never enters the refresh span).
  • The locked refresh re-checking expiry under the row lock, and Shopify rejecting a refresh with invalid_grant — both flow through refresh_stop/3, which emits the event when the classified result is :reauthorization_required.

Metadata carries :shopify_domain only — never token values, consistent with the other events in this module.

Tests

Adds coverage for all three paths in RepoTest (already-expired refresh token via valid_token/2 and via refresh_token/2, and a Shopify invalid_grant). Full suite green (60 tests), mix format clean, compiles with --warnings-as-errors.

🤖 Generated with Claude Code

helording and others added 15 commits June 15, 2026 10:54
…ken expiry

A dormant shop's access token may sit fresh or untouched while its refresh
token approaches the 90-day cliff, after which only the merchant can restore
access. The new option forces a locked refresh when the refresh token expires
inside the window, with the same lock-and-recheck dedup and stale-fallback
semantics as the soft window. Groundwork for the KeepAlive process.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A supervision-tree GenServer that scans for chains whose refresh token
expires inside a window and rotates them via the store's lock-serialized
refresh_token/2 (refresh_token_window option). Multi-node safe: the row lock
plus in-lock re-check collapse concurrent ticks into one Shopify call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolve conflicts in mix.exs and repo_test.exs, and adapt master's
migrate/2 tests and migrations test to the new Mox + ex_machina fixture
structure introduced on this branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve conflicts in mix.exs and test/ex_shopify_app/access_token/repo_test.exs.
Adapt master's migrate/2 and migrations tests to this branch's new Mox +
ex_machina fixture structure (replacing the Agent counter / Tesla.Mock setup).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Surface a dedicated `[:ex_shopify_app, :access_token, :refresh,
:reauthorization_required]` event whenever a call resolves to
`:reauthorization_required`, so host apps can react to chains that can no
longer be refreshed (the merchant must relaunch the app to reauthorize).

It fires on every path that produces the result:

  * `valid_token/2` short-circuiting on an already-expired refresh token
    before any HTTP call (previously emitted no telemetry at all);
  * the locked refresh re-checking expiry under the row lock;
  * Shopify rejecting a refresh with `invalid_grant`.

The latter two flow through `refresh_stop/3`, which now emits the event when
the classified result is `:reauthorization_required`. Metadata carries
`:shopify_domain` only — never token values.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@helording

Copy link
Copy Markdown
Contributor Author

Folding this change directly into #3 (keepalive-refresher) instead of carrying a separate PR for the telemetry event. The commit now lives on the #3 branch.

@helording helording closed this Jun 25, 2026
@helording
helording deleted the reauthorization-required-telemetry-upstream branch June 25, 2026 01:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant