Security advisory

OAuth consent-scope forgery allows any customer account to obtain administrative scopes (Galoy/Blink stack)

October 6, 2026

Summary

An authorization flaw in the Galoy/Blink OAuth consent application allowed an ordinary customer to obtain scopes the OAuth client had not requested. In affected deployments, the administrative gateway accepted these tokens and the admin API trusted their scopes, enabling customer-data access and account takeover through contact-detail changes. The primary source fix is included in version 0.22.65; operators must update the consent and API components and configure the admin gateway correctly.

Root cause

Three chained defects allowed any registered customer account of an affected deployment to obtain full administrative
OAuth scopes:

  1. The OAuth consent application (apps/consent) accepted the grant_scope list from the browser's form submission
    and passed it to ORY Hydra's consent-accept endpoint without intersecting it with the client's requested or
    registered scopes. Hydra trusted the consent app and minted a valid OAuth access token carrying whatever scopes the
    submitter declared, including the full administrative scope set.
  2. The administrative API's ORY Oathkeeper rule used an oauth2_introspection path configured for service clients
    in this deployment, with no required_scope, no target_audience, no trusted_issuers and no client restriction.
    It accepted active Hydra tokens and copied their subject and scopes into an Oathkeeper-signed JWT for the admin API.
  3. The admin GraphQL server treated that JWT's scope string as the entire authorization decision (no subject
    allowlist, no JWT audience check).

The attack requires no signing-key compromise or cryptographic token modification: the identity provider issues the
over-scoped tokens, so standard token-signature validation does not detect the attack.

Affected

  • Archived upstream GaloyMoney/blink contains the vulnerable consent code from commit
    59150be0e049a28d432c9c3200f4f649bfbf8b83 (PR #3339,
    2023-10-12) through its final revision 6c536737a327013f2ebe0a37091a3277ec802373.
    The earlier admin authentication changes were PR #3284, commit
    c46a6939ee102f391d65a55133f9f1b99b4dc849 (2023-10-01), and
    PR #3292, commit
    be54143fcf6fc395b34359c3d85072c5bed8e943 (2023-10-09).
  • blinkbitcoin/blink inherited this code. Source revisions containing the introducing commit and lacking the fix
    are affected under the conditions below; source tag 0.22.64 predates the fix. The primary source correction is
    commit 4d9a4b1fbb0537b31a48a0c05ea42222ae26892d, included in tag 0.22.65.
  • The administrative escalation affects deployments that expose the vulnerable consent application to customers
    and accept customer OAuth tokens on an admin gateway path that forwards their scopes without independently
    establishing administrative authorization. The admin API then authorizes operations from those scopes.
    A required-scope check alone is insufficient when those scopes can be forged. Deployments with an equivalent
    backported consent fix or controls that independently reject customer access to the admin API are not susceptible
    to this chain; a gateway restriction alone does not repair the consent vulnerability itself.

Patches

  1. Deploy the consent and admin API corrections in source tag 0.22.65 or a later revision retaining the fix, or
    backport #853. The consent handler rejects grants exceeding requested
    scopes, and the admin API validates a dedicated JWT audience. Update both components, which may be deployed separately.
  2. Keep ADMIN_API_JWT_AUDIENCE set to galoy-admin or an equivalent dedicated value shared with the trusted admin
    token issuer. PR #855 permits an empty or whitespace-only value to disable this check for rollout compatibility;
    do not leave that opt-out enabled: #855.
  3. Update the operator's gateway configuration to remove the permissive customer-token path or restrict it to
    independently authorized administrative clients. Ensure only authorized admin paths mint the dedicated audience.
    Adding the admin audience to every introspected customer token is not an authorization check. PR #853 updates
    development gateway rules; production gateway configurations require their own update.

The contact-change freeze in #861 is additional containment, not a correction
of scope issuance. Its configuration can disable the freeze, and it does not prevent administrative data reads.

Workarounds

  • Restrict the administrative API to trusted operators through a VPN or private network, including alternate routes
    to the same service. This reduces exposure while the code and gateway corrections are deployed.
  • Freeze the admin mutations that change customer email and phone as partial containment. Other administrative
    operations and data reads remain exposed wherever the vulnerable authorization path is still reachable.

Detection and recovery

  • Review retained OAuth grants, tokens and issuance records for unexpected administrative scopes on customer
    identities. These are indicators requiring investigation; distinguish legitimate authorized grants and test or
    operator identities before concluding that exploitation occurred. An empty current token store does not rule out
    historical exploitation because tokens may have expired or been removed.
  • When compromise is suspected, preserve relevant evidence, close the vulnerable issuance and gateway paths, and
    revoke unauthorized grants, access tokens and refresh tokens. Invalidate affected login sessions and restore
    unauthorized account changes through verified recovery procedures. Ensure downstream token validation and caches
    cannot continue accepting revoked authority. Correcting future consent grants does not invalidate privileges
    that were already issued.

Operators of derived deployments can request a short vulnerable/patched check procedure at bounty@blinkbtc.com; we share
it once we have confirmed that the requester operates a deployment.

References

Proof of concept

Source-level evidence is available in the public repository:

  1. In the pre-fix consent handler, browser-submitted grant_scope values are read from the form and passed to Hydra's acceptOAuth2ConsentRequest without checking that they are a subset of requested_scope:
    https://github.com/blinkbitcoin/blink/blob/0.22.64/apps/consent/app/consent/page.tsx
  2. The pre-fix development admin gateway accepts OAuth tokens through introspection and copies their subject and scopes into an internal JWT:
    https://github.com/blinkbitcoin/blink/blob/0.22.64/dev/config/ory/oathkeeper_rules.yaml
  3. The corrective diff adds the missing scope-subset check, rejects extra scopes with invalid_scope, and adds dedicated audience validation to the admin API:
    #853

For an isolated deployment with the affected gateway configuration, the vulnerable behavior is issuance of an OAuth token carrying consent-submitted scopes beyond those originally requested by the client, followed by acceptance of that authority by the admin API. With the corrected consent handler, the same out-of-request grant must be rejected with invalid_scope. Independently, the corrected admin API must reject JWTs without its configured admin audience, and the gateway must reject customer access to administrative paths.

The complete vulnerable/patched reproduction procedure is shared with maintainers and verified deployment operators through bounty@blinkbtc.com. This public evidence describes the source defect and expected validation outcomes; it does not include the operator-only execution runbook.

Impact

This is an incorrect-authorization vulnerability (CWE-863) that permits escalation from an ordinary customer account to administrative privileges. It affects operators and customers of deployments running the vulnerable consent code with the permissive administrative authorization path described in Details. No existing administrator credentials or interaction by another user are required.

Impact on affected deployments: complete administrative API access, including read access to customer account data (balances,
transaction history, contact details, auth methods), mutation of customer contact details (enabling account takeover via
passwordless login), account-level changes (withdrawal-limit raises) and other administrative operations. Money movement
additionally depends on each deployment's payment rails. In the September 2026 Blink incident this chain enabled the
takeover of 35 custodial accounts and the drain of 24 (about 6.61 BTC net proceeds to the attacker).
See the post-mortem linked in References.

‍

October 6, 2026