@mcp-abap-adt/auth-broker 3.0.4 → 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 +448 -1
- package/README.md +723 -533
- package/dist/AuthBroker.d.ts +178 -29
- package/dist/AuthBroker.d.ts.map +1 -1
- package/dist/AuthBroker.js +494 -101
- package/dist/DestinationConfigError.d.ts +27 -0
- package/dist/DestinationConfigError.d.ts.map +1 -0
- package/dist/DestinationConfigError.js +36 -0
- package/dist/SessionWriter.d.ts +54 -0
- package/dist/SessionWriter.d.ts.map +1 -0
- package/dist/SessionWriter.js +135 -0
- package/dist/binding.d.ts +95 -0
- package/dist/binding.d.ts.map +1 -0
- package/dist/binding.js +207 -0
- package/dist/bindingOf.d.ts +36 -0
- package/dist/bindingOf.d.ts.map +1 -0
- package/dist/bindingOf.js +53 -0
- package/dist/destinations.d.ts +139 -0
- package/dist/destinations.d.ts.map +1 -0
- package/dist/destinations.js +429 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -7
- package/dist/providers/ITokenProvider.d.ts +2 -1
- package/dist/providers/ITokenProvider.d.ts.map +1 -1
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js +2 -2
- package/dist/stores/index.d.ts +2 -2
- package/dist/stores/index.js +2 -2
- package/dist/stores/interfaces.d.ts +4 -3
- package/dist/stores/interfaces.d.ts.map +1 -1
- package/dist/stores/interfaces.js +2 -2
- package/dist/types.d.ts +5 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -1
- package/package.json +25 -45
- package/CONTRIBUTORS.md +0 -33
- package/dist/__tests__/helpers/configHelpers.d.ts +0 -59
- package/dist/__tests__/helpers/configHelpers.d.ts.map +0 -1
- package/dist/__tests__/helpers/configHelpers.js +0 -206
- package/dist/__tests__/helpers/netHelpers.d.ts +0 -3
- package/dist/__tests__/helpers/netHelpers.d.ts.map +0 -1
- package/dist/__tests__/helpers/netHelpers.js +0 -63
- package/dist/__tests__/helpers/testLogger.d.ts +0 -7
- package/dist/__tests__/helpers/testLogger.d.ts.map +0 -1
- package/dist/__tests__/helpers/testLogger.js +0 -54
- package/dist/bin/mcp-auth.js +0 -752
- package/dist/bin/mcp-sso.js +0 -825
- package/dist/bin/mcpSsoConfig.js +0 -566
- package/dist/bin/samlMetadata.js +0 -175
- package/dist/bin/workDir.js +0 -79
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A destination that lacks what its type needs.
|
|
3
|
+
*
|
|
4
|
+
* Thrown by `getProvider` before any provider is asked: a missing field is a
|
|
5
|
+
* fault no renewal cures, so it surfaces where the consumer builds its
|
|
6
|
+
* connector, not at the first `connect()`.
|
|
7
|
+
*
|
|
8
|
+
* **It carries names, never values.** `missingFields` holds store field names
|
|
9
|
+
* (`authType`, `username`, `sncQop`) or broker option names
|
|
10
|
+
* (`serviceKeyStore`, `provider`); the message is fixed wording plus the
|
|
11
|
+
* destination's name and those names. A stored value — a password, a token,
|
|
12
|
+
* even an `authType` that is not one of the four — never reaches it. There is
|
|
13
|
+
* no `cause`: a provider's own error quotes the value it refused (`qop … got
|
|
14
|
+
* '7'`), so it is not carried; its field names are mapped instead.
|
|
15
|
+
*/
|
|
16
|
+
export declare class DestinationConfigError extends Error {
|
|
17
|
+
readonly code: "DESTINATION_CONFIG";
|
|
18
|
+
readonly destination: string;
|
|
19
|
+
readonly missingFields: string[];
|
|
20
|
+
/**
|
|
21
|
+
* @param destination The destination's name, as the caller passed it.
|
|
22
|
+
* @param missingFields Field or option names only.
|
|
23
|
+
* @param reason Fixed wording: what is wrong, never a stored value.
|
|
24
|
+
*/
|
|
25
|
+
constructor(destination: string, missingFields: string[], reason: string);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=DestinationConfigError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DestinationConfigError.d.ts","sourceRoot":"","sources":["../src/DestinationConfigError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,IAAI,EAAG,oBAAoB,CAAU;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC;IAEjC;;;;OAIG;gBACS,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM;CASzE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DestinationConfigError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A destination that lacks what its type needs.
|
|
6
|
+
*
|
|
7
|
+
* Thrown by `getProvider` before any provider is asked: a missing field is a
|
|
8
|
+
* fault no renewal cures, so it surfaces where the consumer builds its
|
|
9
|
+
* connector, not at the first `connect()`.
|
|
10
|
+
*
|
|
11
|
+
* **It carries names, never values.** `missingFields` holds store field names
|
|
12
|
+
* (`authType`, `username`, `sncQop`) or broker option names
|
|
13
|
+
* (`serviceKeyStore`, `provider`); the message is fixed wording plus the
|
|
14
|
+
* destination's name and those names. A stored value — a password, a token,
|
|
15
|
+
* even an `authType` that is not one of the four — never reaches it. There is
|
|
16
|
+
* no `cause`: a provider's own error quotes the value it refused (`qop … got
|
|
17
|
+
* '7'`), so it is not carried; its field names are mapped instead.
|
|
18
|
+
*/
|
|
19
|
+
class DestinationConfigError extends Error {
|
|
20
|
+
code = 'DESTINATION_CONFIG';
|
|
21
|
+
destination;
|
|
22
|
+
missingFields;
|
|
23
|
+
/**
|
|
24
|
+
* @param destination The destination's name, as the caller passed it.
|
|
25
|
+
* @param missingFields Field or option names only.
|
|
26
|
+
* @param reason Fixed wording: what is wrong, never a stored value.
|
|
27
|
+
*/
|
|
28
|
+
constructor(destination, missingFields, reason) {
|
|
29
|
+
super(`Destination "${destination}": ${reason} (${missingFields.join(', ')})`);
|
|
30
|
+
this.name = 'DestinationConfigError';
|
|
31
|
+
this.destination = destination;
|
|
32
|
+
this.missingFields = [...missingFields];
|
|
33
|
+
Object.setPrototypeOf(this, DestinationConfigError.prototype);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.DestinationConfigError = DestinationConfigError;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The broker's writes of a destination's session secret, retried until the
|
|
3
|
+
* store takes them.
|
|
4
|
+
*
|
|
5
|
+
* A store failure is the storage's or a broker bug, never the token's, so it
|
|
6
|
+
* does not fail the authentication: the result stays pending for its
|
|
7
|
+
* destination and the writer tries again on its own — it does not wait for the
|
|
8
|
+
* provider to be called again, since a request may be the process's last.
|
|
9
|
+
*
|
|
10
|
+
* - **One pending result per destination, the latest.** A newer result
|
|
11
|
+
* replaces the one waiting; an older one is never written after it.
|
|
12
|
+
* - **Attempts for one destination never overlap.** Each runs after the
|
|
13
|
+
* previous one has settled.
|
|
14
|
+
* - **A growing delay:** one second after the first failure, doubling, capped
|
|
15
|
+
* at one minute; reset once a write lands. The timer is `unref()`ed, so a
|
|
16
|
+
* pending write never keeps a process alive.
|
|
17
|
+
* - **Failures are logged by class name** — never a message: the store holds
|
|
18
|
+
* tokens, and its errors are foreign text.
|
|
19
|
+
* - **`flush()`** gives every pending result one more attempt and rejects,
|
|
20
|
+
* naming the destinations, if any is still not written.
|
|
21
|
+
*/
|
|
22
|
+
import type { ILogger } from '@mcp-abap-adt/interfaces-utils';
|
|
23
|
+
/** A thrown value's class, for a log line: never its message. */
|
|
24
|
+
export declare function classLabel(error: unknown): string;
|
|
25
|
+
/** `T`: what one write takes — the result, with what the broker writes beside it. */
|
|
26
|
+
export declare class SessionWriter<T> {
|
|
27
|
+
private readonly write;
|
|
28
|
+
private readonly logger;
|
|
29
|
+
private readonly queues;
|
|
30
|
+
/**
|
|
31
|
+
* @param write Writes one result for a destination; throws when the store
|
|
32
|
+
* does not take it.
|
|
33
|
+
*/
|
|
34
|
+
constructor(write: (destination: string, result: T) => Promise<void>, logger: ILogger);
|
|
35
|
+
/**
|
|
36
|
+
* Take a new result for the destination and try to write it now. Never
|
|
37
|
+
* throws: resolves once this attempt has settled, written or left pending.
|
|
38
|
+
*/
|
|
39
|
+
submit(destination: string, result: T): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* One more attempt for every pending result; resolves when all are written,
|
|
42
|
+
* rejects naming the destinations still pending — each failure as its
|
|
43
|
+
* destination and its error's class only, never the store's message. The
|
|
44
|
+
* retries go on after a rejection.
|
|
45
|
+
*/
|
|
46
|
+
flush(): Promise<void>;
|
|
47
|
+
private queueOf;
|
|
48
|
+
/** Runs after the destination's previous attempt; never rejects. */
|
|
49
|
+
private attempt;
|
|
50
|
+
private writePending;
|
|
51
|
+
private schedule;
|
|
52
|
+
private cancelTimer;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=SessionWriter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionWriter.d.ts","sourceRoot":"","sources":["../src/SessionWriter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAgB9D,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKjD;AAED,qFAAqF;AACrF,qBAAa,aAAa,CAAC,CAAC;IAQxB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IARzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IAEtD;;;OAGG;gBAEgB,KAAK,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,EACxD,MAAM,EAAE,OAAO;IAGlC;;;OAGG;IACG,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAO3D;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB5B,OAAO,CAAC,OAAO;IASf,oEAAoE;IACpE,OAAO,CAAC,OAAO;YAMD,YAAY;IAgC1B,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,WAAW;CAMpB"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The broker's writes of a destination's session secret, retried until the
|
|
4
|
+
* store takes them.
|
|
5
|
+
*
|
|
6
|
+
* A store failure is the storage's or a broker bug, never the token's, so it
|
|
7
|
+
* does not fail the authentication: the result stays pending for its
|
|
8
|
+
* destination and the writer tries again on its own — it does not wait for the
|
|
9
|
+
* provider to be called again, since a request may be the process's last.
|
|
10
|
+
*
|
|
11
|
+
* - **One pending result per destination, the latest.** A newer result
|
|
12
|
+
* replaces the one waiting; an older one is never written after it.
|
|
13
|
+
* - **Attempts for one destination never overlap.** Each runs after the
|
|
14
|
+
* previous one has settled.
|
|
15
|
+
* - **A growing delay:** one second after the first failure, doubling, capped
|
|
16
|
+
* at one minute; reset once a write lands. The timer is `unref()`ed, so a
|
|
17
|
+
* pending write never keeps a process alive.
|
|
18
|
+
* - **Failures are logged by class name** — never a message: the store holds
|
|
19
|
+
* tokens, and its errors are foreign text.
|
|
20
|
+
* - **`flush()`** gives every pending result one more attempt and rejects,
|
|
21
|
+
* naming the destinations, if any is still not written.
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.SessionWriter = void 0;
|
|
25
|
+
exports.classLabel = classLabel;
|
|
26
|
+
const FIRST_DELAY_MS = 1_000;
|
|
27
|
+
const MAX_DELAY_MS = 60_000;
|
|
28
|
+
/** A thrown value's class, for a log line: never its message. */
|
|
29
|
+
function classLabel(error) {
|
|
30
|
+
if (error instanceof Error) {
|
|
31
|
+
return error.constructor?.name || 'Error';
|
|
32
|
+
}
|
|
33
|
+
return typeof error;
|
|
34
|
+
}
|
|
35
|
+
/** `T`: what one write takes — the result, with what the broker writes beside it. */
|
|
36
|
+
class SessionWriter {
|
|
37
|
+
write;
|
|
38
|
+
logger;
|
|
39
|
+
queues = new Map();
|
|
40
|
+
/**
|
|
41
|
+
* @param write Writes one result for a destination; throws when the store
|
|
42
|
+
* does not take it.
|
|
43
|
+
*/
|
|
44
|
+
constructor(write, logger) {
|
|
45
|
+
this.write = write;
|
|
46
|
+
this.logger = logger;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Take a new result for the destination and try to write it now. Never
|
|
50
|
+
* throws: resolves once this attempt has settled, written or left pending.
|
|
51
|
+
*/
|
|
52
|
+
async submit(destination, result) {
|
|
53
|
+
const queue = this.queueOf(destination);
|
|
54
|
+
queue.pending = result;
|
|
55
|
+
this.cancelTimer(queue);
|
|
56
|
+
await this.attempt(destination, queue);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* One more attempt for every pending result; resolves when all are written,
|
|
60
|
+
* rejects naming the destinations still pending — each failure as its
|
|
61
|
+
* destination and its error's class only, never the store's message. The
|
|
62
|
+
* retries go on after a rejection.
|
|
63
|
+
*/
|
|
64
|
+
async flush() {
|
|
65
|
+
const queues = [...this.queues];
|
|
66
|
+
await Promise.all(queues.map(([destination, queue]) => {
|
|
67
|
+
if (queue.pending === undefined)
|
|
68
|
+
return queue.tail;
|
|
69
|
+
this.cancelTimer(queue);
|
|
70
|
+
return this.attempt(destination, queue);
|
|
71
|
+
}));
|
|
72
|
+
const failed = queues.filter(([, queue]) => queue.pending !== undefined);
|
|
73
|
+
if (failed.length > 0) {
|
|
74
|
+
// The stores' own errors are not carried: their messages, causes and
|
|
75
|
+
// properties are foreign text that may quote what was being written.
|
|
76
|
+
throw new AggregateError(failed.map(([destination, queue]) => new Error(`"${destination}": ${classLabel(queue.lastError)}`)), `Session writes still failing for ${failed
|
|
77
|
+
.map(([destination]) => `"${destination}"`)
|
|
78
|
+
.join(', ')}; the broker keeps retrying them`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
queueOf(destination) {
|
|
82
|
+
let queue = this.queues.get(destination);
|
|
83
|
+
if (!queue) {
|
|
84
|
+
queue = { tail: Promise.resolve(), failures: 0 };
|
|
85
|
+
this.queues.set(destination, queue);
|
|
86
|
+
}
|
|
87
|
+
return queue;
|
|
88
|
+
}
|
|
89
|
+
/** Runs after the destination's previous attempt; never rejects. */
|
|
90
|
+
attempt(destination, queue) {
|
|
91
|
+
const run = queue.tail.then(() => this.writePending(destination, queue));
|
|
92
|
+
queue.tail = run;
|
|
93
|
+
return run;
|
|
94
|
+
}
|
|
95
|
+
async writePending(destination, queue) {
|
|
96
|
+
const result = queue.pending;
|
|
97
|
+
if (result === undefined)
|
|
98
|
+
return;
|
|
99
|
+
try {
|
|
100
|
+
await this.write(destination, result);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
queue.failures += 1;
|
|
104
|
+
queue.lastError = error;
|
|
105
|
+
const delay = Math.min(FIRST_DELAY_MS * 2 ** (queue.failures - 1), MAX_DELAY_MS);
|
|
106
|
+
this.logger.warn(`[AuthBroker] Session write for ${destination} failed; the token stands, the write is retried in ${delay} ms`, { error: classLabel(error), attempt: queue.failures });
|
|
107
|
+
this.schedule(destination, queue, delay);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
queue.failures = 0;
|
|
111
|
+
queue.lastError = undefined;
|
|
112
|
+
// A newer result that arrived meanwhile stays pending: its own attempt is
|
|
113
|
+
// queued after this one.
|
|
114
|
+
if (queue.pending === result) {
|
|
115
|
+
queue.pending = undefined;
|
|
116
|
+
this.cancelTimer(queue);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
schedule(destination, queue, delay) {
|
|
120
|
+
this.cancelTimer(queue);
|
|
121
|
+
const timer = setTimeout(() => {
|
|
122
|
+
queue.timer = undefined;
|
|
123
|
+
void this.attempt(destination, queue);
|
|
124
|
+
}, delay);
|
|
125
|
+
timer.unref();
|
|
126
|
+
queue.timer = timer;
|
|
127
|
+
}
|
|
128
|
+
cancelTimer(queue) {
|
|
129
|
+
if (queue.timer) {
|
|
130
|
+
clearTimeout(queue.timer);
|
|
131
|
+
queue.timer = undefined;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.SessionWriter = SessionWriter;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A secret is bound to the resource it was obtained for and to who issued it.
|
|
3
|
+
*
|
|
4
|
+
* The session store keeps two strings beside the secret — `issuedFor`, the
|
|
5
|
+
* resource, and `issuedBy`, the issuer and its client — and the broker uses a
|
|
6
|
+
* stored secret only when both equal what this destination's means give. The
|
|
7
|
+
* store keeps them as given; the broker canonicalises **both sides** before it
|
|
8
|
+
* compares, with the one function here (the broker's only, decided), so a store
|
|
9
|
+
* need not canonicalise and a legacy file's composed URI compares equal.
|
|
10
|
+
*
|
|
11
|
+
* The canonical URI:
|
|
12
|
+
* - scheme and host lower-cased (by `URL`, for http and https);
|
|
13
|
+
* - the port explicit — `443` for `https`, `80` for `http` when the URL has
|
|
14
|
+
* none; another scheme keeps the port it states;
|
|
15
|
+
* - the path without a trailing `/` (the root path is empty), its case kept;
|
|
16
|
+
* - one query parameter at most — `sap-client=<n>` for a resource,
|
|
17
|
+
* `client_id=<id>` for an issuer, none for a SAML ACS — parsed and
|
|
18
|
+
* re-encoded with `URLSearchParams`, so a value percent-encoded on one side
|
|
19
|
+
* and plain on the other compares equal;
|
|
20
|
+
* - no user info, no other parameter, no fragment.
|
|
21
|
+
*
|
|
22
|
+
* A URL that does not parse, or has no host, has no canonical form: the
|
|
23
|
+
* binding is then absent, never guessed.
|
|
24
|
+
*/
|
|
25
|
+
import type { IConfig, IConnectionConfig } from '@mcp-abap-adt/interfaces-auth-broker';
|
|
26
|
+
import type { IAuthorizationConfig } from '@mcp-abap-adt/interfaces-auth-sap';
|
|
27
|
+
/**
|
|
28
|
+
* The resource: `serviceUrl` with the SAP client as `sap-client` — the
|
|
29
|
+
* destination's `sapClient` when it states one, over a `sap-client` already in
|
|
30
|
+
* the URL. For a stored value, call it without `sapClient`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resourceUri(serviceUrl: unknown, sapClient?: string): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* The issuer and its client: the authorization server's base URL with
|
|
35
|
+
* `client_id`. For a stored value, call it without `clientId`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function issuerUri(issuerUrl: unknown, clientId?: string): string | undefined;
|
|
38
|
+
/** The ACS of the system that set SAML session cookies: origin and path. */
|
|
39
|
+
export declare function acsUri(acsUrl: unknown): string | undefined;
|
|
40
|
+
/** How a stored `issuedBy` is read: as an issuer with its client, or as an ACS. */
|
|
41
|
+
export type IssuerKind = 'issuer' | 'acs';
|
|
42
|
+
/** What this destination's means bind a secret to, canonical; absent where they cannot say. */
|
|
43
|
+
export interface Binding {
|
|
44
|
+
issuedFor?: string;
|
|
45
|
+
issuedBy?: string;
|
|
46
|
+
/** How the stored `issuedBy` is canonicalised before it is compared. */
|
|
47
|
+
issuerKind: IssuerKind;
|
|
48
|
+
/**
|
|
49
|
+
* The means state an issuer — for a `none` row, whether `issuedBy` is
|
|
50
|
+
* compared at all. True even when the stated URL does not parse: then it
|
|
51
|
+
* matches nothing.
|
|
52
|
+
*/
|
|
53
|
+
issuerStated: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The UAA grants (`authorization_code`, `client_credentials`, `passcode`) and
|
|
57
|
+
* `saml2_bearer`, whose token UAA issues to its client: the resource, and `uaaUrl` with `uaaClientId` — both required to match.
|
|
58
|
+
*/
|
|
59
|
+
export declare function uaaBinding(means: IConnectionConfig, client: IAuthorizationConfig | null): Binding;
|
|
60
|
+
/**
|
|
61
|
+
* The OIDC grants: the resource, and the issuer — `oidcIssuerUrl`, else
|
|
62
|
+
* `uaaUrl` — with `uaaClientId`; both required to match. Means that state
|
|
63
|
+
* neither issuer (explicit endpoints alone) bind no stored secret: one is then
|
|
64
|
+
* never seeded.
|
|
65
|
+
*/
|
|
66
|
+
export declare function oidcBinding(means: IConnectionConfig, client: IAuthorizationConfig | null): Binding;
|
|
67
|
+
/**
|
|
68
|
+
* `saml2_pure`: the resource, and the ACS of the system that sets the
|
|
69
|
+
* cookies — `samlAcsUrl`, origin and path; both required to match.
|
|
70
|
+
*/
|
|
71
|
+
export declare function samlPureBinding(means: IConnectionConfig): Binding;
|
|
72
|
+
/**
|
|
73
|
+
* The token API with a consumer's provider: what the broker handed
|
|
74
|
+
* that provider — the `serviceUrl` it resolved, with the SAP client, and for a
|
|
75
|
+
* factory the client it resolved (`uaaUrl` with `uaaClientId`). An instance is
|
|
76
|
+
* handed no client, so no issuer is claimed for what it obtains: `issuedBy` is
|
|
77
|
+
* then absent, and `getProvider` never seeds from that secret.
|
|
78
|
+
*/
|
|
79
|
+
export declare function consumerBinding(serviceUrl: string, sapClient: string | undefined, client: IAuthorizationConfig | null): Binding;
|
|
80
|
+
/**
|
|
81
|
+
* A `none` row: the resource always; the issuer only when the means state one
|
|
82
|
+
* — `oidcIssuerUrl` (with `uaaClientId` when stated) or the client (`uaaUrl`
|
|
83
|
+
* and `uaaClientId`) for `jwt`, `samlAcsUrl` for `saml`.
|
|
84
|
+
*/
|
|
85
|
+
export declare function handedOverBinding(authType: 'jwt' | 'saml', means: IConnectionConfig, client: IAuthorizationConfig | null): Binding;
|
|
86
|
+
/** The stored `issuedFor`, canonicalised, equals the destination's. */
|
|
87
|
+
export declare function sameResource(stored: IConfig | null, binding: Binding): boolean;
|
|
88
|
+
/** The stored `issuedBy`, canonicalised as the destination's kind, equals it. */
|
|
89
|
+
export declare function sameIssuer(stored: IConfig | null, binding: Binding): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* An obtained secret is this destination's only when **both** stored values
|
|
92
|
+
* equal the computed ones; either absent, on either side, is not a match.
|
|
93
|
+
*/
|
|
94
|
+
export declare function boundHere(stored: IConfig | null, binding: Binding): boolean;
|
|
95
|
+
//# sourceMappingURL=binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,iBAAiB,EAClB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AA6C9E;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,OAAO,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,GAAG,SAAS,CAEpB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,SAAS,EAAE,OAAO,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,GAAG,SAAS,CAEpB;AAED,4EAA4E;AAC5E,wBAAgB,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE1D;AAED,mFAAmF;AACnF,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE1C,+FAA+F;AAC/F,MAAM,WAAW,OAAO;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,UAAU,EAAE,UAAU,CAAC;IACvB;;;;OAIG;IACH,YAAY,EAAE,OAAO,CAAC;CACvB;AAUD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAClC,OAAO,CAUT;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAClC,OAAO,CAWT;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAOjE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAClC,OAAO,CAET;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,KAAK,GAAG,MAAM,EACxB,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAClC,OAAO,CA4BT;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAC1B,MAAM,EAAE,OAAO,GAAG,IAAI,EACtB,OAAO,EAAE,OAAO,GACf,OAAO,CAKT;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAI5E;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAE3E"}
|
package/dist/binding.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A secret is bound to the resource it was obtained for and to who issued it.
|
|
4
|
+
*
|
|
5
|
+
* The session store keeps two strings beside the secret — `issuedFor`, the
|
|
6
|
+
* resource, and `issuedBy`, the issuer and its client — and the broker uses a
|
|
7
|
+
* stored secret only when both equal what this destination's means give. The
|
|
8
|
+
* store keeps them as given; the broker canonicalises **both sides** before it
|
|
9
|
+
* compares, with the one function here (the broker's only, decided), so a store
|
|
10
|
+
* need not canonicalise and a legacy file's composed URI compares equal.
|
|
11
|
+
*
|
|
12
|
+
* The canonical URI:
|
|
13
|
+
* - scheme and host lower-cased (by `URL`, for http and https);
|
|
14
|
+
* - the port explicit — `443` for `https`, `80` for `http` when the URL has
|
|
15
|
+
* none; another scheme keeps the port it states;
|
|
16
|
+
* - the path without a trailing `/` (the root path is empty), its case kept;
|
|
17
|
+
* - one query parameter at most — `sap-client=<n>` for a resource,
|
|
18
|
+
* `client_id=<id>` for an issuer, none for a SAML ACS — parsed and
|
|
19
|
+
* re-encoded with `URLSearchParams`, so a value percent-encoded on one side
|
|
20
|
+
* and plain on the other compares equal;
|
|
21
|
+
* - no user info, no other parameter, no fragment.
|
|
22
|
+
*
|
|
23
|
+
* A URL that does not parse, or has no host, has no canonical form: the
|
|
24
|
+
* binding is then absent, never guessed.
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.resourceUri = resourceUri;
|
|
28
|
+
exports.issuerUri = issuerUri;
|
|
29
|
+
exports.acsUri = acsUri;
|
|
30
|
+
exports.uaaBinding = uaaBinding;
|
|
31
|
+
exports.oidcBinding = oidcBinding;
|
|
32
|
+
exports.samlPureBinding = samlPureBinding;
|
|
33
|
+
exports.consumerBinding = consumerBinding;
|
|
34
|
+
exports.handedOverBinding = handedOverBinding;
|
|
35
|
+
exports.sameResource = sameResource;
|
|
36
|
+
exports.sameIssuer = sameIssuer;
|
|
37
|
+
exports.boundHere = boundHere;
|
|
38
|
+
/** The default port a URL without one is taken to mean. */
|
|
39
|
+
const DEFAULT_PORTS = {
|
|
40
|
+
'https:': '443',
|
|
41
|
+
'http:': '80',
|
|
42
|
+
};
|
|
43
|
+
function present(value) {
|
|
44
|
+
return typeof value === 'string' && value !== '';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The canonical URI of `raw`, keeping only the query parameter `param` — its
|
|
48
|
+
* value `override` when given, else the one `raw` carries.
|
|
49
|
+
*/
|
|
50
|
+
function canonical(raw, param, override) {
|
|
51
|
+
if (!present(raw))
|
|
52
|
+
return undefined;
|
|
53
|
+
let url;
|
|
54
|
+
try {
|
|
55
|
+
url = new URL(raw);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
// `URL` lower-cases the scheme, and the host of an http(s) URL. Another
|
|
61
|
+
// scheme's host is kept as written: no form beyond http(s) is defined yet.
|
|
62
|
+
const host = url.hostname;
|
|
63
|
+
if (host === '')
|
|
64
|
+
return undefined;
|
|
65
|
+
const scheme = url.protocol;
|
|
66
|
+
const port = url.port || DEFAULT_PORTS[scheme] || '';
|
|
67
|
+
const path = url.pathname.replace(/\/+$/, '');
|
|
68
|
+
let query = '';
|
|
69
|
+
if (param) {
|
|
70
|
+
const value = present(override) ? override : url.searchParams.get(param);
|
|
71
|
+
if (present(value)) {
|
|
72
|
+
query = `?${new URLSearchParams([[param, value]]).toString()}`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return `${scheme}//${host}${port ? `:${port}` : ''}${path}${query}`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The resource: `serviceUrl` with the SAP client as `sap-client` — the
|
|
79
|
+
* destination's `sapClient` when it states one, over a `sap-client` already in
|
|
80
|
+
* the URL. For a stored value, call it without `sapClient`.
|
|
81
|
+
*/
|
|
82
|
+
function resourceUri(serviceUrl, sapClient) {
|
|
83
|
+
return canonical(serviceUrl, 'sap-client', sapClient);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The issuer and its client: the authorization server's base URL with
|
|
87
|
+
* `client_id`. For a stored value, call it without `clientId`.
|
|
88
|
+
*/
|
|
89
|
+
function issuerUri(issuerUrl, clientId) {
|
|
90
|
+
return canonical(issuerUrl, 'client_id', clientId);
|
|
91
|
+
}
|
|
92
|
+
/** The ACS of the system that set SAML session cookies: origin and path. */
|
|
93
|
+
function acsUri(acsUrl) {
|
|
94
|
+
return canonical(acsUrl, undefined);
|
|
95
|
+
}
|
|
96
|
+
/** The resource the means name: `serviceUrl` with `sapClient`. */
|
|
97
|
+
function resourceOf(means) {
|
|
98
|
+
return resourceUri(means.serviceUrl, present(means.sapClient) ? means.sapClient : undefined);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The UAA grants (`authorization_code`, `client_credentials`, `passcode`) and
|
|
102
|
+
* `saml2_bearer`, whose token UAA issues to its client: the resource, and `uaaUrl` with `uaaClientId` — both required to match.
|
|
103
|
+
*/
|
|
104
|
+
function uaaBinding(means, client) {
|
|
105
|
+
const stated = present(client?.uaaUrl) && present(client?.uaaClientId);
|
|
106
|
+
return {
|
|
107
|
+
issuedFor: resourceOf(means),
|
|
108
|
+
issuedBy: stated
|
|
109
|
+
? issuerUri(client?.uaaUrl, client?.uaaClientId)
|
|
110
|
+
: undefined,
|
|
111
|
+
issuerKind: 'issuer',
|
|
112
|
+
issuerStated: stated,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The OIDC grants: the resource, and the issuer — `oidcIssuerUrl`, else
|
|
117
|
+
* `uaaUrl` — with `uaaClientId`; both required to match. Means that state
|
|
118
|
+
* neither issuer (explicit endpoints alone) bind no stored secret: one is then
|
|
119
|
+
* never seeded.
|
|
120
|
+
*/
|
|
121
|
+
function oidcBinding(means, client) {
|
|
122
|
+
const issuer = present(means.oidcIssuerUrl)
|
|
123
|
+
? means.oidcIssuerUrl
|
|
124
|
+
: client?.uaaUrl;
|
|
125
|
+
const stated = present(issuer) && present(client?.uaaClientId);
|
|
126
|
+
return {
|
|
127
|
+
issuedFor: resourceOf(means),
|
|
128
|
+
issuedBy: stated ? issuerUri(issuer, client?.uaaClientId) : undefined,
|
|
129
|
+
issuerKind: 'issuer',
|
|
130
|
+
issuerStated: stated,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* `saml2_pure`: the resource, and the ACS of the system that sets the
|
|
135
|
+
* cookies — `samlAcsUrl`, origin and path; both required to match.
|
|
136
|
+
*/
|
|
137
|
+
function samlPureBinding(means) {
|
|
138
|
+
return {
|
|
139
|
+
issuedFor: resourceOf(means),
|
|
140
|
+
issuedBy: acsUri(means.samlAcsUrl),
|
|
141
|
+
issuerKind: 'acs',
|
|
142
|
+
issuerStated: present(means.samlAcsUrl),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The token API with a consumer's provider: what the broker handed
|
|
147
|
+
* that provider — the `serviceUrl` it resolved, with the SAP client, and for a
|
|
148
|
+
* factory the client it resolved (`uaaUrl` with `uaaClientId`). An instance is
|
|
149
|
+
* handed no client, so no issuer is claimed for what it obtains: `issuedBy` is
|
|
150
|
+
* then absent, and `getProvider` never seeds from that secret.
|
|
151
|
+
*/
|
|
152
|
+
function consumerBinding(serviceUrl, sapClient, client) {
|
|
153
|
+
return uaaBinding({ serviceUrl, sapClient }, client);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* A `none` row: the resource always; the issuer only when the means state one
|
|
157
|
+
* — `oidcIssuerUrl` (with `uaaClientId` when stated) or the client (`uaaUrl`
|
|
158
|
+
* and `uaaClientId`) for `jwt`, `samlAcsUrl` for `saml`.
|
|
159
|
+
*/
|
|
160
|
+
function handedOverBinding(authType, means, client) {
|
|
161
|
+
const issuedFor = resourceOf(means);
|
|
162
|
+
if (authType === 'saml') {
|
|
163
|
+
return {
|
|
164
|
+
issuedFor,
|
|
165
|
+
issuedBy: acsUri(means.samlAcsUrl),
|
|
166
|
+
issuerKind: 'acs',
|
|
167
|
+
issuerStated: present(means.samlAcsUrl),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const clientId = present(client?.uaaClientId)
|
|
171
|
+
? client.uaaClientId
|
|
172
|
+
: undefined;
|
|
173
|
+
if (present(means.oidcIssuerUrl)) {
|
|
174
|
+
return {
|
|
175
|
+
issuedFor,
|
|
176
|
+
issuedBy: issuerUri(means.oidcIssuerUrl, clientId),
|
|
177
|
+
issuerKind: 'issuer',
|
|
178
|
+
issuerStated: true,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const stated = present(client?.uaaUrl) && clientId !== undefined;
|
|
182
|
+
return {
|
|
183
|
+
issuedFor,
|
|
184
|
+
issuedBy: stated ? issuerUri(client?.uaaUrl, clientId) : undefined,
|
|
185
|
+
issuerKind: 'issuer',
|
|
186
|
+
issuerStated: stated,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/** The stored `issuedFor`, canonicalised, equals the destination's. */
|
|
190
|
+
function sameResource(stored, binding) {
|
|
191
|
+
return (binding.issuedFor !== undefined &&
|
|
192
|
+
resourceUri(stored?.issuedFor) === binding.issuedFor);
|
|
193
|
+
}
|
|
194
|
+
/** The stored `issuedBy`, canonicalised as the destination's kind, equals it. */
|
|
195
|
+
function sameIssuer(stored, binding) {
|
|
196
|
+
if (binding.issuedBy === undefined)
|
|
197
|
+
return false;
|
|
198
|
+
const read = binding.issuerKind === 'acs' ? acsUri : issuerUri;
|
|
199
|
+
return read(stored?.issuedBy) === binding.issuedBy;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* An obtained secret is this destination's only when **both** stored values
|
|
203
|
+
* equal the computed ones; either absent, on either side, is not a match.
|
|
204
|
+
*/
|
|
205
|
+
function boundHere(stored, binding) {
|
|
206
|
+
return sameResource(stored, binding) && sameIssuer(stored, binding);
|
|
207
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a destination's means bind its secret to: the one
|
|
3
|
+
* place the broker chooses which binding a destination has. `getProvider`
|
|
4
|
+
* builds each provider with it, and `persist` writes what it gives beside every
|
|
5
|
+
* secret; `bindingOf` hands the same to a consumer.
|
|
6
|
+
*/
|
|
7
|
+
import type { DestinationGrant, IConnectionConfig } from '@mcp-abap-adt/interfaces-auth-broker';
|
|
8
|
+
import type { IAuthorizationConfig } from '@mcp-abap-adt/interfaces-auth-sap';
|
|
9
|
+
import { type Binding } from './binding';
|
|
10
|
+
/**
|
|
11
|
+
* The binding of a `jwt` or `saml` destination with the grant it states: the
|
|
12
|
+
* UAA grants and `saml2_bearer` by `uaaUrl` and the client, the OIDC grants by
|
|
13
|
+
* the issuer and the client, `saml2_pure` by its ACS, and `none` by what the
|
|
14
|
+
* means state.
|
|
15
|
+
*/
|
|
16
|
+
export declare function destinationBinding(authType: 'jwt' | 'saml', grant: DestinationGrant, means: IConnectionConfig, client: IAuthorizationConfig | null): Binding;
|
|
17
|
+
/** The two strings a session store keeps beside a secret; absent where the means cannot say. */
|
|
18
|
+
export interface SecretBinding {
|
|
19
|
+
issuedFor?: string;
|
|
20
|
+
issuedBy?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The binding the broker writes beside a secret for a destination with these
|
|
24
|
+
* means and this client — canonical, exactly what `getProvider` compares a
|
|
25
|
+
* stored secret against. For a consumer that hands over a credential the
|
|
26
|
+
* broker cannot obtain (a `none` destination: a token or cookies it was
|
|
27
|
+
* given) and writes it to the session store itself: written with this binding,
|
|
28
|
+
* `getProvider` presents it; written with any other, it refuses.
|
|
29
|
+
*
|
|
30
|
+
* `means` are the key store's (`authType`, `grantType`, `serviceUrl`,
|
|
31
|
+
* `sapClient`, the issuer or ACS); `client` is the key store's client, when the
|
|
32
|
+
* destination has one. A destination that states no `jwt` / `saml` type or no
|
|
33
|
+
* grant binds nothing: `{}`. Never logs, never throws.
|
|
34
|
+
*/
|
|
35
|
+
export declare function bindingOf(means: IConnectionConfig, client?: IAuthorizationConfig | null): SecretBinding;
|
|
36
|
+
//# sourceMappingURL=bindingOf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindingOf.d.ts","sourceRoot":"","sources":["../src/bindingOf.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EACL,KAAK,OAAO,EAKb,MAAM,WAAW,CAAC;AAGnB;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,KAAK,GAAG,MAAM,EACxB,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAClC,OAAO,CAKT;AAED,gGAAgG;AAChG,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,iBAAiB,EACxB,MAAM,GAAE,oBAAoB,GAAG,IAAW,GACzC,aAAa,CAcf"}
|
|
@@ -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
|
+
}
|