@mcp-abap-adt/auth-broker 4.1.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +138 -1
- package/README.md +960 -1214
- package/dist/AuthBroker.d.ts +294 -110
- package/dist/AuthBroker.d.ts.map +1 -1
- package/dist/AuthBroker.js +726 -351
- package/dist/DestinationConfigError.d.ts +35 -6
- package/dist/DestinationConfigError.d.ts.map +1 -1
- package/dist/DestinationConfigError.js +51 -9
- package/dist/SessionWriter.d.ts +70 -33
- package/dist/SessionWriter.d.ts.map +1 -1
- package/dist/SessionWriter.js +118 -94
- package/dist/binding.d.ts +125 -74
- package/dist/binding.d.ts.map +1 -1
- package/dist/binding.js +320 -137
- package/dist/bindingOf.d.ts +33 -15
- package/dist/bindingOf.d.ts.map +1 -1
- package/dist/bindingOf.js +50 -20
- package/dist/buildIdentity.d.ts +137 -0
- package/dist/buildIdentity.d.ts.map +1 -0
- package/dist/buildIdentity.js +393 -0
- package/dist/clientAuthentication.d.ts +21 -6
- package/dist/clientAuthentication.d.ts.map +1 -1
- package/dist/clientAuthentication.js +55 -55
- package/dist/contractShape.d.ts +24 -0
- package/dist/contractShape.d.ts.map +1 -0
- package/dist/contractShape.js +24 -0
- package/dist/destinations.d.ts +149 -53
- package/dist/destinations.d.ts.map +1 -1
- package/dist/destinations.js +426 -160
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/quietLogger.d.ts +24 -0
- package/dist/quietLogger.d.ts.map +1 -0
- package/dist/quietLogger.js +63 -0
- package/package.json +8 -7
package/CHANGELOG.md
CHANGED
|
@@ -9,7 +9,144 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
Thank you to all contributors! See [CONTRIBUTORS.md](../../CONTRIBUTORS.md) for the complete list.
|
|
11
11
|
|
|
12
|
-
## [
|
|
12
|
+
## [5.0.0] - 2026-10-09
|
|
13
|
+
|
|
14
|
+
The broker on the auth chain's 6.0 contracts (auth-providers 6, interfaces-auth 7, auth-errors
|
|
15
|
+
2). A major: what every failure is changes class, two options become required for token
|
|
16
|
+
destinations, the session binding gains a versioned record, and every wait can be cancelled.
|
|
17
|
+
Released with `@mcp-abap-adt/auth-broker-cli` 3.0.0. What a 4.x consumer must do, row by row:
|
|
18
|
+
the README's *Migrating to 5.0.0*.
|
|
19
|
+
|
|
20
|
+
### Breaking
|
|
21
|
+
|
|
22
|
+
- **`renewal` is required for every token row.** `AuthBrokerConfig.renewal` — `(destination,
|
|
23
|
+
grant: TokenGrant) => IRenewalStrategy` — is called once per build of every UAA, OIDC and
|
|
24
|
+
SAML row, after every other check and before the provider's constructor; its answer is the
|
|
25
|
+
provider's `renewal`, unchanged. No default: a token row without it is a
|
|
26
|
+
`DestinationConfigError` naming `renewal`, beside every other missing field. A `renewal` that
|
|
27
|
+
throws is `DestinationConfigError(['renewal'])`, "the renewal option failed", carrying what it
|
|
28
|
+
threw. Not called for `basic`, `snc`, `none`, nor for the token API's `provider`.
|
|
29
|
+
`() => refreshThenLogin()` is 4.x's behaviour.
|
|
30
|
+
- **`onWriteFailure: 'fail' | 'continue'` is required** for every destination that writes a
|
|
31
|
+
secret — a token row `getProvider` builds, and every call of the token API with a `provider`.
|
|
32
|
+
Without it: `DestinationConfigError` naming `onWriteFailure`. `'fail'`: the call whose write
|
|
33
|
+
did not land fails (`unknown`, `persisting-tokens`), and while the destination's last write is
|
|
34
|
+
pending (failed, queued or in flight) `getProvider`, `getToken` and `refreshToken` retry it on
|
|
35
|
+
entry and once more before success, and are refused while it fails. `'continue'`: logged, the
|
|
36
|
+
call goes on. A moment of a provider already handed out that commits nothing is not refused.
|
|
37
|
+
- **Failures are `AuthProviderFailure`s, read through `@mcp-abap-adt/auth-errors`.** A
|
|
38
|
+
provider's failure reaches `getToken()` / `refreshToken()` as the same object. The broker reads
|
|
39
|
+
a caught value only through `readFailure` / `isAuthProviderFailure` — no `instanceof`,
|
|
40
|
+
`message` or `name` of an error is read in `src`. The broker's own failures are minted by
|
|
41
|
+
auth-errors: an aborted wait (`interactive-login`, `aborted`), a write that did not land under
|
|
42
|
+
`'fail'` (`unknown`, `persisting-tokens`, an allowlisted `code`), a provider that answered no
|
|
43
|
+
token (`request-failed`, `token-source`, `no-access-token` — replacing "Token provider did not
|
|
44
|
+
return authorization token …").
|
|
45
|
+
- **`DestinationConfigError` carries the provider's failure.** New `error?: IAuthProviderError`
|
|
46
|
+
(kind, facts, rendered words, admitted diagnostics; no `cause`) when a provider's or a
|
|
47
|
+
strategy's failure caused the refusal; the message then ends `: <error.reason>`. New
|
|
48
|
+
`isDestinationConfigError(value)` — structural, no `instanceof`; true for a JSON copy. A
|
|
49
|
+
provider constructor that throws inside a build — any row, not only SNC — is a
|
|
50
|
+
`DestinationConfigError` naming the store fields its configuration facts name. The SNC row
|
|
51
|
+
decides by kind (`configuration` → its fields), not by `instanceof ValidationError`. The
|
|
52
|
+
broker's own copy of the three certificate phrases is gone: `clientAuthentication`'s refusal
|
|
53
|
+
reads `the clientAuthentication strategy failed: <reason>` with auth-providers'
|
|
54
|
+
`client-certificate` failure carried. New rows: `renewal`, `onWriteFailure`, `provider` (an
|
|
55
|
+
instance after the destination's identity changed), a store field of a shape the broker
|
|
56
|
+
cannot take, `samlIdpInitiated`, `oidcScopes` and `oidcActorTokenType` of the wrong shape.
|
|
57
|
+
- **`issuedBy` is a versioned binding record**: `mcp-abap-adt-binding/2;<row>;<eleven address
|
|
58
|
+
fields>;<trust>` — the row (`authType/grantType`, or `provider/…` for the token API's consumer
|
|
59
|
+
provider), the exact string of every server address the row hands its provider
|
|
60
|
+
(`encodeURIComponent`-encoded, never canonicalised: `clientId`, `uaaUrl`, `oidcIssuerUrl`, the
|
|
61
|
+
three OIDC endpoints, `oidcAudience`, `samlIdpSsoUrl`, `samlAcsUrl`, `samlTokenUrl`, the
|
|
62
|
+
certificate client's `certUrl`) and the SHA-256 of the row's non-secret trust input (SAML
|
|
63
|
+
trust, OIDC scopes, the password grant's user, the token-exchange token types, the certificate
|
|
64
|
+
client's public certificate). Compared by exact equality, never parsed; no secret and no hash
|
|
65
|
+
of one in it. `issuedFor` keeps 4.x's canonical form. **Every session written before 5.0.0
|
|
66
|
+
reads as unbound**: its stored token and refresh token are not used, and the provider's first
|
|
67
|
+
renewal is a login (or a token request). A `none` row compares `issuedBy` always and refuses a
|
|
68
|
+
4.x binding naming `issuedBy`. `bindingOf(means, client)` keeps its signature and returns the
|
|
69
|
+
version-2 record.
|
|
70
|
+
- **A stored secret seeds only a fully stated binding**: one whose record holds the client the
|
|
71
|
+
row authenticates and every address its provider sends a credential to — so an issuer-less
|
|
72
|
+
OIDC row with explicit endpoints and a client is now seeded, and `token_exchange` and the
|
|
73
|
+
token API's consumer provider never are.
|
|
74
|
+
- **A provider is never changed.** Every call of `getProvider` and the token API re-reads what
|
|
75
|
+
the destination's provider was built from (the means, the client, the certificate client when
|
|
76
|
+
read) and compares everything the build read, secrets included (in memory only); anything
|
|
77
|
+
changed builds a new provider that starts with nothing. 4.x cached a destination's provider
|
|
78
|
+
for the broker's life. An instance `provider` whose destination's identity changed is refused
|
|
79
|
+
until a new broker.
|
|
80
|
+
- **Two paths, resolved and cached apart.** The row path (`getProvider`, and the token API
|
|
81
|
+
without `provider`) and the consumer path (the token API with `provider`) each have their own
|
|
82
|
+
resolution, cache, identity and binding; only the destination's write queue is shared.
|
|
83
|
+
- **The token API's factory is handed no stored secret**: `authConfig` is the client
|
|
84
|
+
(`refreshToken` a key, never a value), `connConfig` the allowlisted connection means
|
|
85
|
+
(`serviceUrl`, `sapClient`, `language`, `authType`, `grantType`), the fourth argument the
|
|
86
|
+
strategy's answer and the client identity (`refreshToken` never set). The consumer path is
|
|
87
|
+
never seeded. A consumer provider's result without a refresh token writes `refreshToken: ''`
|
|
88
|
+
(the stored one is no longer carried).
|
|
89
|
+
- **Session writes state every field** (auth-stores 4 merges): a credential write states the
|
|
90
|
+
token or cookies, `expiresAt` (the provider's report), the refresh token the provider owns or
|
|
91
|
+
`''`, `issuedFor` (`''` when the means state no `serviceUrl`) and `issuedBy`; `saml2_pure`'s
|
|
92
|
+
cookies write `refreshToken: ''`; a refresh token discarded before any credential writes only
|
|
93
|
+
`refreshToken: ''`. A refresh token is written only by the provider that obtained it or was
|
|
94
|
+
seeded with it and has not discarded it since — never read from the store at write time.
|
|
95
|
+
- **The write queue has no retry timer.** One queue per destination: one write at a time, in
|
|
96
|
+
order; a replaced provider's late write is dropped once the new one has written; a failed
|
|
97
|
+
write stays pending until the destination's next write or `flush()`. The 1 s → 60 s retry
|
|
98
|
+
timer is gone. The store's contract, documented: `saveSession` settles.
|
|
99
|
+
- **`flush()` rejects with an `AggregateError` of `SessionWriteFailure`s** (`destination`,
|
|
100
|
+
`error`: the store's error classified `persisting-tokens`) instead of
|
|
101
|
+
`Error("<dest>": <class>)`; the message keeps 4.x's lead.
|
|
102
|
+
- **The built providers persist through auth-providers' `refreshStatePersistence`** (`onTokens`
|
|
103
|
+
is gone from every row), with the consumer's `onWriteFailure`.
|
|
104
|
+
- **`AuthBrokerConfig`'s optional fields are declared `?: T | undefined`.**
|
|
105
|
+
|
|
106
|
+
### Added
|
|
107
|
+
|
|
108
|
+
- **Cancellation.** `BrokerCallOptions { signal? }` on `getProvider`, `getToken`,
|
|
109
|
+
`refreshToken`, `flush` and `createTokenRefresher`. Every wait is a waiter of auth-errors'
|
|
110
|
+
`sharedAttempt`: an abort releases that caller alone (`aborted`), the work runs on; when every
|
|
111
|
+
caller of a build aborted, the build is not cached and writes nothing. `getProvider`'s signal
|
|
112
|
+
is attached to the token or SNC provider it answers, so a login that provider starts later is
|
|
113
|
+
aborted once every holder's signal aborted; the token API never attaches and passes its signal
|
|
114
|
+
to `getTokens({ signal })` / `refreshTokens({ signal })`. `ClientAuthenticationContext.signal`:
|
|
115
|
+
the build's attempt. The broker sets no timeout and has no timer.
|
|
116
|
+
- **`authDebug?: boolean`** — passed to every token provider the broker builds, on only for
|
|
117
|
+
`true`, never read from the environment.
|
|
118
|
+
- Exports: `isDestinationConfigError`, `DestinationConfigErrorLike`, `SessionWriteFailure`,
|
|
119
|
+
`BrokerCallOptions`, `TokenGrant`, and the re-exported `IRenewalStrategy`.
|
|
120
|
+
|
|
121
|
+
### Changed
|
|
122
|
+
|
|
123
|
+
- **Logging**: every line goes through a guard — a logger that throws or rejects changes no
|
|
124
|
+
outcome. A failed write is one `warn` line with auth-errors' `logFields`, never a class name or
|
|
125
|
+
message. A stored secret not used is a `warn` only where a stored secret could ever be seeded,
|
|
126
|
+
a `debug` line otherwise.
|
|
127
|
+
- **The certificate client is re-read on every call** when the build read it: a rotated
|
|
128
|
+
certificate builds a new provider at the next call (4.x pinned it for the broker's life).
|
|
129
|
+
- **Built under a stricter compiler.** Both packages, sources and tests, compile with
|
|
130
|
+
`noImplicitReturns`, `noFallthroughCasesInSwitch`, `noImplicitOverride`,
|
|
131
|
+
`noUncheckedIndexedAccess` and `exactOptionalPropertyTypes`; objects handed to a consumer keep
|
|
132
|
+
exactly their own keys (`src/contractShape.ts`). Lint: `noExplicitAny` is an error outside the
|
|
133
|
+
tests, and `lint:check` fails on any warning.
|
|
134
|
+
- **The broker stand and the live suites** run on the 6.0 chain and on
|
|
135
|
+
`@mcp-abap-adt/connection` 14; each states `renewal` and `onWriteFailure`.
|
|
136
|
+
- `tools/check-provider-shape.mjs`, copied byte for byte from auth-errors 2.1.1, runs with
|
|
137
|
+
rules 4, 5, 6 over both packages (`npm run check:shape`).
|
|
138
|
+
|
|
139
|
+
### Dependencies
|
|
140
|
+
|
|
141
|
+
- `@mcp-abap-adt/auth-providers` `^5.3.0` → `^6.0.0`; `@mcp-abap-adt/interfaces-auth` `^3.2.0`
|
|
142
|
+
→ `^7.5.0`; `@mcp-abap-adt/interfaces-auth-sap` `^2.0.0` → `^3.3.0`;
|
|
143
|
+
`@mcp-abap-adt/interfaces-auth-broker` `^1.2.0` → `^1.3.0`; new `@mcp-abap-adt/auth-errors`
|
|
144
|
+
`^2.1.1`. Dev: `@mcp-abap-adt/auth-stores` `^4.0.0`, `@mcp-abap-adt/connection` `^14.0.0`.
|
|
145
|
+
|
|
146
|
+
### Measured, and not
|
|
147
|
+
|
|
148
|
+
- Not yet re-run on 5.0.0: the x509 live check (`npm run test:live:x509`, last run 2026-10-05 on
|
|
149
|
+
4.1.0) and the live suite (`npm run test:live`).
|
|
13
150
|
|
|
14
151
|
## [4.1.0] - 2026-10-05
|
|
15
152
|
|