Skip to content

fix: require a validated service token for external delivery in all environments#92

Merged
Bccorb merged 1 commit into
mainfrom
fix/require-service-token-for-external-delivery
Jul 20, 2026
Merged

fix: require a validated service token for external delivery in all environments#92
Bccorb merged 1 commit into
mainfrom
fix/require-service-token-for-external-delivery

Conversation

@Bccorb

@Bccorb Bccorb commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

What changed

External delivery returns OTP and magic-link secrets in the response body instead of sending them. It now requires a validated internal service token in every environment.

  • canReturnExternalDelivery no longer treats a non-production NODE_ENV as sufficient. The x-seamless-auth-delivery-mode: external header must be accompanied by an x-seamless-service-token that passes the existing validateInternalServiceToken check plus the issuer, audience, and subject checks.
  • canReturnSensitiveDevelopmentDetails followed the same environment-based pattern and is now gated the same way. It is reached from createAdminBootstrapInviteHandler, which returns the bootstrap invite URL and token.
  • Callers affected: src/controllers/otp.ts, src/controllers/magicLinks.ts, src/controllers/registration.ts, src/controllers/bootstrap.ts.

Local development opt-in

Local development and some manual tooling relied on the environment check, so there is a replacement that cannot be reached by accident:

ALLOW_UNCREDENTIALED_DELIVERY_SECRETS=true

It is off unless set explicitly, it is ignored (with an error logged) when NODE_ENV=production, and it is documented in .env.example alongside the docs updates. I chose an explicit env opt-in over reintroducing an environment-derived default so the permissive mode is always a deliberate operator action and can never apply in production, regardless of how a deployment is configured.

Tests

Specs that exercised external delivery previously relied on the environment shortcut. They now mint a trusted service token through the new tests/factories/serviceTokenFactory, and the shared mock in tests/setup/mocks.ts rejects every token by default so any spec not opting in exercises the credentialed path. tests/unit/lib/externalDelivery.spec.ts covers the new matrix: no token, valid token, untrusted issuer or audience, the opt-in flag, and the opt-in flag being ignored in production.

Local run:

  • npm run typecheck passes
  • npm run lint passes
  • npm run format:check passes
  • npm run test:run 951 passed, 1 skipped, 87 files
  • npm run coverage passes thresholds, src/lib/externalDelivery.ts at 100%

Contract note

This changes the conditions under which the external delivery response shape is returned, so callers that use the header without a service token outside production will now receive the standard response instead of a delivery payload. seamless-auth-server is the likely affected repo. Happy to survey it and propose coordinated edits as a follow-up.

Follow-up for the maintainer

Worth confirming separately that every reachable deployed environment, including staging and preview, sets NODE_ENV=production.

…nvironments

External delivery and sensitive bootstrap details were gated on NODE_ENV not
being production, plus a client-supplied header. Both now fall through to the
existing internal service token check (issuer, audience, and subject) in every
environment.

Local development that cannot present a service token can set
ALLOW_UNCREDENTIALED_DELIVERY_SECRETS=true. The flag must be set deliberately
and is ignored under a production NODE_ENV, so it cannot become the default.

Specs that exercised external delivery relied on the NODE_ENV shortcut. They now
mint a trusted service token through tests/factories/serviceTokenFactory.
@Bccorb
Bccorb merged commit 916a3b7 into main Jul 20, 2026
2 of 3 checks passed
@Bccorb
Bccorb deleted the fix/require-service-token-for-external-delivery branch July 24, 2026 02:35
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