@mcp-abap-adt/auth-broker 4.0.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 +273 -1
- package/README.md +1028 -908
- package/dist/AuthBroker.d.ts +353 -99
- package/dist/AuthBroker.d.ts.map +1 -1
- package/dist/AuthBroker.js +806 -258
- 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 +137 -64
- package/dist/binding.d.ts.map +1 -1
- package/dist/binding.js +330 -131
- package/dist/bindingOf.d.ts +33 -14
- 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 +124 -0
- package/dist/clientAuthentication.d.ts.map +1 -0
- package/dist/clientAuthentication.js +218 -0
- 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 +174 -53
- package/dist/destinations.d.ts.map +1 -1
- package/dist/destinations.js +460 -150
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -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 +9 -7
package/dist/AuthBroker.d.ts
CHANGED
|
@@ -3,13 +3,20 @@
|
|
|
3
3
|
*
|
|
4
4
|
* `getProvider` builds the `IAuthProvider` a destination states, from the
|
|
5
5
|
* means its service key store holds and the secret its session store holds.
|
|
6
|
-
* Every token provider it builds
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Every token provider it builds renews through the consumer's `renewal` and
|
|
7
|
+
* writes what it obtains back to the session store through auth-providers'
|
|
8
|
+
* `refreshStatePersistence` — the secret alone, through one plain queue per
|
|
9
|
+
* destination (`SessionWriter`): a failed write stays pending until the
|
|
10
|
+
* destination's next write or `flush()`, and means what the consumer's
|
|
11
|
+
* `onWriteFailure` says; `flush()` reports what is still pending.
|
|
9
12
|
* The token API (`getToken`, `refreshToken`, `createTokenRefresher`) asks that
|
|
10
|
-
* same provider —
|
|
11
|
-
*
|
|
12
|
-
* path.
|
|
13
|
+
* same provider — the row path's — or, when the consumer gives one, the
|
|
14
|
+
* consumer's provider — the consumer path's, resolved, cached and bound on its
|
|
15
|
+
* own — whose every answer it writes through the same write path.
|
|
16
|
+
*
|
|
17
|
+
* A provider is never changed: every call re-reads what its path's
|
|
18
|
+
* provider was built from and builds a new one, starting with nothing, when
|
|
19
|
+
* anything of it changed (`buildIdentity.ts`).
|
|
13
20
|
*
|
|
14
21
|
* The broker orchestrates and nothing more. It resolves what the stores know
|
|
15
22
|
* about a destination, hands it to the provider, asks the provider for a token
|
|
@@ -19,22 +26,72 @@
|
|
|
19
26
|
* broker does not repeat or override any of them.
|
|
20
27
|
*/
|
|
21
28
|
import type { IDeviceCodePresenter, OidcCallbackResult } from '@mcp-abap-adt/auth-providers';
|
|
22
|
-
import type { IAssertionReplayStore, IAuthorizationStrategy, IAuthProvider, IRefreshableTokenProvider, ITokenRefresher } from '@mcp-abap-adt/interfaces-auth';
|
|
29
|
+
import type { IAssertionReplayStore, IAuthorizationStrategy, IAuthProvider, IClientAuthentication, IRefreshableTokenProvider, ITokenRefresher } from '@mcp-abap-adt/interfaces-auth';
|
|
23
30
|
import type { ILogger } from '@mcp-abap-adt/interfaces-utils';
|
|
31
|
+
import { type ClientAuthenticationStrategy } from './clientAuthentication';
|
|
32
|
+
import { type RenewalOption } from './destinations';
|
|
24
33
|
import type { IAuthorizationConfig, IConnectionConfig, IServiceKeyStore, ISessionStore } from './stores/interfaces';
|
|
34
|
+
/** What a caller of `getProvider`, the token API and `flush()` may pass. */
|
|
35
|
+
export interface BrokerCallOptions {
|
|
36
|
+
/**
|
|
37
|
+
* This caller no longer needs the answer: an abort releases this caller
|
|
38
|
+
* alone, with auth-errors' `aborted` failure (`interactive-login`), while
|
|
39
|
+
* the work it waited on runs on for the others. No bound of the broker's
|
|
40
|
+
* own: a caller that wants one passes `AbortSignal.timeout(ms)`.
|
|
41
|
+
*/
|
|
42
|
+
readonly signal?: AbortSignal | undefined;
|
|
43
|
+
}
|
|
25
44
|
/**
|
|
26
|
-
* Builds the provider for one destination, from
|
|
45
|
+
* Builds the token API's provider for one destination, from the means and the
|
|
46
|
+
* client — never from a stored secret: the consumer path is never seeded
|
|
47
|
+
*. A consumer whose provider must resume after a restart composes that
|
|
48
|
+
* itself: its provider's own seed and persistence.
|
|
27
49
|
*
|
|
28
|
-
*
|
|
29
|
-
* else from the service key — with the refresh token the session stored, or
|
|
30
|
-
* `null` when neither store has credentials (a SAML flow needs none).
|
|
31
|
-
* - `connConfig`: the session's connection config, with `serviceUrl` resolved
|
|
32
|
-
* and the last token the session stored, so the provider can reuse it while
|
|
33
|
-
* it is valid.
|
|
50
|
+
* It is handed exactly these, and nothing else a store answered:
|
|
34
51
|
*
|
|
35
|
-
*
|
|
52
|
+
* - `authConfig`: the client, through an allowlist — `uaaUrl`, `uaaClientId`,
|
|
53
|
+
* `uaaClientSecret` — from the session store when it answers one, else from
|
|
54
|
+
* the key store; `refreshToken` is a key, never a value. `null` when
|
|
55
|
+
* neither store has a client (a SAML flow needs none).
|
|
56
|
+
* - `connConfig`: the connection means, through an allowlist — `serviceUrl`
|
|
57
|
+
* (resolved: the session's, else the key store's), and, as the session
|
|
58
|
+
* store's connection config states them, `sapClient`, `language`,
|
|
59
|
+
* `authType` and `grantType`. No token, cookies, expiry, refresh token or
|
|
60
|
+
* binding, nor any other field.
|
|
61
|
+
* - `client` (`TokenProviderClient`), only beside a `clientAuthentication`
|
|
62
|
+
* strategy, for a destination whose grant authenticates a client: the
|
|
63
|
+
* strategy's answer and the client identity (`uaaUrl`, `clientId`); never
|
|
64
|
+
* PEM, never a secret, never a refresh token. Without a strategy the
|
|
65
|
+
* factory gets three arguments, as 4.0.0.
|
|
66
|
+
*
|
|
67
|
+
* Called once per build: the broker keeps the provider it returns while what
|
|
68
|
+
* it was handed is unchanged, re-reading it on every token API call;
|
|
69
|
+
* when any of it changes, the factory is called again and the new provider
|
|
70
|
+
* starts with nothing.
|
|
71
|
+
*/
|
|
72
|
+
export type TokenProviderFactory = (destination: string, authConfig: IAuthorizationConfig | null, connConfig: IConnectionConfig, client?: TokenProviderClient) => IRefreshableTokenProvider;
|
|
73
|
+
/**
|
|
74
|
+
* The token API factory's fourth argument — present only when the broker was
|
|
75
|
+
* given a `clientAuthentication` strategy and the destination states a grant
|
|
76
|
+
* whose client authenticates; without one the factory is called with three
|
|
77
|
+
* arguments, as 4.0.0. It never carries a certificate, a key or a secret.
|
|
36
78
|
*/
|
|
37
|
-
export
|
|
79
|
+
export interface TokenProviderClient {
|
|
80
|
+
/** The strategy's answer, resolved before the factory is called. */
|
|
81
|
+
readonly clientAuthentication?: IClientAuthentication;
|
|
82
|
+
/**
|
|
83
|
+
* The client's identity — the secret client's `uaaUrl` when the stores hold
|
|
84
|
+
* one, else the certificate client's; absent when there is neither.
|
|
85
|
+
*/
|
|
86
|
+
readonly uaaUrl?: string;
|
|
87
|
+
/** The client id beside `uaaUrl`, from the same client. */
|
|
88
|
+
readonly clientId?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Never set — not even as a key: the consumer path is handed no stored
|
|
91
|
+
* secret. Kept in the type, which 5.0.0 leaves unchanged.
|
|
92
|
+
*/
|
|
93
|
+
readonly refreshToken?: string;
|
|
94
|
+
}
|
|
38
95
|
/** The grants whose provider takes an `IAuthorizationStrategy<string>`. */
|
|
39
96
|
export type StrategyGrant = 'authorization_code' | 'passcode' | 'saml2_pure' | 'saml2_bearer';
|
|
40
97
|
/**
|
|
@@ -51,26 +108,82 @@ export interface AuthBrokerConfig {
|
|
|
51
108
|
* basic's user and password, the SNC fields, `serviceUrl`. `getProvider`
|
|
52
109
|
* needs it: there is no other source of means.
|
|
53
110
|
*/
|
|
54
|
-
serviceKeyStore?: IServiceKeyStore;
|
|
111
|
+
serviceKeyStore?: IServiceKeyStore | undefined;
|
|
55
112
|
/**
|
|
56
113
|
* The token API's source: the token provider, or a factory building one per
|
|
57
114
|
* destination. Not used by `getProvider`, which builds what the destination
|
|
58
115
|
* states.
|
|
59
116
|
*
|
|
60
|
-
* An instance is used as given, for every destination. A factory is
|
|
61
|
-
*
|
|
117
|
+
* An instance is used as given, for every destination. A factory is handed
|
|
118
|
+
* the destination's means and client, never a stored secret (see
|
|
119
|
+
* `TokenProviderFactory`).
|
|
62
120
|
*/
|
|
63
|
-
provider?: IRefreshableTokenProvider | TokenProviderFactory;
|
|
121
|
+
provider?: IRefreshableTokenProvider | TokenProviderFactory | undefined;
|
|
64
122
|
/** The interactive strategy of the UAA code, passcode and SAML grants. */
|
|
65
|
-
authorization?: (destination: string, grant: StrategyGrant) => IAuthorizationStrategy<string
|
|
123
|
+
authorization?: ((destination: string, grant: StrategyGrant) => IAuthorizationStrategy<string>) | undefined;
|
|
66
124
|
/** The interactive strategy of the OIDC authorization code grant. */
|
|
67
|
-
oidcAuthorization?: (destination: string) => IAuthorizationStrategy<OidcCallbackResult
|
|
125
|
+
oidcAuthorization?: ((destination: string) => IAuthorizationStrategy<OidcCallbackResult>) | undefined;
|
|
68
126
|
/** Where the device flow shows the user its code. */
|
|
69
|
-
deviceCodePresenter?: (destination: string) => IDeviceCodePresenter;
|
|
127
|
+
deviceCodePresenter?: ((destination: string) => IDeviceCodePresenter) | undefined;
|
|
70
128
|
/** `saml2_pure`: turns the SAMLResponse into the system's session cookies. */
|
|
71
|
-
samlCookies?: (destination: string) => (samlResponse: string) => Promise<string
|
|
129
|
+
samlCookies?: ((destination: string) => (samlResponse: string) => Promise<string>) | undefined;
|
|
72
130
|
/** The replay store the SAML assertion validators share. */
|
|
73
|
-
assertionReplayStore?: (destination: string) => IAssertionReplayStore;
|
|
131
|
+
assertionReplayStore?: ((destination: string) => IAssertionReplayStore) | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* How a destination's client authenticates to the authorization server, for
|
|
134
|
+
* every grant that authenticates one (`ClientAuthenticationGrant`). Called
|
|
135
|
+
* once per build of the destination's provider, with the client the key
|
|
136
|
+
* store answers and its certificate, read only if the strategy asks — and
|
|
137
|
+
* then re-read on every call, a change building a new provider.
|
|
138
|
+
*
|
|
139
|
+
* A given strategy always answers one, or throws: there is no "nothing"
|
|
140
|
+
* answer, so an explicit choice never falls back to the secret. A throw
|
|
141
|
+
* becomes a `DestinationConfigError` naming `clientAuthentication`, in fixed
|
|
142
|
+
* words. Absent: the client secret, as in 4.0.0, and nothing
|
|
143
|
+
* certificate-related is read. Shipped: `fromServiceKeyCertificate()`,
|
|
144
|
+
* `fromServiceKeySecret({ encoding })`.
|
|
145
|
+
*
|
|
146
|
+
* It applies to the providers the broker builds: `getProvider`'s, and the
|
|
147
|
+
* token API's `provider` factory, which gets its answer as a fourth
|
|
148
|
+
* argument (`TokenProviderClient`) for a grant that authenticates a client.
|
|
149
|
+
* An instance given as `provider` is already composed by the consumer, so
|
|
150
|
+
* the token API uses it as given and calls no strategy for it.
|
|
151
|
+
*/
|
|
152
|
+
clientAuthentication?: ClientAuthenticationStrategy | undefined;
|
|
153
|
+
/**
|
|
154
|
+
* How the provider the broker builds for a destination renews: called once
|
|
155
|
+
* per build of every token row (the UAA, OIDC and SAML grants), with the
|
|
156
|
+
* destination and the grant it states; the answer is the provider's
|
|
157
|
+
* `renewal`, unchanged. Required for those rows — there is no default: a
|
|
158
|
+
* token row built without it is refused naming `renewal`. `() =>
|
|
159
|
+
* refreshThenLogin()` is what 4.x did; `refreshOnly()` never logs in. Not
|
|
160
|
+
* called for `basic`, `snc` or `none`, nor for the token API's `provider`,
|
|
161
|
+
* which brings its own.
|
|
162
|
+
*/
|
|
163
|
+
renewal?: RenewalOption | undefined;
|
|
164
|
+
/**
|
|
165
|
+
* What a session write that did not land means — `'fail'`: the call that
|
|
166
|
+
* caused it fails (`unknown`, `persisting-tokens`), and while the
|
|
167
|
+
* destination's write is pending every `getProvider` / `getToken` /
|
|
168
|
+
* `refreshToken` of it retries the write first, and once more right before
|
|
169
|
+
* it succeeds, and is refused while it fails; `'continue'`: it is logged
|
|
170
|
+
* and the call goes on. Either way the write stays pending until the
|
|
171
|
+
* destination's next write or `flush()` — nothing retries it on its own.
|
|
172
|
+
* Required for every destination
|
|
173
|
+
* that writes a secret — a token row built by `getProvider`, and every call
|
|
174
|
+
* of the token API; no default.
|
|
175
|
+
*/
|
|
176
|
+
onWriteFailure?: 'fail' | 'continue' | undefined;
|
|
177
|
+
/**
|
|
178
|
+
* Passed to every token provider the broker builds; on only for `true`
|
|
179
|
+
* itself — absent, `false`, `'true'` or `1` is off. Never read from the
|
|
180
|
+
* environment. With it, a provider's one debug line for a refused token
|
|
181
|
+
* request names the request's secrets in its prepared form (the first and
|
|
182
|
+
* last four characters around a length marker), never the server's text;
|
|
183
|
+
* the line goes to the broker's logger. A consumer's `provider` — an
|
|
184
|
+
* instance or what a factory builds — keeps its own setting.
|
|
185
|
+
*/
|
|
186
|
+
authDebug?: boolean | undefined;
|
|
74
187
|
}
|
|
75
188
|
/**
|
|
76
189
|
* AuthBroker manages authentication tokens for destinations
|
|
@@ -80,28 +193,37 @@ export declare class AuthBroker {
|
|
|
80
193
|
private readonly serviceKeyStore;
|
|
81
194
|
private readonly sessionStore;
|
|
82
195
|
private readonly provider;
|
|
83
|
-
/**
|
|
84
|
-
private readonly
|
|
196
|
+
/** Per (destination, path): the provider handed out while unchanged. */
|
|
197
|
+
private readonly resolvedRow;
|
|
198
|
+
private readonly resolvedConsumer;
|
|
85
199
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* build, set before its first read.
|
|
200
|
+
* Per (destination, path): the slot of shared resolutions — every
|
|
201
|
+
* caller of that path is a waiter of its attempt.
|
|
89
202
|
*/
|
|
90
|
-
private readonly
|
|
203
|
+
private readonly rowSlots;
|
|
204
|
+
private readonly consumerSlots;
|
|
205
|
+
/** The broker-wide slot every `flush()` caller joins. */
|
|
206
|
+
private readonly flushSlot;
|
|
207
|
+
/** Per (destination, path): the last generation a build took. */
|
|
208
|
+
private readonly generations;
|
|
209
|
+
/** Per (destination, path): the newest generation that queued a write. */
|
|
210
|
+
private readonly newestWriter;
|
|
91
211
|
/**
|
|
92
|
-
* The
|
|
93
|
-
*
|
|
94
|
-
* for: the token API throws it to its caller, as 3.x did, while the broker
|
|
95
|
-
* keeps retrying. Dropped once a write of that result for that destination
|
|
96
|
-
* lands.
|
|
212
|
+
* The destinations whose means changed under the consumer's instance: the
|
|
213
|
+
* token API refuses them until a new broker.
|
|
97
214
|
*/
|
|
98
|
-
private readonly
|
|
215
|
+
private readonly instanceRefused;
|
|
99
216
|
private readonly authorization;
|
|
100
217
|
private readonly oidcAuthorization;
|
|
101
218
|
private readonly deviceCodePresenter;
|
|
102
219
|
private readonly samlCookies;
|
|
103
220
|
private readonly assertionReplayStore;
|
|
104
|
-
|
|
221
|
+
private readonly clientAuthentication;
|
|
222
|
+
private readonly renewal;
|
|
223
|
+
private readonly onWriteFailure;
|
|
224
|
+
/** `config.authDebug === true`: what every token provider it builds is told. */
|
|
225
|
+
private readonly authDebug;
|
|
226
|
+
/** Every write of the session secret: one plain queue per destination. */
|
|
105
227
|
private readonly writer;
|
|
106
228
|
/**
|
|
107
229
|
* @param config Stores and the provider (instance or factory)
|
|
@@ -114,26 +236,52 @@ export declare class AuthBroker {
|
|
|
114
236
|
*
|
|
115
237
|
* Without a consumer `provider`, the provider is the one `getProvider` hands
|
|
116
238
|
* out for the destination — the same instance, so one token and one renewal
|
|
117
|
-
* serve both — and what it obtains is written by its
|
|
239
|
+
* serve both — and what it obtains is written by its persistence. With one,
|
|
118
240
|
* the consumer's provider is asked and its answer written, as in 3.x — the
|
|
119
241
|
* secret alone.
|
|
120
242
|
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
243
|
+
* A provider's failure is relayed as the same object; a store's read
|
|
244
|
+
* failure reaches the caller as the store raised it. A write that did not
|
|
245
|
+
* land fails the call under `onWriteFailure: 'fail'` (`unknown`,
|
|
246
|
+
* `persisting-tokens`) — the token stands, and the write stays pending: the
|
|
247
|
+
* destination's next call retries it first and is refused while it fails —
|
|
248
|
+
* and is only logged under `'continue'`.
|
|
124
249
|
*
|
|
125
250
|
* @throws DestinationConfigError for a destination the key store states as
|
|
126
|
-
* `basic` or `snc`, before any provider is asked; without
|
|
127
|
-
* `
|
|
251
|
+
* `basic` or `snc`, before any provider is asked; without the
|
|
252
|
+
* `onWriteFailure` option; without a consumer `provider`, also for one
|
|
253
|
+
* whose provider obtains no token.
|
|
128
254
|
*/
|
|
129
|
-
getToken(destination: string): Promise<string>;
|
|
255
|
+
getToken(destination: string, options?: BrokerCallOptions): Promise<string>;
|
|
130
256
|
/**
|
|
131
257
|
* A new token for the destination, never the cached one — for a caller whose
|
|
132
258
|
* token the server has just refused. Calls the provider's `refreshTokens()`;
|
|
133
259
|
* a renewal already in flight for the destination is joined, not repeated.
|
|
134
260
|
*/
|
|
135
|
-
refreshToken(destination: string): Promise<string>;
|
|
261
|
+
refreshToken(destination: string, options?: BrokerCallOptions): Promise<string>;
|
|
262
|
+
/**
|
|
263
|
+
* The token API's one body. The call's signal releases this caller from
|
|
264
|
+
* every wait it has — the write checks, the means read, the resolution
|
|
265
|
+
* (whose waiter it is, never attaching), its own write — and is
|
|
266
|
+
* passed to the provider's `getTokens` / `refreshTokens`.
|
|
267
|
+
*/
|
|
136
268
|
private obtain;
|
|
269
|
+
/**
|
|
270
|
+
* Under `onWriteFailure: 'fail'`, a call of the destination is refused while
|
|
271
|
+
* its last write is pending — not yet landed: failed, or still queued
|
|
272
|
+
* or in flight. Asked on entry and right before success: the call awaits
|
|
273
|
+
* every write of the destination queued before this moment, and a failed
|
|
274
|
+
* one that no later write replaced is written once more — the destination's
|
|
275
|
+
* next write, queued like any other. All landed: on entry the call goes on,
|
|
276
|
+
* at the end it returns its success. Still failing: it rejects (`unknown`,
|
|
277
|
+
* `persisting-tokens`). A write queued after the check is not this call's.
|
|
278
|
+
* Under `'continue'` nothing is asked.
|
|
279
|
+
*
|
|
280
|
+
* The one wait is `writer.retry(destination)`'s promise, a waiter of its
|
|
281
|
+
* own slot with the call's signal: an abort releases the caller
|
|
282
|
+
* (`aborted`), never with success, and the write runs on.
|
|
283
|
+
*/
|
|
284
|
+
private settlePending;
|
|
137
285
|
/**
|
|
138
286
|
* The destination's means, read first: a destination the key store states
|
|
139
287
|
* as `basic` or `snc` holds no token, so the token API refuses it before any
|
|
@@ -142,38 +290,93 @@ export declare class AuthBroker {
|
|
|
142
290
|
* a 3.x setup, served as before.
|
|
143
291
|
*/
|
|
144
292
|
private statedForTokens;
|
|
145
|
-
/**
|
|
293
|
+
/**
|
|
294
|
+
* The token API on the row path's provider — the one `getProvider` hands
|
|
295
|
+
* out, resolved the same way, never through `getProvider` itself.
|
|
296
|
+
*/
|
|
146
297
|
private obtainShared;
|
|
147
298
|
/**
|
|
148
|
-
* The token API on the consumer's provider
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
299
|
+
* The token API on the consumer's provider: resolved on the consumer path,
|
|
300
|
+
* every answer — a cache hit included — written, the secret alone, through
|
|
301
|
+
* the broker's one write path, with the binding fixed when the provider was
|
|
302
|
+
* taken into use.
|
|
152
303
|
*/
|
|
153
304
|
private obtainFromConsumer;
|
|
154
|
-
/** The failure recorded for this result's write here, thrown as the store raised it. */
|
|
155
|
-
private throwFailedWrite;
|
|
156
305
|
/**
|
|
157
|
-
* The
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
|
|
162
|
-
|
|
306
|
+
* The slot of one (destination, path)'s resolutions: a caller of
|
|
307
|
+
* the path arriving while an attempt is active joins it; when every caller
|
|
308
|
+
* of an attempt has aborted, the attempt leaves the slot at once and the
|
|
309
|
+
* next caller starts afresh. A failed or aborted resolution is not kept.
|
|
310
|
+
*/
|
|
311
|
+
private slotOf;
|
|
312
|
+
/** Whether a build of this path was ever committed for the destination. */
|
|
313
|
+
private everBuilt;
|
|
314
|
+
/** A committed build's generation, from its path's counter. */
|
|
315
|
+
private nextGeneration;
|
|
316
|
+
/**
|
|
317
|
+
* Queues one write of a build of `path`. A write of a build older than the
|
|
318
|
+
* newest build of the same path that has queued a write for the destination
|
|
319
|
+
* is dropped — never written, never pending; a build of the other
|
|
320
|
+
* path never retires this one. A dropped write is not a failed one: the
|
|
321
|
+
* retired provider's holder keeps it, and the destination's session
|
|
322
|
+
* is the newer build's.
|
|
323
|
+
*/
|
|
324
|
+
private submit;
|
|
325
|
+
/** This call's store reads: each source read at most once. */
|
|
326
|
+
private storeReads;
|
|
327
|
+
/**
|
|
328
|
+
* The consumer path's provider for the destination: the cached one
|
|
329
|
+
* while everything it was built from is unchanged; else, for a factory, a
|
|
330
|
+
* new build — the factory called again, its provider starting with nothing
|
|
331
|
+
* — and, for an instance, which cannot be rebuilt, a refusal naming
|
|
332
|
+
* `provider` until a new broker.
|
|
333
|
+
*/
|
|
334
|
+
private resolveConsumer;
|
|
335
|
+
/**
|
|
336
|
+
* Takes the consumer's provider into use for the destination: a factory's
|
|
337
|
+
* build — handed the means and the client, never a stored secret — or an
|
|
338
|
+
* instance as given, handed nothing. The binding is fixed here, from the
|
|
339
|
+
* `serviceUrl` (the session's, else the key store's), the SAP client and
|
|
340
|
+
* the client of this build. Every store answer is read through `recorder`:
|
|
341
|
+
* what it read is the build's identity.
|
|
342
|
+
*/
|
|
343
|
+
private buildConsumer;
|
|
344
|
+
/**
|
|
345
|
+
* The token API's strategy path: with a `clientAuthentication` strategy and a
|
|
346
|
+
* destination stating a grant whose client authenticates, the strategy's
|
|
347
|
+
* answer — resolved inside the guard — and the client identity: the secret
|
|
348
|
+
* client, else the certificate client's, read through the context's memoised
|
|
349
|
+
* `readCertificate`. `null` without a strategy (4.0.0: nothing
|
|
350
|
+
* certificate-related is asked) and for a grant that authenticates no
|
|
351
|
+
* client (`saml2_pure`, `none`), as `getProvider` decides.
|
|
352
|
+
*
|
|
353
|
+
* The strategy is told the grant, so a destination that states none is
|
|
354
|
+
* refused before it is called rather than built without it. (An instance
|
|
355
|
+
* given as `provider` is the consumer's own composition: the strategy
|
|
356
|
+
* applies only to what the broker builds, so it is not used there.)
|
|
163
357
|
*/
|
|
164
|
-
private
|
|
358
|
+
private consumerClientAuthentication;
|
|
165
359
|
/**
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
360
|
+
* What a consumer's factory's credentials are composed from, read in the
|
|
361
|
+
* 3.x order: the session's authorization config; only when it has none, the
|
|
362
|
+
* key store's client. The stored session is not read: nothing of it is
|
|
363
|
+
* handed to the factory. A session store of auth-stores 3
|
|
169
364
|
* answers no client, so the key store's is what is found.
|
|
170
365
|
*/
|
|
171
|
-
private
|
|
366
|
+
private readAuthorization;
|
|
367
|
+
/**
|
|
368
|
+
* What the consumer path takes from its stores for one destination, read
|
|
369
|
+
* from `reads` — the same function for a build and for every later call's
|
|
370
|
+
* comparison: the means, the `serviceUrl` (the session's, else the key
|
|
371
|
+
* store's), the SAP client, the row, the client through its allowlist, and
|
|
372
|
+
* the factory's seed of allowlisted connection means.
|
|
373
|
+
*/
|
|
374
|
+
private consumerInputs;
|
|
172
375
|
/**
|
|
173
376
|
* `serviceUrl` for a consumer's provider, in the 3.x order: the session's,
|
|
174
377
|
* else the key store's, else an error before the provider is asked.
|
|
175
378
|
*/
|
|
176
|
-
private
|
|
379
|
+
private serviceUrlOf;
|
|
177
380
|
/**
|
|
178
381
|
* A store read where absence is an answer and anything else is not.
|
|
179
382
|
*
|
|
@@ -207,54 +410,104 @@ export declare class AuthBroker {
|
|
|
207
410
|
* built from the means the service key store holds and the secret the
|
|
208
411
|
* session store holds.
|
|
209
412
|
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
413
|
+
* A provider is never changed: every call re-reads what the
|
|
414
|
+
* destination's provider was built from — the means, the client, and the
|
|
415
|
+
* certificate client when the build read it — and answers the cached
|
|
416
|
+
* provider while all of it is unchanged. When anything changed, by a single
|
|
417
|
+
* character, it builds a new provider, which starts with nothing — no token,
|
|
418
|
+
* no refresh token, nothing of the old provider or of a session written
|
|
419
|
+
* under other means — and never hands the old one out again; whoever holds
|
|
420
|
+
* it keeps it, and its writes are dropped once the new one has written.
|
|
421
|
+
* Concurrent calls share one resolution; a resolution that threw is tried
|
|
422
|
+
* again on the next call.
|
|
212
423
|
*
|
|
213
424
|
* A token provider (the UAA, OIDC and SAML grants) is seeded from the
|
|
214
|
-
* session
|
|
215
|
-
*
|
|
216
|
-
*
|
|
425
|
+
* session only at its destination's first build in this broker, and only
|
|
426
|
+
* when the stored secret is bound to exactly this build's means (fully
|
|
427
|
+
* stated); it writes every token it obtains — at `prepare()`, on expiry, or
|
|
428
|
+
* in `rejected()` after a 401 — back to the session store before it answers
|
|
429
|
+
* (see `flush()`).
|
|
430
|
+
*
|
|
431
|
+
* `options.signal` releases this caller from every wait (`aborted`), and is
|
|
432
|
+
* attached to the token or SNC provider answered — built or cached — so a
|
|
433
|
+
* login it starts later is aborted once every session holding it has gone
|
|
434
|
+
*. Without a signal nothing is attached.
|
|
217
435
|
*
|
|
218
436
|
* @throws DestinationConfigError when the destination lacks what its type
|
|
219
437
|
* needs — naming the fields or options, never a value.
|
|
220
438
|
*/
|
|
221
|
-
getProvider(destination: string): Promise<IAuthProvider>;
|
|
439
|
+
getProvider(destination: string, options?: BrokerCallOptions): Promise<IAuthProvider>;
|
|
440
|
+
/**
|
|
441
|
+
* The row path's provider for the destination: the cached one while what
|
|
442
|
+
* its build read is unchanged, else a new build, committed with the
|
|
443
|
+
* next generation of the row path.
|
|
444
|
+
*/
|
|
445
|
+
private resolveRow;
|
|
446
|
+
/**
|
|
447
|
+
* One build of the destination's row. Every store answer it reads goes
|
|
448
|
+
* through `recorder` — what it read is its identity. `seeded`: whether the
|
|
449
|
+
* build may start from the stored session; `commit`: the generation its
|
|
450
|
+
* writes carry, set when the build is committed; `signal`: the build's
|
|
451
|
+
* attempt, handed to the `clientAuthentication` strategy. Answers
|
|
452
|
+
* the provider and the row it stated.
|
|
453
|
+
*/
|
|
222
454
|
private build;
|
|
455
|
+
/**
|
|
456
|
+
* A token row's refusal when the consumer's `renewal` or `onWriteFailure`
|
|
457
|
+
* is missing — named beside every other field and option the row lacks, in
|
|
458
|
+
* one error — decided before any collaborator is called. With a
|
|
459
|
+
* clientAuthentication strategy and no secret client, the client's fields
|
|
460
|
+
* are not judged: the strategy would tell the certificate client's.
|
|
461
|
+
*/
|
|
462
|
+
private rowRefusal;
|
|
223
463
|
/**
|
|
224
464
|
* The stored secret when it is bound to this destination's resource and
|
|
225
465
|
* issuer, else `null`: the provider is then built as with no session
|
|
226
|
-
* and logs in afresh — the refresh token is not spent either.
|
|
227
|
-
*
|
|
466
|
+
* and logs in afresh — the refresh token is not spent either.
|
|
467
|
+
*
|
|
468
|
+
* A stored credential not taken is logged in fixed words naming the
|
|
469
|
+
* destination only — never a URI, a record or a token: a `warn` when the
|
|
470
|
+
* binding could ever be seeded (`couldBeSeeded`) — the stored secret is not
|
|
471
|
+
* recorded under the current means: another resource, row, client,
|
|
472
|
+
* endpoint or trust, a 4.x record, or none — and a `debug` line when it
|
|
473
|
+
* never could — a row never seeded by design (`token_exchange`), means
|
|
474
|
+
* lacking what the record needs, or no `serviceUrl` — since that holds on
|
|
475
|
+
* every start and nothing was discarded by a change.
|
|
228
476
|
*/
|
|
229
477
|
private boundOrDiscarded;
|
|
230
478
|
/**
|
|
231
|
-
* Waits for
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
479
|
+
* Waits for every session write queued so far and gives each pending one —
|
|
480
|
+
* a write that did not land — one more attempt; rejects naming the
|
|
481
|
+
* destinations whose store still refuses (an `AggregateError` of
|
|
482
|
+
* `SessionWriteFailure`s). Nothing retries a pending write on its own: only
|
|
483
|
+
* the destination's next write or `flush()` does, so call it on shutdown to
|
|
484
|
+
* know whether every token a provider obtained is stored.
|
|
485
|
+
*
|
|
486
|
+
* Every caller joins one broker-wide slot: its signal releases that
|
|
487
|
+
* caller alone (`aborted`); the attempts run on, and a write still failing
|
|
488
|
+
* stays pending. The `AggregateError` is rethrown as the same object to
|
|
489
|
+
* every caller.
|
|
235
490
|
*/
|
|
236
|
-
flush(): Promise<void>;
|
|
491
|
+
flush(options?: BrokerCallOptions): Promise<void>;
|
|
237
492
|
/**
|
|
238
|
-
* One write of the destination's session secret — and nothing else
|
|
239
|
-
* `
|
|
240
|
-
*
|
|
241
|
-
* `{ sessionCookies, expiresAt, issuedFor, issuedBy }`: cookies are written
|
|
242
|
-
* as cookies, every other result as a token (`saml2_bearer` included). No means is ever written: not `serviceUrl`, not
|
|
243
|
-
* `authType`, not the client — they live in the key store, which the broker
|
|
244
|
-
* never writes.
|
|
493
|
+
* One write of the destination's session secret — and nothing else — in one
|
|
494
|
+
* `saveSession`, built from the write alone: the store merges, so every
|
|
495
|
+
* field that must not survive from an earlier write is stated.
|
|
245
496
|
*
|
|
246
|
-
* -
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
497
|
+
* - A credential: `{ authorizationToken, expiresAt, refreshToken, issuedFor,
|
|
498
|
+
* issuedBy }`, or for `saml2_pure`'s cookies (`tokenType: 'saml'`)
|
|
499
|
+
* `{ sessionCookies, expiresAt, refreshToken: '', issuedFor, issuedBy }` —
|
|
500
|
+
* SAML has no refresh token, and one stored beside earlier cookies or a
|
|
501
|
+
* token is not this credential's.
|
|
502
|
+
* - `refreshToken`: decided when the write was submitted — the build's own,
|
|
503
|
+
* or `''`, the store's clearing operation. The store is never read here.
|
|
504
|
+
* - `issuedFor` / `issuedBy`: the binding fixed when the provider was built,
|
|
505
|
+
* `issuedFor` written as `''` when the means lack its source — never left
|
|
506
|
+
* out, so no earlier binding survives beside a new credential.
|
|
507
|
+
* - A write that holds no credential (a refresh token discarded before any
|
|
508
|
+
* was held) writes only `refreshToken: ''`: the stored credential keeps
|
|
509
|
+
* its own binding — never re-labelled — and loses its refresh token.
|
|
510
|
+
* - `expiresAt`: fixed when the credential arrived.
|
|
258
511
|
* - A destination the key store now states as `basic` or `snc` is not
|
|
259
512
|
* written: those obtain no session secret.
|
|
260
513
|
*/
|
|
@@ -262,8 +515,9 @@ export declare class AuthBroker {
|
|
|
262
515
|
/**
|
|
263
516
|
* An `ITokenRefresher` for one destination, for injection into a connection:
|
|
264
517
|
* `getToken()` is the broker's `getToken`, `refreshToken()` its forced
|
|
265
|
-
* `refreshToken
|
|
518
|
+
* `refreshToken` — each call a waiter with `options.signal`, so a
|
|
519
|
+
* refresher held by a session ends with it.
|
|
266
520
|
*/
|
|
267
|
-
createTokenRefresher(destination: string): ITokenRefresher;
|
|
521
|
+
createTokenRefresher(destination: string, options?: BrokerCallOptions): ITokenRefresher;
|
|
268
522
|
}
|
|
269
523
|
//# sourceMappingURL=AuthBroker.d.ts.map
|
package/dist/AuthBroker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthBroker.d.ts","sourceRoot":"","sources":["../src/AuthBroker.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"AuthBroker.d.ts","sourceRoot":"","sources":["../src/AuthBroker.ts"],"names":[],"mappings":"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"}
|