@mcp-abap-adt/auth-providers 2.2.2 → 4.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 +193 -0
- package/README.md +825 -64
- package/dist/__tests__/integration/stand/formLogin.d.ts +68 -0
- package/dist/__tests__/integration/stand/formLogin.d.ts.map +1 -0
- package/dist/__tests__/integration/stand/formLogin.js +194 -0
- package/dist/auth/callbackServer.js +2 -2
- package/dist/auth/passcodeAuth.d.ts +25 -0
- package/dist/auth/passcodeAuth.d.ts.map +1 -0
- package/dist/auth/passcodeAuth.js +62 -0
- package/dist/auth/saml2Auth.d.ts +6 -2
- package/dist/auth/saml2Auth.d.ts.map +1 -1
- package/dist/auth/saml2Auth.js +9 -20
- package/dist/auth/samlBearerAssertion.d.ts +24 -0
- package/dist/auth/samlBearerAssertion.d.ts.map +1 -0
- package/dist/auth/samlBearerAssertion.js +102 -0
- package/dist/auth/strictXml.d.ts +13 -0
- package/dist/auth/strictXml.d.ts.map +1 -0
- package/dist/auth/strictXml.js +21 -0
- package/dist/errors/AssertionValidationError.d.ts +15 -0
- package/dist/errors/AssertionValidationError.d.ts.map +1 -0
- package/dist/errors/AssertionValidationError.js +24 -0
- package/dist/errors/TokenProviderErrors.d.ts +2 -0
- package/dist/errors/TokenProviderErrors.d.ts.map +1 -1
- package/dist/errors/TokenProviderErrors.js +3 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -2
- package/dist/providers/Saml2BearerProvider.d.ts +1 -0
- package/dist/providers/Saml2BearerProvider.d.ts.map +1 -1
- package/dist/providers/Saml2BearerProvider.js +20 -2
- package/dist/providers/Saml2PureProvider.d.ts +1 -0
- package/dist/providers/Saml2PureProvider.d.ts.map +1 -1
- package/dist/providers/Saml2PureProvider.js +19 -5
- package/dist/providers/UaaPasscodeProvider.d.ts +43 -0
- package/dist/providers/UaaPasscodeProvider.d.ts.map +1 -0
- package/dist/providers/UaaPasscodeProvider.js +86 -0
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -3
- package/dist/providers/saml2Utils.d.ts +49 -2
- package/dist/providers/saml2Utils.d.ts.map +1 -1
- package/dist/providers/saml2Utils.js +94 -2
- package/dist/strategies/index.d.ts +1 -1
- package/dist/strategies/index.d.ts.map +1 -1
- package/dist/strategies/index.js +2 -1
- package/dist/strategies/manualStrategies.d.ts +7 -0
- package/dist/strategies/manualStrategies.d.ts.map +1 -1
- package/dist/strategies/manualStrategies.js +21 -0
- package/dist/validation/assertionValidator.d.ts +28 -0
- package/dist/validation/assertionValidator.d.ts.map +1 -0
- package/dist/validation/assertionValidator.js +444 -0
- package/dist/validation/documentIds.d.ts +15 -0
- package/dist/validation/documentIds.d.ts.map +1 -0
- package/dist/validation/documentIds.js +32 -0
- package/dist/validation/inMemoryReplayStore.d.ts +22 -0
- package/dist/validation/inMemoryReplayStore.d.ts.map +1 -0
- package/dist/validation/inMemoryReplayStore.js +49 -0
- package/dist/validation/signedNode.d.ts +54 -0
- package/dist/validation/signedNode.d.ts.map +1 -0
- package/dist/validation/signedNode.js +171 -0
- package/dist/validation/xsdDateTime.d.ts +17 -0
- package/dist/validation/xsdDateTime.d.ts.map +1 -0
- package/dist/validation/xsdDateTime.js +67 -0
- package/package.json +14 -8
- package/bin/auth-device-flow.ts +0 -114
- package/dist/auth/deviceFlowAuth.d.ts +0 -43
- package/dist/auth/deviceFlowAuth.d.ts.map +0 -1
- package/dist/auth/deviceFlowAuth.js +0 -168
- package/dist/providers/DeviceFlowProvider.d.ts +0 -32
- package/dist/providers/DeviceFlowProvider.d.ts.map +0 -1
- package/dist/providers/DeviceFlowProvider.js +0 -86
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,199 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [4.0.0] - 2026-09-25
|
|
11
|
+
|
|
12
|
+
### Breaking
|
|
13
|
+
|
|
14
|
+
- **Both SAML providers validate the assertion before trusting it** (#19).
|
|
15
|
+
Until now the only check was that the payload was a non-empty string, and
|
|
16
|
+
`Saml2PureProvider` took its session lifetime from a regular expression over
|
|
17
|
+
the unverified XML. `Saml2BearerProvider` now validates before the token
|
|
18
|
+
exchange, `Saml2PureProvider` before `cookieProvider`, through twelve checks:
|
|
19
|
+
document shape, unique IDs, signature, that the signed node is the node read,
|
|
20
|
+
`Status`, assertion ID, issuer, `Conditions`, `NotBefore`, `NotOnOrAfter`,
|
|
21
|
+
audience, one bearer subject confirmation (request ID, recipient, window) and
|
|
22
|
+
`Destination`, then replay. The README's *SAML assertion validation* lists
|
|
23
|
+
them with what refuses each.
|
|
24
|
+
|
|
25
|
+
**The configuration is required.** Supply `idpCertificates` (PEM or bare
|
|
26
|
+
base64 DER, a list for key rotation) and `idpEntityId`, or an
|
|
27
|
+
`assertionValidator` of your own; without them the provider's constructor
|
|
28
|
+
throws a `ValidationError` naming what is missing. A shipped validator
|
|
29
|
+
supplied as `assertionValidator` still needs `idpEntityId`, and its absence
|
|
30
|
+
fails at construction too; only a custom validator does without.
|
|
31
|
+
`spEntityId` becomes the `Audience` the assertion must name.
|
|
32
|
+
|
|
33
|
+
**Request IDs.** `InResponseTo` must answer the AuthnRequest the package
|
|
34
|
+
minted, or `authnRequestId` when the package did not build the request — a
|
|
35
|
+
pre-built `authorizationUrl`, or a strategy that returns a payload without
|
|
36
|
+
calling `buildAuthorizationUrl`. `idpInitiated: true` declares that no request
|
|
37
|
+
was sent, so the assertion must carry no `InResponseTo`; it gives up the
|
|
38
|
+
login-CSRF defence of a request ID, and is never inferred.
|
|
39
|
+
`Saml2BearerProvider` against UAA or XSUAA needs it, since both refuse an
|
|
40
|
+
assertion carrying `InResponseTo`. Having neither an ID nor the declaration,
|
|
41
|
+
or combining the declaration with an ID, raises a `ValidationError`. With
|
|
42
|
+
`idpInitiated` and no `authorizationUrl`, a strategy that calls
|
|
43
|
+
`buildAuthorizationUrl` is refused inside the builder, before any URL is
|
|
44
|
+
produced — so before a browser opens.
|
|
45
|
+
|
|
46
|
+
**Status, by validator.** Under `createSignedResponseValidator` —
|
|
47
|
+
`Saml2PureProvider`'s default — an identity provider returning a
|
|
48
|
+
non-`Success` status is now refused, where it was accepted before.
|
|
49
|
+
`Saml2BearerProvider`'s default, `createSignedAssertionValidator`, does not
|
|
50
|
+
read `Status`, so a bearer consumer sees no change there: a declining identity
|
|
51
|
+
provider mints no signed assertion, and the login fails for want of one.
|
|
52
|
+
|
|
53
|
+
**Migrating:** see *Migrating from 3.x to 4.0* in the README — add the trust
|
|
54
|
+
configuration, check `spEntityId`, and for `Saml2BearerProvider` against UAA
|
|
55
|
+
or XSUAA add `idpInitiated: true` with a strategy that never calls
|
|
56
|
+
`buildAuthorizationUrl`.
|
|
57
|
+
- **`parseSamlNotOnOrAfter` is removed.** `Saml2PureProvider`'s `expiresAt` is
|
|
58
|
+
now the validated assertion's expiry — the earlier of `Conditions/@NotOnOrAfter`
|
|
59
|
+
and the accepted bearer confirmation's — read from the verified document.
|
|
60
|
+
- **`buildSamlAuthorizationUrl` returns `{ url, requestId? }`**, since the
|
|
61
|
+
minted ID must survive to validation, and **`getSamlAssertion` returns
|
|
62
|
+
`Promise<SamlAssertionResult>`** (payload, request ID, ACS) instead of
|
|
63
|
+
`Promise<string>`. None of these, nor `parseSamlNotOnOrAfter`, was exported
|
|
64
|
+
from the package root; only a deep import of `dist/auth/saml2Auth` or
|
|
65
|
+
`dist/providers/saml2Utils` is affected.
|
|
66
|
+
- **`@mcp-abap-adt/interfaces-auth` `^2.0.0`** (was `^1.2.0`), where
|
|
67
|
+
`AssertionContext.expectedInResponseTo` is optional — a breaking change for
|
|
68
|
+
implementers of `IAssertionValidator`, which must refuse an assertion
|
|
69
|
+
carrying `InResponseTo` when it is absent.
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- **`createSignedResponseValidator`** and **`createSignedAssertionValidator`**,
|
|
74
|
+
sharing `ShippedValidatorOptions` (`idpCertificates`, `clockSkewMs`,
|
|
75
|
+
`replayStore`). The first requires the signature to cover the `Response` and
|
|
76
|
+
performs all twelve checks; the second accepts a signature over the
|
|
77
|
+
`Assertion` — bare, or inside a Response — and does not read `Status`,
|
|
78
|
+
`Response/Issuer` or `Destination` at all. `Saml2PureProvider` defaults to the
|
|
79
|
+
first, `Saml2BearerProvider` to the second; `assertionValidator` replaces
|
|
80
|
+
either.
|
|
81
|
+
- **Replay detection**: `defaultReplayStore`, a process-wide in-memory store
|
|
82
|
+
shared by every default validator, keyed by `{issuer, assertionId}` and
|
|
83
|
+
retained until the earlier of `Conditions/@NotOnOrAfter` and the latest
|
|
84
|
+
`NotOnOrAfter` of a bearer confirmation that answers the request and names
|
|
85
|
+
the ACS, plus `clockSkewMs` — as long as the assertion could still be
|
|
86
|
+
accepted, which can outlast `expiresAt`; `createInMemoryReplayStore()` for
|
|
87
|
+
an isolated one; `assertionReplayStore` on the providers for a shared store
|
|
88
|
+
across processes.
|
|
89
|
+
- **`clockSkewMs`**, default `0`.
|
|
90
|
+
- Any XML parse fault — including one `@xmldom/xmldom` would otherwise repair,
|
|
91
|
+
such as an undeclared entity — is a refusal at `document`, and the parser
|
|
92
|
+
never writes to the console; the bearer conversion (`toBearerAssertion`)
|
|
93
|
+
parses the same way.
|
|
94
|
+
- The validators refuse a payload carrying a `<!DOCTYPE` declaration, never
|
|
95
|
+
take a signing certificate from the document's own `KeyInfo`, and accept
|
|
96
|
+
RSA-SHA1 signatures and SHA-1 digests, as `xml-crypto` does by default; a
|
|
97
|
+
consumer wanting to refuse SHA-1 wraps a shipped validator in its own.
|
|
98
|
+
- **`AssertionValidationError`**, with `check: AssertionCheck` naming the check
|
|
99
|
+
that refused the assertion, and code `ASSERTION_VALIDATION_ERROR`.
|
|
100
|
+
- `xml-crypto` becomes a runtime dependency, for signature verification.
|
|
101
|
+
|
|
102
|
+
### Development
|
|
103
|
+
|
|
104
|
+
- Both validators run end to end in `npm test`, through `Saml2PureProvider`
|
|
105
|
+
and a real callback, against responses from `@mcp-abap-adt/auth-mocks` (the
|
|
106
|
+
devDependency and its range are unchanged). Every corruption variant is refused at the
|
|
107
|
+
check it targets, except `statusFailure` and `wrongDestination`, which the
|
|
108
|
+
assertion-only validator accepts — both halves asserted.
|
|
109
|
+
- The provider stand's SAML suites run every login through the provider's
|
|
110
|
+
validation, including Keycloak's real responses, signed at both levels; the
|
|
111
|
+
live XSUAA suite does the same with its per-run identity provider, and now
|
|
112
|
+
refuses the `InResponseTo` case itself, before XSUAA sees it.
|
|
113
|
+
- `@mcp-abap-adt/interfaces-auth-sap` `^1.0.1`.
|
|
114
|
+
|
|
115
|
+
## [3.0.0] - 2026-09-24
|
|
116
|
+
|
|
117
|
+
### Added
|
|
118
|
+
|
|
119
|
+
- **`UaaPasscodeProvider`** and **`manualPasscodeStrategy`** — the one-time
|
|
120
|
+
passcode `cf login --sso` uses, for UAA and XSUAA. The user fetches a
|
|
121
|
+
Temporary Authentication Code from `<uaaUrl>/passcode` in any browser,
|
|
122
|
+
logging in however the identity zone asks, and the provider exchanges it
|
|
123
|
+
through the password grant and refreshes afterwards — a headless SSO login
|
|
124
|
+
that XSUAA supports, unlike the device authorization grant. A rejected code
|
|
125
|
+
reports UAA's reason (`Invalid passcode`). Tested on the UAA stand.
|
|
126
|
+
|
|
127
|
+
### Breaking
|
|
128
|
+
|
|
129
|
+
- **Node.js 22 or 24** — `engines: "^22 || ^24"` (was `>=18.2.0`). The
|
|
130
|
+
supported versions now follow SAP BTP, Cloud Foundry, whose Node.js buildpack
|
|
131
|
+
offers exactly 22 and 24: Node 18 reached its end of life on 30 April 2025
|
|
132
|
+
and Node 20 on 30 April 2026, and SAP has removed 20 from Cloud Foundry. The
|
|
133
|
+
odd releases between them are excluded too — 23 reached end of life on 1 June
|
|
134
|
+
2025 and 25 on 1 June 2026 — and so is 26 until SAP offers it. CI tests on 22
|
|
135
|
+
and 24 instead of 18.
|
|
136
|
+
|
|
137
|
+
**Migrating:** run on Node 22 or 24. Nothing in the API changed.
|
|
138
|
+
- **`DeviceFlowProvider` is removed**, with `DeviceFlowProviderConfig` and the
|
|
139
|
+
`auth-device-flow` command. It sent the device authorization grant to
|
|
140
|
+
`${uaaUrl}/oauth/device_authorization`, a path no server we could find
|
|
141
|
+
serves: Cloud Foundry UAA has no device grant, XSUAA answers that path with a
|
|
142
|
+
redirect to its login page and advertises no `device_authorization_endpoint`,
|
|
143
|
+
and servers that do implement RFC 8628 — Keycloak, Spring Authorization
|
|
144
|
+
Server, Ory Hydra, Zitadel, Dex — publish it elsewhere. Its live tests had
|
|
145
|
+
long been skipped.
|
|
146
|
+
|
|
147
|
+
**Migrating:** for a server that implements RFC 8628, use
|
|
148
|
+
`OidcDeviceFlowProvider` — it finds the endpoints through discovery
|
|
149
|
+
(`issuerUrl`) or takes them explicitly, and is tested against Keycloak. For a
|
|
150
|
+
headless login to UAA or XSUAA, use `UaaPasscodeProvider`: the user fetches
|
|
151
|
+
a one-time code from `<uaaUrl>/passcode` in any browser, as with
|
|
152
|
+
`cf login --sso`.
|
|
153
|
+
|
|
154
|
+
### Fixed
|
|
155
|
+
|
|
156
|
+
- **`Saml2BearerProvider` sends what RFC 7522 accepts** (#37). It forwarded
|
|
157
|
+
the strategy's payload as is — after an interactive login, the whole
|
|
158
|
+
`SAMLResponse` in standard base64 — where §2.1 takes one Assertion,
|
|
159
|
+
base64url-encoded. Cloud Foundry UAA answers 401 to a Response in either
|
|
160
|
+
encoding, so the provider could not get a token through any interactive
|
|
161
|
+
strategy. It now takes the Assertion out of a Response (copying onto it
|
|
162
|
+
every namespace declaration it inherited, including one used only inside an
|
|
163
|
+
`xsi:type` value, and keeping its signature) and sends it
|
|
164
|
+
base64url-encoded; a bare Assertion in either encoding is re-encoded. A
|
|
165
|
+
Response with no Assertion, several, or only an `EncryptedAssertion` is
|
|
166
|
+
refused before anything is sent. `@xmldom/xmldom` becomes a dependency.
|
|
167
|
+
|
|
168
|
+
### Development
|
|
169
|
+
|
|
170
|
+
- **Every provider but two is tested against a real authorization server** in
|
|
171
|
+
Docker: Cloud Foundry UAA — the server XSUAA is built from — and Keycloak.
|
|
172
|
+
`npm run test:stand` starts both, runs the suites and stops them, and CI runs
|
|
173
|
+
the same as its own job on Node 22 and 24.
|
|
174
|
+
- UAA: `Saml2BearerProvider` (confirming end to end what #23 was about: a
|
|
175
|
+
refresh token is issued with the saml2-bearer token when the client may
|
|
176
|
+
hold one, and spent without running the authorization strategy),
|
|
177
|
+
`ClientCredentialsProvider`, `AuthorizationCodeProvider` with refresh.
|
|
178
|
+
- Keycloak: `OidcPasswordProvider` with refresh, `OidcBrowserProvider` with
|
|
179
|
+
S256 PKCE, `OidcDeviceFlowProvider`, `OidcTokenExchangeProvider`
|
|
180
|
+
(RFC 8693).
|
|
181
|
+
- Keycloak as the SAML identity provider: `Saml2BearerProvider` end to end
|
|
182
|
+
into UAA, with no assertion built by the tests, and the identity-provider
|
|
183
|
+
half of `Saml2PureProvider`. This showed that UAA's bearer grant refuses
|
|
184
|
+
the answer to an SP-initiated login for its `InResponseTo`, and accepts an
|
|
185
|
+
IdP-initiated one; the README says so under `Saml2BearerProvider`.
|
|
186
|
+
- Interactive logins go through each server's own login and consent pages,
|
|
187
|
+
submitted over HTTP by a test helper.
|
|
188
|
+
- Not covered: `DeviceFlowProvider`, whose `/oauth/device_authorization`
|
|
189
|
+
neither server serves, and `Saml2PureProvider`'s cookie exchange, which is
|
|
190
|
+
the consumer's `cookieProvider`.
|
|
191
|
+
|
|
192
|
+
- **Live checks against a real XSUAA** — `npm run test:xsuaa`, not in CI —
|
|
193
|
+
create an XSUAA instance and a SAML trust in a BTP subaccount, run, and
|
|
194
|
+
remove them. On a trial subaccount: `Saml2BearerProvider` gets a token and a
|
|
195
|
+
refresh token from an IdP-initiated assertion, converts a whole
|
|
196
|
+
`SAMLResponse`, refreshes without its strategy, and is refused an assertion
|
|
197
|
+
carrying `InResponseTo`, exactly as by UAA; `UaaPasscodeProvider` works,
|
|
198
|
+
including — checked by hand — with an ABAP environment's own service key,
|
|
199
|
+
whose token opens ADT.
|
|
200
|
+
`@mcp-abap-adt/auth-mocks` becomes a devDependency, for signing assertions.
|
|
201
|
+
The stand itself is not published.
|
|
202
|
+
|
|
10
203
|
## [2.2.2] - 2026-09-24
|
|
11
204
|
|
|
12
205
|
### Dependencies
|