Breaking changes in 0.7.0
This release has ten breaking changes. Two of them (#115 and #139) do not show up as breaking in the PR list below, because their titles have no !. For what each change looks like on the wire and in the logs, see the CHANGELOG.
- Validation, with
CLIENT_ID/CLIENT_SECRETset: a provider401to introspection is now502 Provider Configuration Error, not401 Invalid Token, and it is logged asvalidation.provider_config_error(#111). Action: fix the proxy's client credentials, and move alerts offintrospect failed. - Validation: a provider failure (
5xx,429, another4xx, an invalid response, a timeout, a network error) is now502 Bad Gateway.500now means only a fault in the proxy itself (#126). Action: review alerts and retry policies keyed on500. NoRetry-Afteris passed through. - Validation: an introspection redirect is refused with
502 Provider Configuration Error(#123). Action: setINTROSPECT_URLto the final URL. - Validation: the
401now carriesWWW-Authenticate: Bearer error="invalid_token"(#114). Action: none for a client that ignores the header. If a cross-origin browser client needs to read it, expose the header at your edge. - Injection: the session grant no longer follows a redirect. A
3xxgets502 provider_config_error, and a same-origin307/308no longer mints a token (#112). Action: pointINJECTION_PROVIDER_ORIGINat the origin that serves/oauth/token; if the redirect stays on the same origin, the provider or its ingress must answerPOST /oauth/tokenitself, since the origin cannot carry a path. Re-key alerts that matchedprovider_unavailable/unexpected provider response: 405. - Injection: only a
200counts as a successful session grant. Any other2xxgets502 provider_unavailable(#124). Action: none for a provider that follows RFC 6749. - Injection: a session grant
401 invalid_clientis now502 provider_config_error, not401 session_required, and it is logged asinjection.provider_config_errorat error (#120). Action: fix theINJECTION_CLIENT_IDregistration. Alerts oninjection.session_unauthorizedno longer see this case; alert oninjection.provider_config_error. - Injection: provider error text that contains a credential is refused at any length, not only from 8 characters up. When the session cookie values or the exchange client secret are shorter than 8 characters and the provider's text contains one, you see the proxy's own wording or
invalid_error_codeinstead of the provider's diagnostic (#115). Action: rotate anINJECTION_EXCHANGE_CLIENT_SECRETthat is shorter than 8 characters. - Logs, both modes:
"x-request-id"is nowrequestId. Request, decision and failure lines carry anevent. A provider401about the caller's token now logs at info (validation.token_unauthorized), not error; its message is stillintrospect failed(#139). Action: move queries from"x-request-id"torequestId, and key alerts oneventrather than on level or message. - Boot:
INTROSPECT_URLmust be an absolutehttp(s)URL without userinfo. A deployment that uses userinfo, a non-URL, or another scheme (such asdata:) no longer boots (#140, #141). Action: set the plain endpoint URL, and useCLIENT_ID/CLIENT_SECRETfor credentials.
What's Changed
- test(validation): pin the router's own mappings — pass-through, 400, 401, 500 (#95 F27) by @y1o1 in #96
- refactor(router): one upstream proxy stage for both modes (#95 F18) by @y1o1 in #97
- docs: README responsibility maps for the directories that own a contract (#95) by @y1o1 in #98
- refactor(injection): the session decision out of the middleware, deps injectable (#95 F1, F4) by @y1o1 in #99
- refactor(injection): the exchange decision out of the handler, its deps injectable (#95 F2) by @y1o1 in #100
- refactor(validation): the decision out of the handler, its deps injectable (#95 F3) by @y1o1 in #101
- refactor(validation): the introspection client and the cache out of one function (#95 F5, F23) by @y1o1 in #102
- refactor(injection): the token-endpoint helpers into a module of their own (#95 F19) by @y1o1 in #103
- refactor: drop the header field nothing read and the dependency nothing imports (#95 F15, F24) by @y1o1 in #104
- fix(validation): cancel the introspection body the non-2xx path never reads (#95 F28) by @y1o1 in #105
- fix(injection): parseJsonBody answers null for the array its type excludes (#95 F34) by @y1o1 in #106
- fix(injection): report the action a forward without injection actually takes (#95 F31) by @y1o1 in #108
- refactor(express): extractBearerToken answers the token, not a record of one (#95 F36) by @y1o1 in #110
- fix(injection): one discriminator for the whole session-error branch (#95 F32) by @y1o1 in #109
- fix(injection): bound the token response body the success path buffered (#95 F35) by @y1o1 in #107
- feat(validation)!: a provider 401 that refused the proxy is 502, not 401 (#95 F7) by @y1o1 in #111
- fix(injection)!: neither token client follows a redirect (#95 F8) by @y1o1 in #112
- fix(injection): key the session cache by the grant context, not the cookie alone (#95 F33) by @y1o1 in #113
- fix(injection): redact a credential at any length, not only from eight up (#95 F30) by @y1o1 in #115
- feat(validation)!: the 401 carries the RFC 6750 challenge it owes (#95 F29) by @y1o1 in #114
- feat(validation): concurrent misses on one token share one provider call (#95 F6) by @y1o1 in #116
- feat(proxy): state the cancellation contract, and pin it (#95 F10) by @y1o1 in #117
- fix(injection): release every provider body the clients answer without reading (#95 F37) by @y1o1 in #118
- fix(validation): read a 200 introspection body at a bound, not whatever arrives (#95 F39) by @y1o1 in #119
- fix(injection)!: a session 401 invalid_client is a config error, not an expired session (#95 F47) by @y1o1 in #120
- fix(injection): an exchange refusal with an undeclared code is still logged (#95 F41) by @y1o1 in #121
- fix(validation)!: refuse an introspection redirect instead of following it (#95 F43) by @y1o1 in #123
- fix(injection): strip an empty inbound Authorization like any other (#95 F40) by @y1o1 in #122
- fix(injection)!: a session grant succeeds on a 200, not on any 2xx (#95 F38) by @y1o1 in #124
- fix(validation)!: a provider failure is 502, and 500 is the proxy's own (#95 F42) by @y1o1 in #126
- test(injection): coalescing tests wait for the join, not a duration (#95 F46) by @y1o1 in #125
- feat(validation): the 400 challenges by what was sent, with an optional realm (#95 F45) by @y1o1 in #127
- fix(logger): validation's failure lines carry their error (#95 F48) by @y1o1 in #128
- chore(deps): bump zod from 4.6.2 to 4.6.5 in the production-minor-patch group across 1 directory by @dependabot[bot] in #94
- chore(deps-dev): bump the dev-dependencies group across 1 directory with 4 updates by @dependabot[bot] in #129
- docs: every README states its responsibility, carries a date, and cites files not lines by @y1o1 in #131
- fix(injection): inject checks presence like the other two paths (#133) by @y1o1 in #135
- docs(router): keep the upstream decorator for header-name casing (#132) by @y1o1 in #136
- docs: a refused cookie forwards without a minted Authorization, not without any by @y1o1 in #137
- docs: source-directory READMEs describe the directory; files describe themselves by @y1o1 in #138
- fix: log requestId and event in both modes, and a caller-caused 401 at info (#134) by @y1o1 in #139
- fix!: an introspection URL carries no credential, and a raw one is redacted (#140), with the release audit's findings by @y1o1 in #141
- release: v0.7.0 by @y1o1 in #142
Full Changelog: v0.6.0...v0.7.0