@mcp-abap-adt/auth-providers 1.2.0 ā 2.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 +97 -0
- package/README.md +373 -62
- package/bin/auth-authorization-code.ts +5 -3
- package/dist/__tests__/helpers/netHelpers.d.ts +16 -0
- package/dist/__tests__/helpers/netHelpers.d.ts.map +1 -1
- package/dist/__tests__/helpers/netHelpers.js +29 -0
- package/dist/auth/announce.d.ts +11 -0
- package/dist/auth/announce.d.ts.map +1 -0
- package/dist/auth/announce.js +19 -0
- package/dist/auth/browserAuth.d.ts +21 -14
- package/dist/auth/browserAuth.d.ts.map +1 -1
- package/dist/auth/browserAuth.js +25 -111
- package/dist/auth/callbackServer.d.ts +6 -0
- package/dist/auth/callbackServer.d.ts.map +1 -1
- package/dist/auth/callbackServer.js +31 -21
- package/dist/auth/oidcBrowserAuth.d.ts +2 -9
- package/dist/auth/oidcBrowserAuth.d.ts.map +1 -1
- package/dist/auth/oidcBrowserAuth.js +16 -125
- package/dist/auth/saml2Auth.d.ts +2 -9
- package/dist/auth/saml2Auth.d.ts.map +1 -1
- package/dist/auth/saml2Auth.js +10 -130
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -1
- package/dist/providers/AuthorizationCodeProvider.d.ts +7 -3
- package/dist/providers/AuthorizationCodeProvider.d.ts.map +1 -1
- package/dist/providers/AuthorizationCodeProvider.js +51 -40
- package/dist/providers/DeviceFlowProvider.d.ts +2 -1
- package/dist/providers/DeviceFlowProvider.d.ts.map +1 -1
- package/dist/providers/DeviceFlowProvider.js +13 -10
- package/dist/providers/OidcBrowserProvider.d.ts +4 -7
- package/dist/providers/OidcBrowserProvider.d.ts.map +1 -1
- package/dist/providers/OidcBrowserProvider.js +54 -57
- package/dist/providers/OidcDeviceFlowProvider.d.ts.map +1 -1
- package/dist/providers/OidcDeviceFlowProvider.js +9 -7
- package/dist/providers/Saml2BearerProvider.d.ts +2 -2
- package/dist/providers/Saml2BearerProvider.d.ts.map +1 -1
- package/dist/providers/Saml2BearerProvider.js +3 -0
- package/dist/providers/Saml2PureProvider.d.ts +2 -2
- package/dist/providers/Saml2PureProvider.d.ts.map +1 -1
- package/dist/providers/Saml2PureProvider.js +3 -0
- package/dist/providers/saml2Utils.d.ts +12 -12
- package/dist/providers/saml2Utils.d.ts.map +1 -1
- package/dist/providers/saml2Utils.js +46 -26
- package/dist/strategies/BrowserCallbackStrategy.d.ts +64 -0
- package/dist/strategies/BrowserCallbackStrategy.d.ts.map +1 -0
- package/dist/strategies/BrowserCallbackStrategy.js +210 -0
- package/dist/strategies/asOidcResult.d.ts +11 -0
- package/dist/strategies/asOidcResult.d.ts.map +1 -0
- package/dist/strategies/asOidcResult.js +30 -0
- package/dist/strategies/codeStrategies.d.ts +22 -0
- package/dist/strategies/codeStrategies.d.ts.map +1 -0
- package/dist/strategies/codeStrategies.js +39 -0
- package/dist/strategies/index.d.ts +8 -0
- package/dist/strategies/index.d.ts.map +1 -0
- package/dist/strategies/index.js +18 -0
- package/dist/strategies/manualStrategies.d.ts +20 -0
- package/dist/strategies/manualStrategies.d.ts.map +1 -0
- package/dist/strategies/manualStrategies.js +77 -0
- package/package.json +2 -2
- package/dist/auth/manualInput.d.ts +0 -5
- package/dist/auth/manualInput.d.ts.map +0 -1
- package/dist/auth/manualInput.js +0 -19
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.DeviceFlowProvider = void 0;
|
|
10
10
|
const interfaces_1 = require("@mcp-abap-adt/interfaces");
|
|
11
|
+
const announce_1 = require("../auth/announce");
|
|
11
12
|
const deviceFlowAuth_1 = require("../auth/deviceFlowAuth");
|
|
12
13
|
const tokenRefresher_1 = require("../auth/tokenRefresher");
|
|
13
14
|
const BaseTokenProvider_1 = require("./BaseTokenProvider");
|
|
@@ -22,6 +23,7 @@ class DeviceFlowProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
22
23
|
constructor(config) {
|
|
23
24
|
super();
|
|
24
25
|
this.config = config;
|
|
26
|
+
this.logger = config.logger;
|
|
25
27
|
if (config.accessToken) {
|
|
26
28
|
this.authorizationToken = config.accessToken;
|
|
27
29
|
this.expiresAt = this.parseExpirationFromJWT(config.accessToken);
|
|
@@ -38,19 +40,20 @@ class DeviceFlowProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
38
40
|
}
|
|
39
41
|
async performLogin() {
|
|
40
42
|
// Initiate device flow
|
|
41
|
-
const deviceFlow = await (0, deviceFlowAuth_1.initiateDeviceFlow)(this.config.uaaUrl, this.config.clientId, this.config.scope,
|
|
42
|
-
// Display user code and verification URI
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
const deviceFlow = await (0, deviceFlowAuth_1.initiateDeviceFlow)(this.config.uaaUrl, this.config.clientId, this.config.scope, this.logger);
|
|
44
|
+
// Display user code and verification URI. This is a prompt the user must
|
|
45
|
+
// see to complete the flow, not a log line ā it goes to the logger when
|
|
46
|
+
// there is one and to stderr otherwise, never to stdout.
|
|
47
|
+
const announce = (0, announce_1.announcer)(this.logger);
|
|
48
|
+
announce('Device Flow Authorization');
|
|
49
|
+
announce(`Go to: ${deviceFlow.verificationUri}`);
|
|
46
50
|
if (deviceFlow.verificationUriComplete) {
|
|
47
|
-
|
|
51
|
+
announce(`Or use complete URL: ${deviceFlow.verificationUriComplete}`);
|
|
48
52
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
console.log('ā³ Waiting for authorization...\n');
|
|
53
|
+
announce(`Enter code: ${deviceFlow.userCode}`);
|
|
54
|
+
announce('Waiting for authorization...');
|
|
52
55
|
// Poll for tokens
|
|
53
|
-
const result = await (0, deviceFlowAuth_1.pollForDeviceTokens)(this.config.uaaUrl, this.config.clientId, this.config.clientSecret, deviceFlow.deviceCode, deviceFlow.interval,
|
|
56
|
+
const result = await (0, deviceFlowAuth_1.pollForDeviceTokens)(this.config.uaaUrl, this.config.clientId, this.config.clientSecret, deviceFlow.deviceCode, deviceFlow.interval, this.logger);
|
|
54
57
|
const expiresIn = this.calculateExpiresIn(result.accessToken);
|
|
55
58
|
return {
|
|
56
59
|
authorizationToken: result.accessToken,
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OIDC Authorization Code Provider (with PKCE)
|
|
3
3
|
*/
|
|
4
|
-
import type { ILogger, ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces';
|
|
4
|
+
import type { IAuthorizationStrategy, ILogger, ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces';
|
|
5
|
+
import type { OidcCallbackResult } from '../auth/oidcBrowserAuth';
|
|
5
6
|
import { BaseTokenProvider } from './BaseTokenProvider';
|
|
6
7
|
export interface OidcBrowserProviderConfig {
|
|
7
8
|
issuerUrl?: string;
|
|
8
9
|
clientId: string;
|
|
9
10
|
clientSecret?: string;
|
|
10
11
|
scopes?: string[];
|
|
11
|
-
browser?: string;
|
|
12
|
-
redirectPort?: number;
|
|
13
|
-
redirectUri?: string;
|
|
14
|
-
authorizationCode?: string;
|
|
15
|
-
authorizationCodeProvider?: () => Promise<string>;
|
|
16
12
|
authorizationEndpoint?: string;
|
|
17
13
|
tokenEndpoint?: string;
|
|
14
|
+
/** How the login is conducted. Omitted means a browser callback on the default port. */
|
|
15
|
+
authorization?: IAuthorizationStrategy<OidcCallbackResult>;
|
|
18
16
|
accessToken?: string;
|
|
19
17
|
refreshToken?: string;
|
|
20
18
|
logger?: ILogger;
|
|
@@ -25,6 +23,5 @@ export declare class OidcBrowserProvider extends BaseTokenProvider {
|
|
|
25
23
|
protected getAuthType(): OAuth2GrantType;
|
|
26
24
|
protected performLogin(): Promise<ITokenResult>;
|
|
27
25
|
protected performRefresh(): Promise<ITokenResult>;
|
|
28
|
-
private resolveAuthorizationCode;
|
|
29
26
|
}
|
|
30
27
|
//# sourceMappingURL=OidcBrowserProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OidcBrowserProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OidcBrowserProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"OidcBrowserProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OidcBrowserProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAKlE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,aAAa,CAAC,EAAE,sBAAsB,CAAC,kBAAkB,CAAC,CAAC;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,mBAAoB,SAAQ,iBAAiB;IACxD,OAAO,CAAC,MAAM,CAA4B;gBAE9B,MAAM,EAAE,yBAAyB;IAc7C,SAAS,CAAC,WAAW,IAAI,eAAe;cAIxB,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC;cAyFrC,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC;CAsCxD"}
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.OidcBrowserProvider = void 0;
|
|
7
7
|
const interfaces_1 = require("@mcp-abap-adt/interfaces");
|
|
8
|
-
const oidcBrowserAuth_1 = require("../auth/oidcBrowserAuth");
|
|
9
8
|
const oidcDiscovery_1 = require("../auth/oidcDiscovery");
|
|
10
9
|
const oidcPkce_1 = require("../auth/oidcPkce");
|
|
11
10
|
const oidcToken_1 = require("../auth/oidcToken");
|
|
11
|
+
const strategies_1 = require("../strategies");
|
|
12
12
|
const BaseTokenProvider_1 = require("./BaseTokenProvider");
|
|
13
13
|
class OidcBrowserProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
14
14
|
config;
|
|
@@ -28,50 +28,62 @@ class OidcBrowserProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
28
28
|
return interfaces_1.AUTH_TYPE_AUTHORIZATION_CODE_PKCE;
|
|
29
29
|
}
|
|
30
30
|
async performLogin() {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
// One memoised discovery per login, started on first use rather than up
|
|
32
|
+
// front: a strategy that already holds a code must not drag in a request ā
|
|
33
|
+
// nor the `issuerUrl` requirement that comes with it.
|
|
34
34
|
let discovery = null;
|
|
35
|
-
|
|
36
|
-
if (!
|
|
37
|
-
|
|
35
|
+
const discover = () => {
|
|
36
|
+
if (!discovery) {
|
|
37
|
+
if (!this.config.issuerUrl) {
|
|
38
|
+
throw new Error('OIDC issuerUrl is required when discovery is used');
|
|
39
|
+
}
|
|
40
|
+
discovery = (0, oidcDiscovery_1.discoverOidc)(this.config.issuerUrl, this.logger);
|
|
38
41
|
}
|
|
39
|
-
discovery
|
|
42
|
+
return discovery;
|
|
43
|
+
};
|
|
44
|
+
const verifier = (0, oidcPkce_1.generatePkceVerifier)();
|
|
45
|
+
const challenge = (0, oidcPkce_1.generatePkceChallenge)(verifier);
|
|
46
|
+
const scope = (this.config.scopes && this.config.scopes.length > 0
|
|
47
|
+
? this.config.scopes
|
|
48
|
+
: ['openid', 'profile', 'email']).join(' ');
|
|
49
|
+
const request = {
|
|
50
|
+
logger: this.logger,
|
|
51
|
+
buildAuthorizationUrl: async (redirectUri) => {
|
|
52
|
+
const endpoint = this.config.authorizationEndpoint ??
|
|
53
|
+
(await discover()).authorization_endpoint;
|
|
54
|
+
if (!endpoint) {
|
|
55
|
+
throw new Error('OIDC authorization endpoint is required (authorizationEndpoint or discovery)');
|
|
56
|
+
}
|
|
57
|
+
const params = new URLSearchParams();
|
|
58
|
+
params.append('response_type', 'code');
|
|
59
|
+
params.append('client_id', this.config.clientId);
|
|
60
|
+
params.append('redirect_uri', redirectUri);
|
|
61
|
+
params.append('scope', scope);
|
|
62
|
+
params.append('code_challenge', challenge);
|
|
63
|
+
params.append('code_challenge_method', 'S256');
|
|
64
|
+
return `${endpoint}?${params.toString()}`;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
const supplied = this.config.authorization;
|
|
68
|
+
const strategy = supplied ?? (0, strategies_1.oidcCallbackStrategy)();
|
|
69
|
+
let outcome;
|
|
70
|
+
try {
|
|
71
|
+
outcome = await strategy.authorize(request);
|
|
40
72
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
73
|
+
finally {
|
|
74
|
+
if (!supplied) {
|
|
75
|
+
await strategy.dispose?.().catch((error) => {
|
|
76
|
+
this.logger?.warn('[OidcBrowserProvider] dispose failed', {
|
|
77
|
+
error: error instanceof Error ? error.message : String(error),
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
47
81
|
}
|
|
82
|
+
const tokenEndpoint = this.config.tokenEndpoint ?? (await discover()).token_endpoint;
|
|
48
83
|
if (!tokenEndpoint) {
|
|
49
84
|
throw new Error('OIDC token endpoint is required (tokenEndpoint or discovery)');
|
|
50
85
|
}
|
|
51
|
-
const
|
|
52
|
-
const redirectUri = this.config.redirectUri || `http://localhost:${redirectPort}/callback`;
|
|
53
|
-
if (needsAuthorizationEndpoint && this.config.redirectUri) {
|
|
54
|
-
if (!redirectUri.startsWith('http://localhost:')) {
|
|
55
|
-
throw new Error('OIDC redirectUri must be localhost for browser callback flow');
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const scope = (this.config.scopes && this.config.scopes.length > 0
|
|
59
|
-
? this.config.scopes
|
|
60
|
-
: ['openid', 'profile', 'email']).join(' ');
|
|
61
|
-
const verifier = (0, oidcPkce_1.generatePkceVerifier)();
|
|
62
|
-
const challenge = (0, oidcPkce_1.generatePkceChallenge)(verifier);
|
|
63
|
-
const params = new URLSearchParams();
|
|
64
|
-
params.append('response_type', 'code');
|
|
65
|
-
params.append('client_id', this.config.clientId);
|
|
66
|
-
params.append('redirect_uri', redirectUri);
|
|
67
|
-
params.append('scope', scope);
|
|
68
|
-
params.append('code_challenge', challenge);
|
|
69
|
-
params.append('code_challenge_method', 'S256');
|
|
70
|
-
const authorizationUrl = needsAuthorizationEndpoint
|
|
71
|
-
? `${authorizationEndpoint}?${params.toString()}`
|
|
72
|
-
: undefined;
|
|
73
|
-
const code = await this.resolveAuthorizationCode(authorizationUrl, redirectPort);
|
|
74
|
-
const tokens = await (0, oidcToken_1.exchangeAuthorizationCode)(tokenEndpoint, this.config.clientId, this.config.clientSecret, code, redirectUri, verifier, this.logger);
|
|
86
|
+
const tokens = await (0, oidcToken_1.exchangeAuthorizationCode)(tokenEndpoint, this.config.clientId, this.config.clientSecret, outcome.payload.code, outcome.redirectUri, verifier, this.logger);
|
|
75
87
|
return {
|
|
76
88
|
authorizationToken: tokens.accessToken,
|
|
77
89
|
refreshToken: tokens.refreshToken,
|
|
@@ -91,7 +103,10 @@ class OidcBrowserProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
91
103
|
}
|
|
92
104
|
discovery = await (0, oidcDiscovery_1.discoverOidc)(this.config.issuerUrl, this.logger);
|
|
93
105
|
}
|
|
94
|
-
|
|
106
|
+
// `??`, matching the `=== undefined` gate above and the login path: `||`
|
|
107
|
+
// treated a configured-but-empty endpoint as absent while the gate had
|
|
108
|
+
// already decided it was present, so the two disagreed about the same value.
|
|
109
|
+
const tokenEndpoint = this.config.tokenEndpoint ?? discovery?.token_endpoint;
|
|
95
110
|
if (!tokenEndpoint) {
|
|
96
111
|
throw new Error('OIDC token endpoint is required (tokenEndpoint or discovery)');
|
|
97
112
|
}
|
|
@@ -104,23 +119,5 @@ class OidcBrowserProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
104
119
|
tokenType: 'jwt',
|
|
105
120
|
};
|
|
106
121
|
}
|
|
107
|
-
async resolveAuthorizationCode(authorizationUrl, redirectPort) {
|
|
108
|
-
if (this.config.authorizationCode) {
|
|
109
|
-
return this.config.authorizationCode;
|
|
110
|
-
}
|
|
111
|
-
if (this.config.authorizationCodeProvider) {
|
|
112
|
-
const code = await this.config.authorizationCodeProvider();
|
|
113
|
-
if (!code) {
|
|
114
|
-
throw new Error('Authorization code provider returned empty value');
|
|
115
|
-
}
|
|
116
|
-
return code;
|
|
117
|
-
}
|
|
118
|
-
if (!authorizationUrl) {
|
|
119
|
-
throw new Error('OIDC authorization URL is required when using browser flow');
|
|
120
|
-
}
|
|
121
|
-
const browser = this.config.browser || 'auto';
|
|
122
|
-
const { code } = await (0, oidcBrowserAuth_1.startOidcBrowserAuth)(authorizationUrl, browser, this.logger, redirectPort);
|
|
123
|
-
return code;
|
|
124
|
-
}
|
|
125
122
|
}
|
|
126
123
|
exports.OidcBrowserProvider = OidcBrowserProvider;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OidcDeviceFlowProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OidcDeviceFlowProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"OidcDeviceFlowProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OidcDeviceFlowProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AASlC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,4BAA4B;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,sBAAuB,SAAQ,iBAAiB;IAC3D,OAAO,CAAC,MAAM,CAA+B;gBAEjC,MAAM,EAAE,4BAA4B;IAchD,SAAS,CAAC,WAAW,IAAI,eAAe;cAIxB,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC;cAwErC,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC;CAqCxD"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.OidcDeviceFlowProvider = void 0;
|
|
7
7
|
const interfaces_1 = require("@mcp-abap-adt/interfaces");
|
|
8
|
+
const announce_1 = require("../auth/announce");
|
|
8
9
|
const oidcDiscovery_1 = require("../auth/oidcDiscovery");
|
|
9
10
|
const oidcToken_1 = require("../auth/oidcToken");
|
|
10
11
|
const BaseTokenProvider_1 = require("./BaseTokenProvider");
|
|
@@ -50,15 +51,16 @@ class OidcDeviceFlowProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
50
51
|
}
|
|
51
52
|
const scope = this.config.scopes?.join(' ');
|
|
52
53
|
const deviceFlow = await (0, oidcToken_1.initiateDeviceAuthorization)(deviceAuthorizationEndpoint, this.config.clientId, scope, this.logger);
|
|
53
|
-
// Manual user guidance
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
// Manual user guidance. This is a prompt the user must see to complete
|
|
55
|
+
// the flow, not a log line ā it goes to the logger when there is one and
|
|
56
|
+
// to stderr otherwise, never to stdout.
|
|
57
|
+
const announce = (0, announce_1.announcer)(this.logger);
|
|
58
|
+
announce('OIDC device authorization');
|
|
59
|
+
announce(`Go to: ${deviceFlow.verificationUri}`);
|
|
57
60
|
if (deviceFlow.verificationUriComplete) {
|
|
58
|
-
|
|
61
|
+
announce(`Or use: ${deviceFlow.verificationUriComplete}`);
|
|
59
62
|
}
|
|
60
|
-
|
|
61
|
-
console.log('');
|
|
63
|
+
announce(`Enter code: ${deviceFlow.userCode}`);
|
|
62
64
|
const tokens = await (0, oidcToken_1.pollDeviceTokens)(tokenEndpoint, this.config.clientId, this.config.clientSecret, deviceFlow.deviceCode, deviceFlow.interval || 5, this.logger);
|
|
63
65
|
return {
|
|
64
66
|
authorizationToken: tokens.accessToken,
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { ILogger, ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces';
|
|
7
7
|
import { BaseTokenProvider } from './BaseTokenProvider';
|
|
8
|
-
import type {
|
|
9
|
-
export interface Saml2BearerProviderConfig extends
|
|
8
|
+
import type { Saml2BearerExchangeConfig, Saml2CommonConfig } from './saml2Utils';
|
|
9
|
+
export interface Saml2BearerProviderConfig extends Saml2CommonConfig, Saml2BearerExchangeConfig {
|
|
10
10
|
logger?: ILogger;
|
|
11
11
|
accessToken?: string;
|
|
12
12
|
refreshToken?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Saml2BearerProvider.d.ts","sourceRoot":"","sources":["../../src/providers/Saml2BearerProvider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACV,
|
|
1
|
+
{"version":3,"file":"Saml2BearerProvider.d.ts","sourceRoot":"","sources":["../../src/providers/Saml2BearerProvider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACV,yBAAyB,EACzB,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAOtB,MAAM,WAAW,yBACf,SAAQ,iBAAiB,EACvB,yBAAyB;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,mBAAoB,SAAQ,iBAAiB;IACxD,OAAO,CAAC,MAAM,CAA4B;gBAE9B,MAAM,EAAE,yBAAyB;IAiB7C,SAAS,CAAC,WAAW,IAAI,eAAe;cAIxB,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC;cAoBrC,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC;CAMxD"}
|
|
@@ -14,6 +14,9 @@ class Saml2BearerProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
14
14
|
config;
|
|
15
15
|
constructor(config) {
|
|
16
16
|
super();
|
|
17
|
+
// A pre-built URL with no declared ACS cannot be verified against whatever
|
|
18
|
+
// the strategy binds, so it is refused here rather than at login time.
|
|
19
|
+
(0, saml2Utils_1.validateSamlConfig)(config);
|
|
17
20
|
this.config = config;
|
|
18
21
|
this.logger = config.logger;
|
|
19
22
|
if (config.accessToken) {
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { ILogger, ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces';
|
|
7
7
|
import { BaseTokenProvider } from './BaseTokenProvider';
|
|
8
|
-
import type {
|
|
9
|
-
export interface Saml2PureProviderConfig extends
|
|
8
|
+
import type { Saml2CommonConfig } from './saml2Utils';
|
|
9
|
+
export interface Saml2PureProviderConfig extends Saml2CommonConfig {
|
|
10
10
|
logger?: ILogger;
|
|
11
11
|
cookieProvider: (samlResponse: string) => Promise<string>;
|
|
12
12
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Saml2PureProvider.d.ts","sourceRoot":"","sources":["../../src/providers/Saml2PureProvider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"Saml2PureProvider.d.ts","sourceRoot":"","sources":["../../src/providers/Saml2PureProvider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtD,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3D;AAED,qBAAa,iBAAkB,SAAQ,iBAAiB;IACtD,OAAO,CAAC,MAAM,CAA0B;gBAE5B,MAAM,EAAE,uBAAuB;IAU3C,SAAS,CAAC,WAAW,IAAI,eAAe;cAIxB,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC;cAarC,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC;CAGxD"}
|
|
@@ -14,6 +14,9 @@ class Saml2PureProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
14
14
|
config;
|
|
15
15
|
constructor(config) {
|
|
16
16
|
super();
|
|
17
|
+
// A pre-built URL with no declared ACS cannot be verified against whatever
|
|
18
|
+
// the strategy binds, so it is refused here rather than at login time.
|
|
19
|
+
(0, saml2Utils_1.validateSamlConfig)(config);
|
|
17
20
|
this.config = config;
|
|
18
21
|
this.logger = config.logger;
|
|
19
22
|
this.tokenType = 'saml';
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SAML2 provider shared helpers
|
|
2
|
+
* SAML2 provider shared helpers.
|
|
3
3
|
*/
|
|
4
|
-
import type { ILogger } from '@mcp-abap-adt/interfaces';
|
|
5
|
-
export type Saml2AssertionFlow = 'browser' | 'manual' | 'assertion';
|
|
4
|
+
import type { IAuthorizationStrategy, ILogger } from '@mcp-abap-adt/interfaces';
|
|
6
5
|
export interface Saml2CommonConfig {
|
|
7
6
|
idpSsoUrl: string;
|
|
8
7
|
spEntityId: string;
|
|
8
|
+
/**
|
|
9
|
+
* Where the assertion is delivered. Required when `authorizationUrl` is set:
|
|
10
|
+
* the ACS is then buried in a deflated `SAMLRequest` this package did not
|
|
11
|
+
* build and cannot read, so it must be declared rather than inferred.
|
|
12
|
+
*/
|
|
9
13
|
acsUrl?: string;
|
|
10
14
|
relayState?: string;
|
|
11
15
|
authorizationUrl?: string;
|
|
12
|
-
browser
|
|
13
|
-
|
|
16
|
+
/** How the login is conducted. Omitted means a browser callback. */
|
|
17
|
+
authorization?: IAuthorizationStrategy<string>;
|
|
14
18
|
logger?: ILogger;
|
|
15
19
|
}
|
|
16
|
-
export interface Saml2AssertionConfig extends Saml2CommonConfig {
|
|
17
|
-
assertionFlow: Saml2AssertionFlow;
|
|
18
|
-
assertionProvider?: () => Promise<string>;
|
|
19
|
-
manualInput?: () => Promise<string>;
|
|
20
|
-
}
|
|
21
20
|
export interface Saml2BearerExchangeConfig {
|
|
22
21
|
tokenUrl?: string;
|
|
23
22
|
uaaUrl?: string;
|
|
24
23
|
clientId?: string;
|
|
25
24
|
clientSecret?: string;
|
|
26
25
|
}
|
|
27
|
-
|
|
26
|
+
/** Throw at construction rather than half-verify at runtime. */
|
|
27
|
+
export declare function validateSamlConfig(config: Saml2CommonConfig): void;
|
|
28
28
|
export declare function resolveTokenUrl(config: Saml2BearerExchangeConfig): string;
|
|
29
|
-
export declare function getSamlAssertion(config:
|
|
29
|
+
export declare function getSamlAssertion(config: Saml2CommonConfig): Promise<string>;
|
|
30
30
|
//# sourceMappingURL=saml2Utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"saml2Utils.d.ts","sourceRoot":"","sources":["../../src/providers/saml2Utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"saml2Utils.d.ts","sourceRoot":"","sources":["../../src/providers/saml2Utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAIhF,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,aAAa,CAAC,EAAE,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAOlE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,yBAAyB,GAAG,MAAM,CAQzE;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,MAAM,CAAC,CA+CjB"}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* SAML2 provider shared helpers
|
|
3
|
+
* SAML2 provider shared helpers.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.validateSamlConfig = validateSamlConfig;
|
|
7
7
|
exports.resolveTokenUrl = resolveTokenUrl;
|
|
8
8
|
exports.getSamlAssertion = getSamlAssertion;
|
|
9
|
-
const manualInput_1 = require("../auth/manualInput");
|
|
10
9
|
const saml2Auth_1 = require("../auth/saml2Auth");
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const strategies_1 = require("../strategies");
|
|
11
|
+
/** Throw at construction rather than half-verify at runtime. */
|
|
12
|
+
function validateSamlConfig(config) {
|
|
13
|
+
if (config.authorizationUrl && !config.acsUrl) {
|
|
14
|
+
throw new Error('acsUrl is required when authorizationUrl is set: the ACS inside a ' +
|
|
15
|
+
'pre-built SAML request cannot be read, so it must be declared.');
|
|
16
|
+
}
|
|
14
17
|
}
|
|
15
18
|
function resolveTokenUrl(config) {
|
|
16
19
|
if (config.tokenUrl) {
|
|
@@ -22,28 +25,45 @@ function resolveTokenUrl(config) {
|
|
|
22
25
|
throw new Error('Missing tokenUrl or uaaUrl for SAML bearer exchange');
|
|
23
26
|
}
|
|
24
27
|
async function getSamlAssertion(config) {
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const declaredAcs = config.acsUrl;
|
|
29
|
+
const request = {
|
|
30
|
+
logger: config.logger,
|
|
31
|
+
buildAuthorizationUrl: async (redirectUri) => {
|
|
32
|
+
// A declared ACS is registered with the IdP; the strategy must be
|
|
33
|
+
// listening exactly there, and an ephemeral port cannot be.
|
|
34
|
+
const acsUrl = declaredAcs ?? redirectUri;
|
|
35
|
+
if (declaredAcs && declaredAcs !== redirectUri) {
|
|
36
|
+
throw new Error(`SAML acsUrl is ${declaredAcs}, but the authorization strategy is ` +
|
|
37
|
+
`listening on ${redirectUri}. They must match.`);
|
|
38
|
+
}
|
|
39
|
+
return (0, saml2Auth_1.buildSamlAuthorizationUrl)({
|
|
40
|
+
idpSsoUrl: config.idpSsoUrl,
|
|
41
|
+
spEntityId: config.spEntityId,
|
|
42
|
+
acsUrl,
|
|
43
|
+
relayState: config.relayState,
|
|
44
|
+
authorizationUrl: config.authorizationUrl,
|
|
45
|
+
});
|
|
46
|
+
},
|
|
32
47
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
48
|
+
const supplied = config.authorization;
|
|
49
|
+
const strategy = supplied ?? (0, strategies_1.samlCallbackStrategy)();
|
|
50
|
+
try {
|
|
51
|
+
const outcome = await strategy.authorize(request);
|
|
52
|
+
// The second net, for a strategy that never called the builder and so
|
|
53
|
+
// never met the check inside it.
|
|
54
|
+
if (declaredAcs && declaredAcs !== outcome.redirectUri) {
|
|
55
|
+
throw new Error(`SAML acsUrl is ${declaredAcs}, but the authorization strategy used ` +
|
|
56
|
+
`${outcome.redirectUri}. They must match.`);
|
|
36
57
|
}
|
|
37
|
-
return
|
|
58
|
+
return outcome.payload;
|
|
38
59
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
60
|
+
finally {
|
|
61
|
+
if (!supplied) {
|
|
62
|
+
await strategy.dispose?.().catch((error) => {
|
|
63
|
+
config.logger?.warn('[SAML] dispose failed', {
|
|
64
|
+
error: error instanceof Error ? error.message : String(error),
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
46
68
|
}
|
|
47
|
-
const browser = config.browser || 'auto';
|
|
48
|
-
return await (0, saml2Auth_1.startSamlBrowserAuth)(authConfig, browser, config.logger, config.redirectPort || 3001);
|
|
49
69
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reaching the authorization URL with a browser, receiving the redirect on a
|
|
3
|
+
* local socket.
|
|
4
|
+
*
|
|
5
|
+
* The transport is injected rather than assumed: a consumer that already runs
|
|
6
|
+
* an HTTP server can pass its own `CallbackServerFactory` and keep everything
|
|
7
|
+
* else here.
|
|
8
|
+
*/
|
|
9
|
+
import type { AuthorizationOutcome, AuthorizationRequest, CallbackServerFactory, IAuthorizationStrategy } from '@mcp-abap-adt/interfaces';
|
|
10
|
+
import type { OidcCallbackResult } from '../auth/oidcBrowserAuth';
|
|
11
|
+
/**
|
|
12
|
+
* Above Linux's `ip_local_port_range` (32768ā60999), so an outbound connection
|
|
13
|
+
* never squats on it, and far from the 3001/3333 range application servers use.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DEFAULT_CALLBACK_PORT = 61001;
|
|
16
|
+
/** How long an interactive login may wait for its callback. */
|
|
17
|
+
export declare const DEFAULT_LOGIN_TIMEOUT_MS = 30000;
|
|
18
|
+
export interface CallbackStrategyOptions<TResult = string> {
|
|
19
|
+
/** `0` binds an ephemeral port. Unusable where the IdP has a registered URI. */
|
|
20
|
+
port?: number;
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/** 'none' | 'headless' print the URL; 'auto' | 'system' | 'chrome' | ⦠open it. */
|
|
23
|
+
browser?: string;
|
|
24
|
+
/**
|
|
25
|
+
* The transport. Omitted means the one this package ships for the flow; a
|
|
26
|
+
* consumer that already runs an HTTP server passes its own here and keeps
|
|
27
|
+
* everything else ā which is the point of the ready constructors existing at
|
|
28
|
+
* all rather than forcing everyone through the class.
|
|
29
|
+
*/
|
|
30
|
+
callbackServer?: CallbackServerFactory<TResult>;
|
|
31
|
+
/** Receives the bound redirect URI too, since with `port: 0` nobody knew it earlier. */
|
|
32
|
+
openUrl?: (url: string, browser: string, redirectUri: string) => Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Extra guidance for 'none'/'headless', built from the URI actually bound ā
|
|
35
|
+
* "if your browser is elsewhere, do this instead".
|
|
36
|
+
*
|
|
37
|
+
* It describes a *route*, so it belongs to whoever supplied the transport. A
|
|
38
|
+
* consumer injecting its own `callbackServer` states its own hint here; the
|
|
39
|
+
* package supplies one only for the transport it ships, and never guesses on
|
|
40
|
+
* behalf of an injected one.
|
|
41
|
+
*/
|
|
42
|
+
remoteHint?: (redirectUri: string) => string;
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
}
|
|
45
|
+
export interface BrowserCallbackStrategyOptions<TResult> extends CallbackStrategyOptions<TResult> {
|
|
46
|
+
callbackServer: CallbackServerFactory<TResult>;
|
|
47
|
+
}
|
|
48
|
+
export declare class BrowserCallbackStrategy<TResult> implements IAuthorizationStrategy<TResult> {
|
|
49
|
+
private readonly options;
|
|
50
|
+
private inFlight;
|
|
51
|
+
private controller;
|
|
52
|
+
private disposed;
|
|
53
|
+
constructor(options: BrowserCallbackStrategyOptions<TResult>);
|
|
54
|
+
authorize(request: AuthorizationRequest): Promise<AuthorizationOutcome<TResult>>;
|
|
55
|
+
/**
|
|
56
|
+
* Idempotent; ends an authorization in flight and resolves only once the
|
|
57
|
+
* factory has settled ā which it does after the socket is free.
|
|
58
|
+
*/
|
|
59
|
+
dispose(): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
export declare function browserCallbackStrategy(options?: CallbackStrategyOptions<string>): IAuthorizationStrategy<string>;
|
|
62
|
+
export declare function oidcCallbackStrategy(options?: CallbackStrategyOptions<OidcCallbackResult>): IAuthorizationStrategy<OidcCallbackResult>;
|
|
63
|
+
export declare function samlCallbackStrategy(options?: CallbackStrategyOptions<string>): IAuthorizationStrategy<string>;
|
|
64
|
+
//# sourceMappingURL=BrowserCallbackStrategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserCallbackStrategy.d.ts","sourceRoot":"","sources":["../../src/strategies/BrowserCallbackStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,0BAA0B,CAAC;AAIlC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAE3C,+DAA+D;AAC/D,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C,MAAM,WAAW,uBAAuB,CAAC,OAAO,GAAG,MAAM;IACvD,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAChD,wFAAwF;IACxF,OAAO,CAAC,EAAE,CACR,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,KAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,8BAA8B,CAAC,OAAO,CACrD,SAAQ,uBAAuB,CAAC,OAAO,CAAC;IACxC,cAAc,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAC;CAChD;AAwBD,qBAAa,uBAAuB,CAAC,OAAO,CAC1C,YAAW,sBAAsB,CAAC,OAAO,CAAC;IAOxC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,QAAQ,CAAuD;IACvE,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,QAAQ,CAAS;gBAGN,OAAO,EAAE,8BAA8B,CAAC,OAAO,CAAC;IAG7D,SAAS,CACb,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IA8FzC;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAM/B;AAyBD,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,uBAAuB,CAAC,MAAM,CAAM,GAC5C,sBAAsB,CAAC,MAAM,CAAC,CAWhC;AAED,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,uBAAuB,CAAC,kBAAkB,CAAM,GACxD,sBAAsB,CAAC,kBAAkB,CAAC,CAK5C;AAED,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,uBAAuB,CAAC,MAAM,CAAM,GAC5C,sBAAsB,CAAC,MAAM,CAAC,CAKhC"}
|