@mcp-abap-adt/auth-broker 3.1.0 → 4.1.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +531 -0
  2. package/README.md +1062 -220
  3. package/dist/AuthBroker.d.ts +249 -30
  4. package/dist/AuthBroker.d.ts.map +1 -1
  5. package/dist/AuthBroker.js +681 -115
  6. package/dist/DestinationConfigError.d.ts +27 -0
  7. package/dist/DestinationConfigError.d.ts.map +1 -0
  8. package/dist/DestinationConfigError.js +36 -0
  9. package/dist/SessionWriter.d.ts +54 -0
  10. package/dist/SessionWriter.d.ts.map +1 -0
  11. package/dist/SessionWriter.js +135 -0
  12. package/dist/binding.d.ts +117 -0
  13. package/dist/binding.d.ts.map +1 -0
  14. package/dist/binding.js +223 -0
  15. package/dist/bindingOf.d.ts +37 -0
  16. package/dist/bindingOf.d.ts.map +1 -0
  17. package/dist/bindingOf.js +53 -0
  18. package/dist/clientAuthentication.d.ts +109 -0
  19. package/dist/clientAuthentication.d.ts.map +1 -0
  20. package/dist/clientAuthentication.js +218 -0
  21. package/dist/destinations.d.ts +164 -0
  22. package/dist/destinations.d.ts.map +1 -0
  23. package/dist/destinations.js +473 -0
  24. package/dist/index.d.ts +10 -4
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +14 -7
  27. package/dist/providers/ITokenProvider.d.ts +2 -1
  28. package/dist/providers/ITokenProvider.d.ts.map +1 -1
  29. package/dist/providers/index.d.ts +2 -2
  30. package/dist/providers/index.js +2 -2
  31. package/dist/stores/index.d.ts +2 -2
  32. package/dist/stores/index.js +2 -2
  33. package/dist/stores/interfaces.d.ts +4 -3
  34. package/dist/stores/interfaces.d.ts.map +1 -1
  35. package/dist/stores/interfaces.js +2 -2
  36. package/dist/types.d.ts +5 -2
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/types.js +3 -1
  39. package/package.json +16 -8
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ /**
3
+ * What a destination's means bind its secret to: the one
4
+ * place the broker chooses which binding a destination has. `getProvider`
5
+ * builds each provider with it, and `persist` writes what it gives beside every
6
+ * secret; `bindingOf` hands the same to a consumer.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.destinationBinding = destinationBinding;
10
+ exports.bindingOf = bindingOf;
11
+ const binding_1 = require("./binding");
12
+ const destinations_1 = require("./destinations");
13
+ /**
14
+ * The binding of a `jwt` or `saml` destination with the grant it states: the
15
+ * UAA grants and `saml2_bearer` by `uaaUrl` and the client, the OIDC grants by
16
+ * the issuer and the client, `saml2_pure` by its ACS, and `none` by what the
17
+ * means state.
18
+ */
19
+ function destinationBinding(authType, grant, means, client) {
20
+ if (grant === 'none')
21
+ return (0, binding_1.handedOverBinding)(authType, means, client);
22
+ if ((0, destinations_1.isOidcGrant)(grant))
23
+ return (0, binding_1.oidcBinding)(means, client);
24
+ if (grant === 'saml2_pure')
25
+ return (0, binding_1.samlPureBinding)(means);
26
+ return (0, binding_1.uaaBinding)(means, client);
27
+ }
28
+ /**
29
+ * The binding the broker writes beside a secret for a destination with these
30
+ * means and this client — canonical, exactly what `getProvider` compares a
31
+ * stored secret against. For a consumer that hands over a credential the
32
+ * broker cannot obtain (a `none` destination: a token or cookies it was
33
+ * given) and writes it to the session store itself: written with this binding,
34
+ * `getProvider` presents it; written with any other, it refuses.
35
+ *
36
+ * `means` are the key store's (`authType`, `grantType`, `serviceUrl`,
37
+ * `sapClient`, the issuer or ACS); `client` is the key store's client, when the
38
+ * destination has one. A destination that states no `jwt` / `saml` type or no
39
+ * grant binds nothing: `{}`. Never logs, never throws.
40
+ */
41
+ function bindingOf(means, client = null) {
42
+ const authType = means.authType;
43
+ const grant = means.grantType;
44
+ if ((authType !== 'jwt' && authType !== 'saml') || !grant)
45
+ return {};
46
+ const { issuedFor, issuedBy } = destinationBinding(authType, grant, means, client);
47
+ const binding = {};
48
+ if (issuedFor !== undefined)
49
+ binding.issuedFor = issuedFor;
50
+ if (issuedBy !== undefined)
51
+ binding.issuedBy = issuedBy;
52
+ return binding;
53
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * How a destination's client authenticates to the authorization server — the
3
+ * consumer's choice, as a strategy (`AuthBrokerConfig.clientAuthentication`).
4
+ *
5
+ * The broker ships two factories, each failing closed: one answers the
6
+ * certificate the key store holds, one the secret. Neither falls back to the
7
+ * other; a consumer that wants a fallback composes it, and its order is the
8
+ * consumer's statement. Whatever a strategy throws, the broker turns into a
9
+ * `DestinationConfigError` in fixed words (`resolveClientAuthentication`) —
10
+ * nothing of the thrown value, no `cause`.
11
+ */
12
+ import type { IClientAuthentication } from '@mcp-abap-adt/interfaces-auth';
13
+ import type { IClientCertificate } from '@mcp-abap-adt/interfaces-auth-broker';
14
+ import type { IAuthorizationConfig } from '@mcp-abap-adt/interfaces-auth-sap';
15
+ /**
16
+ * Every grant whose client authenticates to the authorization server: the
17
+ * three UAA grants, the OIDC grants and `saml2_bearer`. (`saml2_pure` and the
18
+ * basic, SNC and handed-over rows authenticate no client and never call the
19
+ * strategy.)
20
+ */
21
+ export type ClientAuthenticationGrant = 'client_credentials' | 'authorization_code' | 'passcode' | 'oidc_authorization_code' | 'device_code' | 'password' | 'token_exchange' | 'saml2_bearer';
22
+ /** What a strategy is given, once per build of a destination's provider. */
23
+ export interface ClientAuthenticationContext {
24
+ readonly destination: string;
25
+ readonly grant: ClientAuthenticationGrant;
26
+ /**
27
+ * The secret client the key store's `getAuthorizationConfig` answered —
28
+ * `uaaUrl`, `uaaClientId`, `uaaClientSecret` only, never a refresh token —
29
+ * or null.
30
+ */
31
+ readonly client: IAuthorizationConfig | null;
32
+ /**
33
+ * The key store's certificate client, read only when called and at most
34
+ * once per build; `null` when the store has none or implements no
35
+ * `getClientCertificate`.
36
+ */
37
+ readCertificate(): Promise<IClientCertificate | null>;
38
+ }
39
+ /** The strategy `AuthBrokerConfig.clientAuthentication` takes. */
40
+ export type ClientAuthenticationStrategy = (context: ClientAuthenticationContext) => Promise<IClientAuthentication>;
41
+ /**
42
+ * `tls_client_auth` with the certificate and key the key store holds, against
43
+ * `${certUrl}/oauth/token`. The material is handed over as given and checked
44
+ * before the factory answers — auth-providers checks it only on first use, so
45
+ * a malformed, incomplete or expired PEM would otherwise surface only at the
46
+ * first token request, in a provider already built and cached.
47
+ *
48
+ * Throws "the destination has no client certificate" when the store answers
49
+ * `null` or implements no `getClientCertificate`.
50
+ */
51
+ export declare function fromServiceKeyCertificate(): ClientAuthenticationStrategy;
52
+ /** How `fromServiceKeySecret` writes the client id and secret. */
53
+ export interface FromServiceKeySecretOptions {
54
+ /**
55
+ * `'raw'`: `base64(id:secret)` as given — XSUAA accepts only this
56
+ * (measured). `'form'`: each component form-encoded first (RFC 6749
57
+ * §2.3.1) — UAA, Keycloak. Required: it depends on the server.
58
+ */
59
+ readonly encoding: 'raw' | 'form';
60
+ }
61
+ /**
62
+ * `client_secret_basic` with the secret client the key store holds.
63
+ *
64
+ * Throws "the destination has no client secret" when the store answers no
65
+ * secret client, or one with an empty secret. A missing or unknown `encoding`
66
+ * is refused when the factory is made.
67
+ */
68
+ export declare function fromServiceKeySecret(options: FromServiceKeySecretOptions): ClientAuthenticationStrategy;
69
+ /**
70
+ * What a strategy is told of the secret client: an allowlist — who it is
71
+ * (`uaaUrl`, `uaaClientId`) and its secret (`uaaClientSecret`, which
72
+ * `fromServiceKeySecret` sends) — never a refresh token or any other field a
73
+ * store's `getAuthorizationConfig` carried, which no binding has checked.
74
+ */
75
+ export declare function contextClient(client: IAuthorizationConfig | null): IAuthorizationConfig | null;
76
+ /**
77
+ * The context for one build: the client through `contextClient`;
78
+ * `readCertificate` lazy and memoised — the first call reads, every later one
79
+ * gets the same answer.
80
+ */
81
+ export declare function clientAuthenticationContext(destination: string, grant: ClientAuthenticationGrant, client: IAuthorizationConfig | null, read: () => Promise<IClientCertificate | null>): ClientAuthenticationContext;
82
+ /**
83
+ * Runs the strategy inside the guard. Any throw — the strategy's own, a
84
+ * store's, a malformed PEM — and any answer that is no client authentication
85
+ * becomes a `DestinationConfigError` naming `clientAuthentication`, in fixed
86
+ * words: no `cause`, nothing of the thrown value.
87
+ */
88
+ export declare function resolveClientAuthentication(strategy: ClientAuthenticationStrategy, context: ClientAuthenticationContext): Promise<IClientAuthentication>;
89
+ /**
90
+ * Who the client is — its authorization server and client id — and nothing
91
+ * else: no secret field, so it can never be taken for a (public) secret
92
+ * client, and never PEM.
93
+ */
94
+ export interface ClientIdentity {
95
+ readonly uaaUrl: string;
96
+ readonly uaaClientId: string;
97
+ }
98
+ /**
99
+ * The client identity a strategy-authenticated row and its binding take: the
100
+ * secret client's `uaaUrl` and `uaaClientId` when the key store has one, else
101
+ * the certificate client's `uaaUrl` and `clientId` — read through the
102
+ * context's memoised `readCertificate`, so at most once per build — else
103
+ * `null`. The secret stays behind: beside a strategy a row passes none.
104
+ *
105
+ * A failing read is a `DestinationConfigError` naming `clientAuthentication`
106
+ * in fixed words — nothing of what the store threw, no `cause`.
107
+ */
108
+ export declare function clientIdentity(context: ClientAuthenticationContext): Promise<ClientIdentity | null>;
109
+ //# sourceMappingURL=clientAuthentication.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clientAuthentication.d.ts","sourceRoot":"","sources":["../src/clientAuthentication.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAG9E;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GACjC,oBAAoB,GACpB,oBAAoB,GACpB,UAAU,GACV,yBAAyB,GACzB,aAAa,GACb,UAAU,GACV,gBAAgB,GAChB,cAAc,CAAC;AAEnB,4EAA4E;AAC5E,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,yBAAyB,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC7C;;;;OAIG;IACH,eAAe,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;CACvD;AAED,kEAAkE;AAClE,MAAM,MAAM,4BAA4B,GAAG,CACzC,OAAO,EAAE,2BAA2B,KACjC,OAAO,CAAC,qBAAqB,CAAC,CAAC;AAsBpC;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,IAAI,4BAA4B,CAgBxE;AAED,kEAAkE;AAClE,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,MAAM,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,2BAA2B,GACnC,4BAA4B,CAc9B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAClC,oBAAoB,GAAG,IAAI,CAO7B;AAmBD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,yBAAyB,EAChC,MAAM,EAAE,oBAAoB,GAAG,IAAI,EACnC,IAAI,EAAE,MAAM,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,GAC7C,2BAA2B,CAW7B;AAiCD;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,4BAA4B,EACtC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,qBAAqB,CAAC,CAyBhC;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAiBhC"}
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ /**
3
+ * How a destination's client authenticates to the authorization server — the
4
+ * consumer's choice, as a strategy (`AuthBrokerConfig.clientAuthentication`).
5
+ *
6
+ * The broker ships two factories, each failing closed: one answers the
7
+ * certificate the key store holds, one the secret. Neither falls back to the
8
+ * other; a consumer that wants a fallback composes it, and its order is the
9
+ * consumer's statement. Whatever a strategy throws, the broker turns into a
10
+ * `DestinationConfigError` in fixed words (`resolveClientAuthentication`) —
11
+ * nothing of the thrown value, no `cause`.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.fromServiceKeyCertificate = fromServiceKeyCertificate;
15
+ exports.fromServiceKeySecret = fromServiceKeySecret;
16
+ exports.contextClient = contextClient;
17
+ exports.clientAuthenticationContext = clientAuthenticationContext;
18
+ exports.resolveClientAuthentication = resolveClientAuthentication;
19
+ exports.clientIdentity = clientIdentity;
20
+ const auth_providers_1 = require("@mcp-abap-adt/auth-providers");
21
+ const DestinationConfigError_1 = require("./DestinationConfigError");
22
+ const NO_CERTIFICATE = 'the destination has no client certificate';
23
+ const NO_SECRET = 'the destination has no client secret';
24
+ /**
25
+ * A shipped factory's refusal. Module-private, so nothing outside this file
26
+ * can make one — but a consumer composing a factory can catch and alter it,
27
+ * so the guard reads only `missing` and picks the words itself.
28
+ */
29
+ class ClientUnavailableError extends Error {
30
+ missing;
31
+ constructor(missing) {
32
+ super(missing === 'certificate' ? NO_CERTIFICATE : NO_SECRET);
33
+ this.missing = missing;
34
+ this.name = 'ClientUnavailableError';
35
+ }
36
+ }
37
+ /** The broker's words for auth-providers' CertificateMaterialError, by its flags. */
38
+ const CERTIFICATE_INCOMPLETE = 'the client certificate is incomplete';
39
+ const CERTIFICATE_EXPIRED = 'the client certificate has expired';
40
+ const CERTIFICATE_UNUSABLE = 'the client certificate could not be used';
41
+ /**
42
+ * `tls_client_auth` with the certificate and key the key store holds, against
43
+ * `${certUrl}/oauth/token`. The material is handed over as given and checked
44
+ * before the factory answers — auth-providers checks it only on first use, so
45
+ * a malformed, incomplete or expired PEM would otherwise surface only at the
46
+ * first token request, in a provider already built and cached.
47
+ *
48
+ * Throws "the destination has no client certificate" when the store answers
49
+ * `null` or implements no `getClientCertificate`.
50
+ */
51
+ function fromServiceKeyCertificate() {
52
+ return async (context) => {
53
+ const certificate = await context.readCertificate();
54
+ if (!certificate) {
55
+ throw new ClientUnavailableError('certificate');
56
+ }
57
+ const authentication = (0, auth_providers_1.tlsClientCertificate)({
58
+ material: { cert: certificate.certificate, key: certificate.key },
59
+ endpoint: `${certificate.certUrl.replace(/\/+$/, '')}/oauth/token`,
60
+ });
61
+ if (typeof authentication.tlsMaterial !== 'function') {
62
+ throw new ClientUnavailableError('certificate');
63
+ }
64
+ await authentication.tlsMaterial();
65
+ return authentication;
66
+ };
67
+ }
68
+ /**
69
+ * `client_secret_basic` with the secret client the key store holds.
70
+ *
71
+ * Throws "the destination has no client secret" when the store answers no
72
+ * secret client, or one with an empty secret. A missing or unknown `encoding`
73
+ * is refused when the factory is made.
74
+ */
75
+ function fromServiceKeySecret(options) {
76
+ const encoding = options?.encoding;
77
+ if (encoding !== 'raw' && encoding !== 'form') {
78
+ throw new TypeError("fromServiceKeySecret: encoding must be 'raw' or 'form'");
79
+ }
80
+ return async (context) => {
81
+ const secret = context.client?.uaaClientSecret;
82
+ if (typeof secret !== 'string' || secret.length === 0) {
83
+ throw new ClientUnavailableError('secret');
84
+ }
85
+ return (0, auth_providers_1.clientSecretBasic)(secret, { encoding });
86
+ };
87
+ }
88
+ /**
89
+ * What a strategy is told of the secret client: an allowlist — who it is
90
+ * (`uaaUrl`, `uaaClientId`) and its secret (`uaaClientSecret`, which
91
+ * `fromServiceKeySecret` sends) — never a refresh token or any other field a
92
+ * store's `getAuthorizationConfig` carried, which no binding has checked.
93
+ */
94
+ function contextClient(client) {
95
+ if (!client)
96
+ return null;
97
+ return {
98
+ uaaUrl: client.uaaUrl,
99
+ uaaClientId: client.uaaClientId,
100
+ uaaClientSecret: client.uaaClientSecret,
101
+ };
102
+ }
103
+ /**
104
+ * The certificate client as the contract declares it — `uaaUrl`, `clientId`,
105
+ * `certificate`, `key`, `certUrl` — and nothing else a store answered with it.
106
+ */
107
+ function certificateOf(answer) {
108
+ if (!answer)
109
+ return null;
110
+ return {
111
+ uaaUrl: answer.uaaUrl,
112
+ clientId: answer.clientId,
113
+ certificate: answer.certificate,
114
+ key: answer.key,
115
+ certUrl: answer.certUrl,
116
+ };
117
+ }
118
+ /**
119
+ * The context for one build: the client through `contextClient`;
120
+ * `readCertificate` lazy and memoised — the first call reads, every later one
121
+ * gets the same answer.
122
+ */
123
+ function clientAuthenticationContext(destination, grant, client, read) {
124
+ let certificate;
125
+ return {
126
+ destination,
127
+ grant,
128
+ client: contextClient(client),
129
+ readCertificate: () => {
130
+ certificate ??= read().then(certificateOf);
131
+ return certificate;
132
+ },
133
+ };
134
+ }
135
+ const FAILED = 'the clientAuthentication strategy failed';
136
+ const REFUSED = 'the clientAuthentication strategy refused';
137
+ /**
138
+ * The fixed words for what a strategy threw. Every word is this module's own:
139
+ * the thrown value only selects among them, through `instanceof` and flags
140
+ * compared with `===` — never a message, never its `words`. Classifying may
141
+ * itself throw (a Proxy, a getter), so it runs in its own try and falls back
142
+ * to the generic words.
143
+ */
144
+ function refusalWords(error) {
145
+ try {
146
+ if (error instanceof ClientUnavailableError) {
147
+ const missing = error.missing;
148
+ if (missing === 'certificate')
149
+ return `${REFUSED}: ${NO_CERTIFICATE}`;
150
+ if (missing === 'secret')
151
+ return `${REFUSED}: ${NO_SECRET}`;
152
+ return FAILED;
153
+ }
154
+ if (error instanceof auth_providers_1.CertificateMaterialError) {
155
+ if (error.incomplete === true) {
156
+ return `${REFUSED}: ${CERTIFICATE_INCOMPLETE}`;
157
+ }
158
+ if (error.expired === true)
159
+ return `${REFUSED}: ${CERTIFICATE_EXPIRED}`;
160
+ return `${REFUSED}: ${CERTIFICATE_UNUSABLE}`;
161
+ }
162
+ }
163
+ catch {
164
+ // Fall through: the thrown value could not even be classified.
165
+ }
166
+ return FAILED;
167
+ }
168
+ /**
169
+ * Runs the strategy inside the guard. Any throw — the strategy's own, a
170
+ * store's, a malformed PEM — and any answer that is no client authentication
171
+ * becomes a `DestinationConfigError` naming `clientAuthentication`, in fixed
172
+ * words: no `cause`, nothing of the thrown value.
173
+ */
174
+ async function resolveClientAuthentication(strategy, context) {
175
+ let answer;
176
+ let usable;
177
+ try {
178
+ answer = await strategy(context);
179
+ // Read inside the guard: a getter on the answer may throw too.
180
+ usable =
181
+ typeof answer === 'object' &&
182
+ answer !== null &&
183
+ typeof answer.authenticate === 'function';
184
+ }
185
+ catch (error) {
186
+ throw new DestinationConfigError_1.DestinationConfigError(context.destination, ['clientAuthentication'], refusalWords(error));
187
+ }
188
+ if (!usable) {
189
+ throw new DestinationConfigError_1.DestinationConfigError(context.destination, ['clientAuthentication'], 'the clientAuthentication strategy answered no client authentication');
190
+ }
191
+ return answer;
192
+ }
193
+ /**
194
+ * The client identity a strategy-authenticated row and its binding take: the
195
+ * secret client's `uaaUrl` and `uaaClientId` when the key store has one, else
196
+ * the certificate client's `uaaUrl` and `clientId` — read through the
197
+ * context's memoised `readCertificate`, so at most once per build — else
198
+ * `null`. The secret stays behind: beside a strategy a row passes none.
199
+ *
200
+ * A failing read is a `DestinationConfigError` naming `clientAuthentication`
201
+ * in fixed words — nothing of what the store threw, no `cause`.
202
+ */
203
+ async function clientIdentity(context) {
204
+ const client = context.client;
205
+ if (client) {
206
+ return { uaaUrl: client.uaaUrl, uaaClientId: client.uaaClientId };
207
+ }
208
+ let certificate;
209
+ try {
210
+ certificate = await context.readCertificate();
211
+ }
212
+ catch {
213
+ throw new DestinationConfigError_1.DestinationConfigError(context.destination, ['clientAuthentication'], 'the client certificate could not be read');
214
+ }
215
+ if (!certificate)
216
+ return null;
217
+ return { uaaUrl: certificate.uaaUrl, uaaClientId: certificate.clientId };
218
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * What a destination states, and the provider it states.
3
+ *
4
+ * Two fields choose the provider — `authType` and, for `jwt` and `saml`,
5
+ * `grantType` — and nothing else: no field's presence or absence is ever
6
+ * read to pick a row. Both come from the key store's means.
7
+ */
8
+ import { type IDeviceCodePresenter, type OidcCallbackResult } from '@mcp-abap-adt/auth-providers';
9
+ import type { IAssertionReplayStore, IAuthorizationStrategy, IAuthProvider, IClientAuthentication, ITokenResult } from '@mcp-abap-adt/interfaces-auth';
10
+ import type { DestinationGrant, IConfig, IConnectionConfig } from '@mcp-abap-adt/interfaces-auth-broker';
11
+ import type { IAuthorizationConfig } from '@mcp-abap-adt/interfaces-auth-sap';
12
+ import type { ILogger } from '@mcp-abap-adt/interfaces-utils';
13
+ import { type Binding } from './binding';
14
+ import type { ClientIdentity } from './clientAuthentication';
15
+ type StatedAuthType = NonNullable<IConnectionConfig['authType']>;
16
+ /** The stated `authType`, or the error naming it. */
17
+ export declare function statedAuthType(destination: string, means: IConnectionConfig | null): StatedAuthType;
18
+ /** The stated grant of a `jwt` or `saml` destination, or the error naming it. */
19
+ export declare function statedGrant(destination: string, authType: 'jwt' | 'saml', means: IConnectionConfig): DestinationGrant;
20
+ /**
21
+ * A row's client: the key store's secret client, or — on the strategy path
22
+ * without one — the certificate client's identity, which carries no secret
23
+ * field at all, so it can never pass for a (public) secret client.
24
+ */
25
+ export type RowClient = IAuthorizationConfig | ClientIdentity;
26
+ /** `basic` → `BasicAuthProvider(username, password)`, from the means alone. */
27
+ export declare function basicProvider(destination: string, means: IConnectionConfig): IAuthProvider;
28
+ /**
29
+ * `snc` → the Secure Login Client recipe, from the four SNC fields. Only the
30
+ * partner name is required: the contract says the library is discovered when
31
+ * `sncLib` is absent and the user's name taken from the credential when
32
+ * `sncMyName` is, and the provider documents its own `qop` default.
33
+ */
34
+ export declare function sncProvider(destination: string, means: IConnectionConfig, logger: ILogger): IAuthProvider;
35
+ /**
36
+ * `none` → the credential handed over, from the session: the only way in, so
37
+ * required. Read from the secret alone — the means are not a seed.
38
+ *
39
+ * The broker did not obtain it and cannot obtain it again, so a binding that
40
+ * does not match is refused, never discarded: `issuedFor`
41
+ * must always equal the destination's resource — it is what stops the
42
+ * credential going to another one; `issuedBy` is compared only when the means
43
+ * state an issuer.
44
+ */
45
+ export declare function handedOverProvider(destination: string, authType: 'jwt' | 'saml', secret: IConfig | null, binding: Binding): IAuthProvider;
46
+ /** The UAA grants: one client, one token endpoint (`<uaaUrl>/oauth/token`). */
47
+ export type UaaGrant = 'authorization_code' | 'client_credentials' | 'passcode';
48
+ export declare function isUaaGrant(grant: DestinationGrant): grant is UaaGrant;
49
+ /** What a UAA row is built from. */
50
+ export interface UaaRow {
51
+ destination: string;
52
+ grant: UaaGrant;
53
+ /**
54
+ * The client: the key store's `getAuthorizationConfig` — or, with a
55
+ * strategy and no secret client, the certificate client's identity.
56
+ */
57
+ client: RowClient | null;
58
+ /** The session secret — the seed; `null` when there is no session. */
59
+ secret: IConfig | null;
60
+ /**
61
+ * The consumer strategy's answer. Given, the provider authenticates its
62
+ * client with it and gets no `clientSecret`; absent, 4.0.0's secret.
63
+ */
64
+ clientAuthentication?: IClientAuthentication;
65
+ /** The consumer's `authorization` option. */
66
+ authorization: ((destination: string, grant: 'authorization_code' | 'passcode') => IAuthorizationStrategy<string>) | undefined;
67
+ logger: ILogger;
68
+ onTokens: (result: ITokenResult) => Promise<void>;
69
+ }
70
+ /**
71
+ * `jwt` / `authorization_code` → `AuthorizationCodeProvider`,
72
+ * `client_credentials` → `ClientCredentialsProvider`, `passcode` →
73
+ * `UaaPasscodeProvider`: the client from the key store, the seed from the
74
+ * session (not for `client_credentials`, whose row takes the client alone).
75
+ *
76
+ * Every field and option the row lacks is named in one error, before the
77
+ * consumer's `authorization` is called. `serviceUrl` is not among them: the
78
+ * providers read the client and `uaaUrl` only. Without a strategy,
79
+ * `uaaClientSecret: ''` is a public client: `passcode` takes it as no secret;
80
+ * the other two rows' providers require a secret, so for them `''` is
81
+ * missing. On the strategy path the client may be a certificate client's
82
+ * identity, which holds no secret field at all (`RowClient`).
83
+ *
84
+ * With the strategy's answer (`clientAuthentication`) no secret is required
85
+ * and none is passed: the provider authenticates its client with the answer.
86
+ */
87
+ export declare function uaaProvider(row: UaaRow): IAuthProvider;
88
+ /** The OIDC grants: a client, and an issuer or explicit endpoints. */
89
+ export type OidcGrant = 'oidc_authorization_code' | 'device_code' | 'password' | 'token_exchange';
90
+ export declare function isOidcGrant(grant: DestinationGrant): grant is OidcGrant;
91
+ /** What an OIDC row is built from. */
92
+ export interface OidcRow {
93
+ destination: string;
94
+ grant: OidcGrant;
95
+ means: IConnectionConfig;
96
+ /** The client, from the key store's `getAuthorizationConfig`. */
97
+ client: RowClient | null;
98
+ /** The session secret — the seed; `null` when there is none, or it is not bound here. */
99
+ secret: IConfig | null;
100
+ /** The consumer strategy's answer: given, it replaces the secret. */
101
+ clientAuthentication?: IClientAuthentication;
102
+ oidcAuthorization: ((destination: string) => IAuthorizationStrategy<OidcCallbackResult>) | undefined;
103
+ deviceCodePresenter: ((destination: string) => IDeviceCodePresenter) | undefined;
104
+ logger: ILogger;
105
+ onTokens: (result: ITokenResult) => Promise<void>;
106
+ }
107
+ /**
108
+ * `jwt` / `oidc_authorization_code` → `OidcBrowserProvider`, `device_code` →
109
+ * `OidcDeviceFlowProvider`, `password` → `OidcPasswordProvider`,
110
+ * `token_exchange` → `OidcTokenExchangeProvider`: the client id (and secret,
111
+ * `''` being a public client) from the key store's client, the grant's data
112
+ * from its `oidc*` fields, the seed from the session.
113
+ *
114
+ * The endpoints: `oidcIssuerUrl`, from which the provider discovers them, or
115
+ * every explicit endpoint the row uses — without either the error names the
116
+ * issuer and the endpoints missing. Every field and option the row lacks is
117
+ * named in one error, before any collaborator is called. `token_exchange`
118
+ * takes one scope string: the stored scopes joined by a space.
119
+ */
120
+ export declare function oidcProvider(row: OidcRow): IAuthProvider;
121
+ /** The SAML grants: an identity provider's assertion, validated first. */
122
+ export type SamlGrant = 'saml2_pure' | 'saml2_bearer';
123
+ export declare function isSamlGrant(grant: DestinationGrant): grant is SamlGrant;
124
+ /** What a SAML row is built from. */
125
+ export interface SamlRow {
126
+ destination: string;
127
+ grant: SamlGrant;
128
+ means: IConnectionConfig;
129
+ /** `saml2_bearer`'s client, from the key store; `null` for `saml2_pure`. */
130
+ client: RowClient | null;
131
+ /** The session secret — the seed; `null` when there is none, or it is not bound here. */
132
+ secret: IConfig | null;
133
+ /** `saml2_bearer`: the consumer strategy's answer; given, it replaces the secret. */
134
+ clientAuthentication?: IClientAuthentication;
135
+ authorization: ((destination: string, grant: SamlGrant) => IAuthorizationStrategy<string>) | undefined;
136
+ samlCookies: ((destination: string) => (samlResponse: string) => Promise<string>) | undefined;
137
+ assertionReplayStore: ((destination: string) => IAssertionReplayStore) | undefined;
138
+ logger: ILogger;
139
+ onTokens: (result: ITokenResult) => Promise<void>;
140
+ }
141
+ /**
142
+ * `saml` / `saml2_pure` → `Saml2PureProvider`, `saml2_bearer` →
143
+ * `Saml2BearerProvider`, each validating the assertion before anything uses
144
+ * it, with the validator the broker composes from the destination's trust and
145
+ * the consumer's replay store: `createSignedResponseValidator` for
146
+ * `saml2_pure` — the Response signed, as the cookies' system receives it whole
147
+ * — and `createSignedAssertionValidator` for `saml2_bearer`, whose token
148
+ * endpoint gets the Assertion alone (auth-providers' own pairing). The
149
+ * expected issuer is `samlIdpEntityId`.
150
+ *
151
+ * `saml2_pure` is seeded with the stored cookies and `expiresAt`;
152
+ * `saml2_bearer` with the stored token, its refresh token and expiry, and
153
+ * takes the client (`uaaUrl`, `uaaClientId`; without a strategy,
154
+ * `uaaClientSecret` `''` a public client; on the strategy path the identity
155
+ * alone, no secret) and `samlTokenUrl` when stated.
156
+ *
157
+ * Every field and option the row lacks is named in one error, before any
158
+ * collaborator is called; so is a `samlClockSkewMs` that is not a whole number
159
+ * of milliseconds, and a certificate the validator cannot read — by name, never
160
+ * the value.
161
+ */
162
+ export declare function samlProvider(row: SamlRow): IAuthProvider;
163
+ export {};
164
+ //# sourceMappingURL=destinations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"destinations.d.ts","sourceRoot":"","sources":["../src/destinations.ts"],"names":[],"mappings":"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"}