@mcp-z/oauth 1.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/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/cjs/account-utils.d.cts +107 -0
- package/dist/cjs/account-utils.d.ts +107 -0
- package/dist/cjs/account-utils.js +481 -0
- package/dist/cjs/account-utils.js.map +1 -0
- package/dist/cjs/index.d.cts +19 -0
- package/dist/cjs/index.d.ts +19 -0
- package/dist/cjs/index.js +149 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/jwt-auth.d.cts +53 -0
- package/dist/cjs/jwt-auth.d.ts +53 -0
- package/dist/cjs/jwt-auth.js +417 -0
- package/dist/cjs/jwt-auth.js.map +1 -0
- package/dist/cjs/key-utils.d.cts +131 -0
- package/dist/cjs/key-utils.d.ts +131 -0
- package/dist/cjs/key-utils.js +421 -0
- package/dist/cjs/key-utils.js.map +1 -0
- package/dist/cjs/lib/account-server/index.d.cts +45 -0
- package/dist/cjs/lib/account-server/index.d.ts +45 -0
- package/dist/cjs/lib/account-server/index.js +67 -0
- package/dist/cjs/lib/account-server/index.js.map +1 -0
- package/dist/cjs/lib/account-server/loopback.d.cts +22 -0
- package/dist/cjs/lib/account-server/loopback.d.ts +22 -0
- package/dist/cjs/lib/account-server/loopback.js +778 -0
- package/dist/cjs/lib/account-server/loopback.js.map +1 -0
- package/dist/cjs/lib/account-server/me.d.cts +23 -0
- package/dist/cjs/lib/account-server/me.d.ts +23 -0
- package/dist/cjs/lib/account-server/me.js +412 -0
- package/dist/cjs/lib/account-server/me.js.map +1 -0
- package/dist/cjs/lib/account-server/shared-utils.d.cts +6 -0
- package/dist/cjs/lib/account-server/shared-utils.d.ts +6 -0
- package/dist/cjs/lib/account-server/shared-utils.js +235 -0
- package/dist/cjs/lib/account-server/shared-utils.js.map +1 -0
- package/dist/cjs/lib/account-server/stateless.d.cts +20 -0
- package/dist/cjs/lib/account-server/stateless.d.ts +20 -0
- package/dist/cjs/lib/account-server/stateless.js +32 -0
- package/dist/cjs/lib/account-server/stateless.js.map +1 -0
- package/dist/cjs/lib/account-server/types.d.cts +32 -0
- package/dist/cjs/lib/account-server/types.d.ts +32 -0
- package/dist/cjs/lib/account-server/types.js +7 -0
- package/dist/cjs/lib/account-server/types.js.map +1 -0
- package/dist/cjs/lib/dcr-types.d.cts +126 -0
- package/dist/cjs/lib/dcr-types.d.ts +126 -0
- package/dist/cjs/lib/dcr-types.js +12 -0
- package/dist/cjs/lib/dcr-types.js.map +1 -0
- package/dist/cjs/lib/rfc-metadata-types.d.cts +46 -0
- package/dist/cjs/lib/rfc-metadata-types.d.ts +46 -0
- package/dist/cjs/lib/rfc-metadata-types.js +8 -0
- package/dist/cjs/lib/rfc-metadata-types.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/pkce.d.cts +36 -0
- package/dist/cjs/pkce.d.ts +36 -0
- package/dist/cjs/pkce.js +25 -0
- package/dist/cjs/pkce.js.map +1 -0
- package/dist/cjs/sanitizer.d.cts +37 -0
- package/dist/cjs/sanitizer.d.ts +37 -0
- package/dist/cjs/sanitizer.js +407 -0
- package/dist/cjs/sanitizer.js.map +1 -0
- package/dist/cjs/schemas/index.d.cts +36 -0
- package/dist/cjs/schemas/index.d.ts +36 -0
- package/dist/cjs/schemas/index.js +28 -0
- package/dist/cjs/schemas/index.js.map +1 -0
- package/dist/cjs/session-auth.d.cts +79 -0
- package/dist/cjs/session-auth.d.ts +79 -0
- package/dist/cjs/session-auth.js +354 -0
- package/dist/cjs/session-auth.js.map +1 -0
- package/dist/cjs/templates.d.cts +18 -0
- package/dist/cjs/templates.d.ts +18 -0
- package/dist/cjs/templates.js +38 -0
- package/dist/cjs/templates.js.map +1 -0
- package/dist/cjs/types.d.cts +343 -0
- package/dist/cjs/types.d.ts +343 -0
- package/dist/cjs/types.js +210 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/account-utils.d.ts +107 -0
- package/dist/esm/account-utils.js +179 -0
- package/dist/esm/account-utils.js.map +1 -0
- package/dist/esm/index.d.ts +19 -0
- package/dist/esm/index.js +23 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/jwt-auth.d.ts +53 -0
- package/dist/esm/jwt-auth.js +164 -0
- package/dist/esm/jwt-auth.js.map +1 -0
- package/dist/esm/key-utils.d.ts +131 -0
- package/dist/esm/key-utils.js +143 -0
- package/dist/esm/key-utils.js.map +1 -0
- package/dist/esm/lib/account-server/index.d.ts +45 -0
- package/dist/esm/lib/account-server/index.js +41 -0
- package/dist/esm/lib/account-server/index.js.map +1 -0
- package/dist/esm/lib/account-server/loopback.d.ts +22 -0
- package/dist/esm/lib/account-server/loopback.js +372 -0
- package/dist/esm/lib/account-server/loopback.js.map +1 -0
- package/dist/esm/lib/account-server/me.d.ts +23 -0
- package/dist/esm/lib/account-server/me.js +170 -0
- package/dist/esm/lib/account-server/me.js.map +1 -0
- package/dist/esm/lib/account-server/shared-utils.d.ts +6 -0
- package/dist/esm/lib/account-server/shared-utils.js +24 -0
- package/dist/esm/lib/account-server/shared-utils.js.map +1 -0
- package/dist/esm/lib/account-server/stateless.d.ts +20 -0
- package/dist/esm/lib/account-server/stateless.js +25 -0
- package/dist/esm/lib/account-server/stateless.js.map +1 -0
- package/dist/esm/lib/account-server/types.d.ts +32 -0
- package/dist/esm/lib/account-server/types.js +6 -0
- package/dist/esm/lib/account-server/types.js.map +1 -0
- package/dist/esm/lib/dcr-types.d.ts +126 -0
- package/dist/esm/lib/dcr-types.js +13 -0
- package/dist/esm/lib/dcr-types.js.map +1 -0
- package/dist/esm/lib/rfc-metadata-types.d.ts +46 -0
- package/dist/esm/lib/rfc-metadata-types.js +7 -0
- package/dist/esm/lib/rfc-metadata-types.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/pkce.d.ts +36 -0
- package/dist/esm/pkce.js +33 -0
- package/dist/esm/pkce.js.map +1 -0
- package/dist/esm/sanitizer.d.ts +37 -0
- package/dist/esm/sanitizer.js +256 -0
- package/dist/esm/sanitizer.js.map +1 -0
- package/dist/esm/schemas/index.d.ts +36 -0
- package/dist/esm/schemas/index.js +19 -0
- package/dist/esm/schemas/index.js.map +1 -0
- package/dist/esm/session-auth.d.ts +79 -0
- package/dist/esm/session-auth.js +141 -0
- package/dist/esm/session-auth.js.map +1 -0
- package/dist/esm/templates.d.ts +18 -0
- package/dist/esm/templates.js +132 -0
- package/dist/esm/templates.js.map +1 -0
- package/dist/esm/types.d.ts +343 -0
- package/dist/esm/types.js +34 -0
- package/dist/esm/types.js.map +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-based user authentication for multi-tenant deployments
|
|
3
|
+
*
|
|
4
|
+
* Extracts user ID from HTTP session cookies with HMAC signature verification.
|
|
5
|
+
* Compatible with Express/Connect session middleware patterns.
|
|
6
|
+
*/
|
|
7
|
+
import type { SessionUserAuthConfig, UserAuthProvider } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Session-based user authentication provider
|
|
10
|
+
*
|
|
11
|
+
* Verifies signed session cookies and extracts user IDs.
|
|
12
|
+
* Use for multi-tenant deployments where users authenticate via web sessions.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* // Multi-tenant server setup with session-based user authentication
|
|
17
|
+
* const userAuth = new SessionUserAuth({
|
|
18
|
+
* sessionSecret: process.env.SESSION_SECRET!,
|
|
19
|
+
* cookieName: 'app_session',
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* // Create OAuth adapters with session-based user identification
|
|
23
|
+
* const oauthAdapters = await createOAuthAdapters(
|
|
24
|
+
* config.transport,
|
|
25
|
+
* {
|
|
26
|
+
* service: 'gmail',
|
|
27
|
+
* clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
28
|
+
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
29
|
+
* scope: GOOGLE_SCOPE,
|
|
30
|
+
* auth: 'loopback-oauth',
|
|
31
|
+
* headless: false,
|
|
32
|
+
* redirectUri: undefined,
|
|
33
|
+
* },
|
|
34
|
+
* {
|
|
35
|
+
* logger,
|
|
36
|
+
* tokenStore,
|
|
37
|
+
* userAuth, // Session-based user identification for multi-tenant deployments
|
|
38
|
+
* }
|
|
39
|
+
* );
|
|
40
|
+
*
|
|
41
|
+
* // Use auth middleware with tools
|
|
42
|
+
* const { middleware: authMiddleware } = oauthAdapters;
|
|
43
|
+
* const tools = toolFactories.map(f => f()).map(authMiddleware.withToolAuth);
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare class SessionUserAuth implements UserAuthProvider {
|
|
47
|
+
private readonly secret;
|
|
48
|
+
private readonly cookieName;
|
|
49
|
+
private readonly algorithm;
|
|
50
|
+
constructor(config: SessionUserAuthConfig);
|
|
51
|
+
/**
|
|
52
|
+
* Extract and verify user ID from session cookie
|
|
53
|
+
*
|
|
54
|
+
* @param req - HTTP request object with cookie header
|
|
55
|
+
* @returns User ID from verified session
|
|
56
|
+
* @throws Error if session missing, invalid, or expired
|
|
57
|
+
*/
|
|
58
|
+
getUserId(req: unknown): Promise<string>;
|
|
59
|
+
/**
|
|
60
|
+
* Parse cookie from header string
|
|
61
|
+
*/
|
|
62
|
+
private parseCookie;
|
|
63
|
+
/**
|
|
64
|
+
* Generate HMAC signature for session data
|
|
65
|
+
*/
|
|
66
|
+
private sign;
|
|
67
|
+
/**
|
|
68
|
+
* Constant-time string comparison to prevent timing attacks
|
|
69
|
+
*/
|
|
70
|
+
private constantTimeCompare;
|
|
71
|
+
/**
|
|
72
|
+
* Helper for creating session cookies (for testing/integration)
|
|
73
|
+
*
|
|
74
|
+
* @param userId - User ID to encode in session
|
|
75
|
+
* @param expiresInMs - Optional expiration time in milliseconds from now
|
|
76
|
+
* @returns Signed session cookie value
|
|
77
|
+
*/
|
|
78
|
+
createSessionCookie(userId: string, expiresInMs?: number): string;
|
|
79
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-based user authentication for multi-tenant deployments
|
|
3
|
+
*
|
|
4
|
+
* Extracts user ID from HTTP session cookies with HMAC signature verification.
|
|
5
|
+
* Compatible with Express/Connect session middleware patterns.
|
|
6
|
+
*/ "use strict";
|
|
7
|
+
Object.defineProperty(exports, "__esModule", {
|
|
8
|
+
value: true
|
|
9
|
+
});
|
|
10
|
+
Object.defineProperty(exports, "SessionUserAuth", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function() {
|
|
13
|
+
return SessionUserAuth;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
var _crypto = require("crypto");
|
|
17
|
+
function _array_like_to_array(arr, len) {
|
|
18
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
19
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
20
|
+
return arr2;
|
|
21
|
+
}
|
|
22
|
+
function _array_with_holes(arr) {
|
|
23
|
+
if (Array.isArray(arr)) return arr;
|
|
24
|
+
}
|
|
25
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
26
|
+
try {
|
|
27
|
+
var info = gen[key](arg);
|
|
28
|
+
var value = info.value;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
reject(error);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (info.done) {
|
|
34
|
+
resolve(value);
|
|
35
|
+
} else {
|
|
36
|
+
Promise.resolve(value).then(_next, _throw);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function _async_to_generator(fn) {
|
|
40
|
+
return function() {
|
|
41
|
+
var self = this, args = arguments;
|
|
42
|
+
return new Promise(function(resolve, reject) {
|
|
43
|
+
var gen = fn.apply(self, args);
|
|
44
|
+
function _next(value) {
|
|
45
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
46
|
+
}
|
|
47
|
+
function _throw(err) {
|
|
48
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
49
|
+
}
|
|
50
|
+
_next(undefined);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function _class_call_check(instance, Constructor) {
|
|
55
|
+
if (!(instance instanceof Constructor)) {
|
|
56
|
+
throw new TypeError("Cannot call a class as a function");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function _define_property(obj, key, value) {
|
|
60
|
+
if (key in obj) {
|
|
61
|
+
Object.defineProperty(obj, key, {
|
|
62
|
+
value: value,
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
writable: true
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
obj[key] = value;
|
|
69
|
+
}
|
|
70
|
+
return obj;
|
|
71
|
+
}
|
|
72
|
+
function _iterable_to_array(iter) {
|
|
73
|
+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
74
|
+
}
|
|
75
|
+
function _iterable_to_array_limit(arr, i) {
|
|
76
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
77
|
+
if (_i == null) return;
|
|
78
|
+
var _arr = [];
|
|
79
|
+
var _n = true;
|
|
80
|
+
var _d = false;
|
|
81
|
+
var _s, _e;
|
|
82
|
+
try {
|
|
83
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
84
|
+
_arr.push(_s.value);
|
|
85
|
+
if (i && _arr.length === i) break;
|
|
86
|
+
}
|
|
87
|
+
} catch (err) {
|
|
88
|
+
_d = true;
|
|
89
|
+
_e = err;
|
|
90
|
+
} finally{
|
|
91
|
+
try {
|
|
92
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
93
|
+
} finally{
|
|
94
|
+
if (_d) throw _e;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return _arr;
|
|
98
|
+
}
|
|
99
|
+
function _non_iterable_rest() {
|
|
100
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
101
|
+
}
|
|
102
|
+
function _object_spread(target) {
|
|
103
|
+
for(var i = 1; i < arguments.length; i++){
|
|
104
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
105
|
+
var ownKeys = Object.keys(source);
|
|
106
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
107
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
108
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
ownKeys.forEach(function(key) {
|
|
112
|
+
_define_property(target, key, source[key]);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return target;
|
|
116
|
+
}
|
|
117
|
+
function _sliced_to_array(arr, i) {
|
|
118
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
119
|
+
}
|
|
120
|
+
function _to_array(arr) {
|
|
121
|
+
return _array_with_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_rest();
|
|
122
|
+
}
|
|
123
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
124
|
+
if (!o) return;
|
|
125
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
126
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
127
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
128
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
129
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
130
|
+
}
|
|
131
|
+
function _ts_generator(thisArg, body) {
|
|
132
|
+
var f, y, t, _ = {
|
|
133
|
+
label: 0,
|
|
134
|
+
sent: function() {
|
|
135
|
+
if (t[0] & 1) throw t[1];
|
|
136
|
+
return t[1];
|
|
137
|
+
},
|
|
138
|
+
trys: [],
|
|
139
|
+
ops: []
|
|
140
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
141
|
+
return d(g, "next", {
|
|
142
|
+
value: verb(0)
|
|
143
|
+
}), d(g, "throw", {
|
|
144
|
+
value: verb(1)
|
|
145
|
+
}), d(g, "return", {
|
|
146
|
+
value: verb(2)
|
|
147
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
148
|
+
value: function() {
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
}), g;
|
|
152
|
+
function verb(n) {
|
|
153
|
+
return function(v) {
|
|
154
|
+
return step([
|
|
155
|
+
n,
|
|
156
|
+
v
|
|
157
|
+
]);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function step(op) {
|
|
161
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
162
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
163
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
164
|
+
if (y = 0, t) op = [
|
|
165
|
+
op[0] & 2,
|
|
166
|
+
t.value
|
|
167
|
+
];
|
|
168
|
+
switch(op[0]){
|
|
169
|
+
case 0:
|
|
170
|
+
case 1:
|
|
171
|
+
t = op;
|
|
172
|
+
break;
|
|
173
|
+
case 4:
|
|
174
|
+
_.label++;
|
|
175
|
+
return {
|
|
176
|
+
value: op[1],
|
|
177
|
+
done: false
|
|
178
|
+
};
|
|
179
|
+
case 5:
|
|
180
|
+
_.label++;
|
|
181
|
+
y = op[1];
|
|
182
|
+
op = [
|
|
183
|
+
0
|
|
184
|
+
];
|
|
185
|
+
continue;
|
|
186
|
+
case 7:
|
|
187
|
+
op = _.ops.pop();
|
|
188
|
+
_.trys.pop();
|
|
189
|
+
continue;
|
|
190
|
+
default:
|
|
191
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
192
|
+
_ = 0;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
196
|
+
_.label = op[1];
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
200
|
+
_.label = t[1];
|
|
201
|
+
t = op;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
if (t && _.label < t[2]) {
|
|
205
|
+
_.label = t[2];
|
|
206
|
+
_.ops.push(op);
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
if (t[2]) _.ops.pop();
|
|
210
|
+
_.trys.pop();
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
op = body.call(thisArg, _);
|
|
214
|
+
} catch (e) {
|
|
215
|
+
op = [
|
|
216
|
+
6,
|
|
217
|
+
e
|
|
218
|
+
];
|
|
219
|
+
y = 0;
|
|
220
|
+
} finally{
|
|
221
|
+
f = t = 0;
|
|
222
|
+
}
|
|
223
|
+
if (op[0] & 5) throw op[1];
|
|
224
|
+
return {
|
|
225
|
+
value: op[0] ? op[1] : void 0,
|
|
226
|
+
done: true
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
var SessionUserAuth = /*#__PURE__*/ function() {
|
|
231
|
+
"use strict";
|
|
232
|
+
function SessionUserAuth(config) {
|
|
233
|
+
_class_call_check(this, SessionUserAuth);
|
|
234
|
+
var _config_cookieName, _config_algorithm;
|
|
235
|
+
if (!config.sessionSecret || config.sessionSecret.length < 32) {
|
|
236
|
+
throw new Error('SessionUserAuth: sessionSecret must be at least 32 characters');
|
|
237
|
+
}
|
|
238
|
+
this.secret = config.sessionSecret;
|
|
239
|
+
this.cookieName = (_config_cookieName = config.cookieName) !== null && _config_cookieName !== void 0 ? _config_cookieName : 'session';
|
|
240
|
+
this.algorithm = (_config_algorithm = config.algorithm) !== null && _config_algorithm !== void 0 ? _config_algorithm : 'sha256';
|
|
241
|
+
}
|
|
242
|
+
var _proto = SessionUserAuth.prototype;
|
|
243
|
+
/**
|
|
244
|
+
* Extract and verify user ID from session cookie
|
|
245
|
+
*
|
|
246
|
+
* @param req - HTTP request object with cookie header
|
|
247
|
+
* @returns User ID from verified session
|
|
248
|
+
* @throws Error if session missing, invalid, or expired
|
|
249
|
+
*/ _proto.getUserId = function getUserId(req) {
|
|
250
|
+
return _async_to_generator(function() {
|
|
251
|
+
var _httpReq_headers, httpReq, cookieHeader, sessionCookie, parts, _parts, dataB64, signature, expectedSignature, sessionData, dataJson;
|
|
252
|
+
return _ts_generator(this, function(_state) {
|
|
253
|
+
httpReq = req;
|
|
254
|
+
cookieHeader = (_httpReq_headers = httpReq.headers) === null || _httpReq_headers === void 0 ? void 0 : _httpReq_headers.cookie;
|
|
255
|
+
if (!cookieHeader) {
|
|
256
|
+
throw new Error('SessionUserAuth: No cookie header found');
|
|
257
|
+
}
|
|
258
|
+
sessionCookie = this.parseCookie(cookieHeader, this.cookieName);
|
|
259
|
+
if (!sessionCookie) {
|
|
260
|
+
throw new Error("SessionUserAuth: Session cookie '".concat(this.cookieName, "' not found"));
|
|
261
|
+
}
|
|
262
|
+
// Format: base64(data).signature
|
|
263
|
+
parts = sessionCookie.split('.');
|
|
264
|
+
if (parts.length !== 2) {
|
|
265
|
+
throw new Error('SessionUserAuth: Invalid session format (expected data.signature)');
|
|
266
|
+
}
|
|
267
|
+
_parts = _sliced_to_array(parts, 2), dataB64 = _parts[0], signature = _parts[1];
|
|
268
|
+
expectedSignature = this.sign(dataB64);
|
|
269
|
+
if (!this.constantTimeCompare(signature, expectedSignature)) {
|
|
270
|
+
throw new Error('SessionUserAuth: Invalid session signature');
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
dataJson = Buffer.from(dataB64, 'base64').toString('utf8');
|
|
274
|
+
sessionData = JSON.parse(dataJson);
|
|
275
|
+
} catch (unused) {
|
|
276
|
+
throw new Error('SessionUserAuth: Invalid session data encoding');
|
|
277
|
+
}
|
|
278
|
+
if (sessionData.exp !== undefined && Date.now() > sessionData.exp) {
|
|
279
|
+
throw new Error('SessionUserAuth: Session expired');
|
|
280
|
+
}
|
|
281
|
+
if (!sessionData.userId || typeof sessionData.userId !== 'string') {
|
|
282
|
+
throw new Error('SessionUserAuth: Session missing userId field');
|
|
283
|
+
}
|
|
284
|
+
return [
|
|
285
|
+
2,
|
|
286
|
+
sessionData.userId
|
|
287
|
+
];
|
|
288
|
+
});
|
|
289
|
+
}).call(this);
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* Parse cookie from header string
|
|
293
|
+
*/ _proto.parseCookie = function parseCookie(cookieHeader, name) {
|
|
294
|
+
var cookies = cookieHeader.split(';');
|
|
295
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
296
|
+
try {
|
|
297
|
+
for(var _iterator = cookies[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
298
|
+
var cookie = _step.value;
|
|
299
|
+
var _cookie_trim_split = _to_array(cookie.trim().split('=')), key = _cookie_trim_split[0], valueParts = _cookie_trim_split.slice(1);
|
|
300
|
+
if (key === name) {
|
|
301
|
+
return valueParts.join('='); // Handle values with = in them
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} catch (err) {
|
|
305
|
+
_didIteratorError = true;
|
|
306
|
+
_iteratorError = err;
|
|
307
|
+
} finally{
|
|
308
|
+
try {
|
|
309
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
310
|
+
_iterator.return();
|
|
311
|
+
}
|
|
312
|
+
} finally{
|
|
313
|
+
if (_didIteratorError) {
|
|
314
|
+
throw _iteratorError;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return null;
|
|
319
|
+
};
|
|
320
|
+
/**
|
|
321
|
+
* Generate HMAC signature for session data
|
|
322
|
+
*/ _proto.sign = function sign(data) {
|
|
323
|
+
return (0, _crypto.createHmac)(this.algorithm, this.secret).update(data).digest('hex');
|
|
324
|
+
};
|
|
325
|
+
/**
|
|
326
|
+
* Constant-time string comparison to prevent timing attacks
|
|
327
|
+
*/ _proto.constantTimeCompare = function constantTimeCompare(a, b) {
|
|
328
|
+
if (a.length !== b.length) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
var bufA = Buffer.from(a);
|
|
332
|
+
var bufB = Buffer.from(b);
|
|
333
|
+
return (0, _crypto.timingSafeEqual)(bufA, bufB);
|
|
334
|
+
};
|
|
335
|
+
/**
|
|
336
|
+
* Helper for creating session cookies (for testing/integration)
|
|
337
|
+
*
|
|
338
|
+
* @param userId - User ID to encode in session
|
|
339
|
+
* @param expiresInMs - Optional expiration time in milliseconds from now
|
|
340
|
+
* @returns Signed session cookie value
|
|
341
|
+
*/ _proto.createSessionCookie = function createSessionCookie(userId, expiresInMs) {
|
|
342
|
+
var sessionData = _object_spread({
|
|
343
|
+
userId: userId
|
|
344
|
+
}, expiresInMs !== undefined && {
|
|
345
|
+
exp: Date.now() + expiresInMs
|
|
346
|
+
});
|
|
347
|
+
var dataJson = JSON.stringify(sessionData);
|
|
348
|
+
var dataB64 = Buffer.from(dataJson).toString('base64');
|
|
349
|
+
var signature = this.sign(dataB64);
|
|
350
|
+
return "".concat(dataB64, ".").concat(signature);
|
|
351
|
+
};
|
|
352
|
+
return SessionUserAuth;
|
|
353
|
+
}();
|
|
354
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth/src/session-auth.ts"],"sourcesContent":["/**\n * Session-based user authentication for multi-tenant deployments\n *\n * Extracts user ID from HTTP session cookies with HMAC signature verification.\n * Compatible with Express/Connect session middleware patterns.\n */\n\nimport { createHmac, timingSafeEqual } from 'crypto';\nimport type { SessionUserAuthConfig, UserAuthProvider } from './types.ts';\n\n/**\n * HTTP request interface (subset needed for session auth)\n */\ninterface HttpRequest {\n headers?: {\n cookie?: string;\n };\n}\n\n/**\n * Session cookie structure\n */\ninterface SessionData {\n userId: string;\n exp?: number; // Optional expiration timestamp (ms)\n}\n\n/**\n * Session-based user authentication provider\n *\n * Verifies signed session cookies and extracts user IDs.\n * Use for multi-tenant deployments where users authenticate via web sessions.\n *\n * @example\n * ```typescript\n * // Multi-tenant server setup with session-based user authentication\n * const userAuth = new SessionUserAuth({\n * sessionSecret: process.env.SESSION_SECRET!,\n * cookieName: 'app_session',\n * });\n *\n * // Create OAuth adapters with session-based user identification\n * const oauthAdapters = await createOAuthAdapters(\n * config.transport,\n * {\n * service: 'gmail',\n * clientId: process.env.GOOGLE_CLIENT_ID!,\n * clientSecret: process.env.GOOGLE_CLIENT_SECRET,\n * scope: GOOGLE_SCOPE,\n * auth: 'loopback-oauth',\n * headless: false,\n * redirectUri: undefined,\n * },\n * {\n * logger,\n * tokenStore,\n * userAuth, // Session-based user identification for multi-tenant deployments\n * }\n * );\n *\n * // Use auth middleware with tools\n * const { middleware: authMiddleware } = oauthAdapters;\n * const tools = toolFactories.map(f => f()).map(authMiddleware.withToolAuth);\n * ```\n */\nexport class SessionUserAuth implements UserAuthProvider {\n private readonly secret: string;\n private readonly cookieName: string;\n private readonly algorithm: 'sha256' | 'sha512';\n\n constructor(config: SessionUserAuthConfig) {\n if (!config.sessionSecret || config.sessionSecret.length < 32) {\n throw new Error('SessionUserAuth: sessionSecret must be at least 32 characters');\n }\n\n this.secret = config.sessionSecret;\n this.cookieName = config.cookieName ?? 'session';\n this.algorithm = config.algorithm ?? 'sha256';\n }\n\n /**\n * Extract and verify user ID from session cookie\n *\n * @param req - HTTP request object with cookie header\n * @returns User ID from verified session\n * @throws Error if session missing, invalid, or expired\n */\n async getUserId(req: unknown): Promise<string> {\n const httpReq = req as HttpRequest;\n\n const cookieHeader = httpReq.headers?.cookie;\n if (!cookieHeader) {\n throw new Error('SessionUserAuth: No cookie header found');\n }\n\n const sessionCookie = this.parseCookie(cookieHeader, this.cookieName);\n if (!sessionCookie) {\n throw new Error(`SessionUserAuth: Session cookie '${this.cookieName}' not found`);\n }\n\n // Format: base64(data).signature\n const parts = sessionCookie.split('.');\n if (parts.length !== 2) {\n throw new Error('SessionUserAuth: Invalid session format (expected data.signature)');\n }\n\n const [dataB64, signature] = parts as [string, string];\n\n const expectedSignature = this.sign(dataB64);\n if (!this.constantTimeCompare(signature, expectedSignature)) {\n throw new Error('SessionUserAuth: Invalid session signature');\n }\n\n let sessionData: SessionData;\n try {\n const dataJson = Buffer.from(dataB64, 'base64').toString('utf8');\n sessionData = JSON.parse(dataJson) as SessionData;\n } catch {\n throw new Error('SessionUserAuth: Invalid session data encoding');\n }\n\n if (sessionData.exp !== undefined && Date.now() > sessionData.exp) {\n throw new Error('SessionUserAuth: Session expired');\n }\n\n if (!sessionData.userId || typeof sessionData.userId !== 'string') {\n throw new Error('SessionUserAuth: Session missing userId field');\n }\n\n return sessionData.userId;\n }\n\n /**\n * Parse cookie from header string\n */\n private parseCookie(cookieHeader: string, name: string): string | null {\n const cookies = cookieHeader.split(';');\n for (const cookie of cookies) {\n const [key, ...valueParts] = cookie.trim().split('=');\n if (key === name) {\n return valueParts.join('='); // Handle values with = in them\n }\n }\n return null;\n }\n\n /**\n * Generate HMAC signature for session data\n */\n private sign(data: string): string {\n return createHmac(this.algorithm, this.secret).update(data).digest('hex');\n }\n\n /**\n * Constant-time string comparison to prevent timing attacks\n */\n private constantTimeCompare(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n\n const bufA = Buffer.from(a);\n const bufB = Buffer.from(b);\n\n return timingSafeEqual(bufA, bufB);\n }\n\n /**\n * Helper for creating session cookies (for testing/integration)\n *\n * @param userId - User ID to encode in session\n * @param expiresInMs - Optional expiration time in milliseconds from now\n * @returns Signed session cookie value\n */\n createSessionCookie(userId: string, expiresInMs?: number): string {\n const sessionData: SessionData = {\n userId,\n ...(expiresInMs !== undefined && { exp: Date.now() + expiresInMs }),\n };\n\n const dataJson = JSON.stringify(sessionData);\n const dataB64 = Buffer.from(dataJson).toString('base64');\n const signature = this.sign(dataB64);\n\n return `${dataB64}.${signature}`;\n }\n}\n"],"names":["SessionUserAuth","config","sessionSecret","length","Error","secret","cookieName","algorithm","getUserId","req","httpReq","cookieHeader","sessionCookie","parts","dataB64","signature","expectedSignature","sessionData","dataJson","headers","cookie","parseCookie","split","sign","constantTimeCompare","Buffer","from","toString","JSON","parse","exp","undefined","Date","now","userId","name","cookies","trim","key","valueParts","join","data","createHmac","update","digest","a","b","bufA","bufB","timingSafeEqual","createSessionCookie","expiresInMs","stringify"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML templates for OAuth callback pages
|
|
3
|
+
*
|
|
4
|
+
* These templates are shown in the browser after OAuth authorization
|
|
5
|
+
* for loopback OAuth flows (RFC 8252).
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* HTML template for successful OAuth authorization
|
|
9
|
+
*/
|
|
10
|
+
export declare function getSuccessTemplate(): string;
|
|
11
|
+
/**
|
|
12
|
+
* HTML template for OAuth error
|
|
13
|
+
*/
|
|
14
|
+
export declare function getErrorTemplate(error: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Escape HTML special characters to prevent XSS
|
|
17
|
+
*/
|
|
18
|
+
export declare function escapeHtml(unsafe: string): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML templates for OAuth callback pages
|
|
3
|
+
*
|
|
4
|
+
* These templates are shown in the browser after OAuth authorization
|
|
5
|
+
* for loopback OAuth flows (RFC 8252).
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* HTML template for successful OAuth authorization
|
|
9
|
+
*/
|
|
10
|
+
export declare function getSuccessTemplate(): string;
|
|
11
|
+
/**
|
|
12
|
+
* HTML template for OAuth error
|
|
13
|
+
*/
|
|
14
|
+
export declare function getErrorTemplate(error: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Escape HTML special characters to prevent XSS
|
|
17
|
+
*/
|
|
18
|
+
export declare function escapeHtml(unsafe: string): string;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML templates for OAuth callback pages
|
|
3
|
+
*
|
|
4
|
+
* These templates are shown in the browser after OAuth authorization
|
|
5
|
+
* for loopback OAuth flows (RFC 8252).
|
|
6
|
+
*/ /**
|
|
7
|
+
* HTML template for successful OAuth authorization
|
|
8
|
+
*/ "use strict";
|
|
9
|
+
Object.defineProperty(exports, "__esModule", {
|
|
10
|
+
value: true
|
|
11
|
+
});
|
|
12
|
+
function _export(target, all) {
|
|
13
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
_export(exports, {
|
|
19
|
+
get escapeHtml () {
|
|
20
|
+
return escapeHtml;
|
|
21
|
+
},
|
|
22
|
+
get getErrorTemplate () {
|
|
23
|
+
return getErrorTemplate;
|
|
24
|
+
},
|
|
25
|
+
get getSuccessTemplate () {
|
|
26
|
+
return getSuccessTemplate;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
function getSuccessTemplate() {
|
|
30
|
+
return '\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset="utf-8">\n <title>Authorization Successful</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #e8ebf7 0%, #ede9f2 100%);\n }\n .container {\n background: white;\n padding: 3rem;\n border-radius: 1rem;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n text-align: center;\n max-width: 400px;\n }\n h1 {\n color: #2d3748;\n margin-bottom: 1rem;\n font-size: 1.875rem;\n }\n p {\n color: #718096;\n font-size: 1.125rem;\n margin-bottom: 1.5rem;\n }\n .icon {\n font-size: 4rem;\n margin-bottom: 1rem;\n }\n </style>\n</head>\n<body>\n <div class="container">\n <div class="icon">✅</div>\n <h1>Authorization Successful!</h1>\n <p>You can close this window and return to your terminal.</p>\n </div>\n <script>\n // Auto-close after 3 seconds\n setTimeout(() => {\n window.close();\n }, 3000);\n </script>\n</body>\n</html>\n '.trim();
|
|
31
|
+
}
|
|
32
|
+
function getErrorTemplate(error) {
|
|
33
|
+
return '\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset="utf-8">\n <title>Authorization Failed</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #fce9f7 0%, #faebed 100%);\n }\n .container {\n background: white;\n padding: 3rem;\n border-radius: 1rem;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n text-align: center;\n max-width: 400px;\n }\n h1 {\n color: #2d3748;\n margin-bottom: 1rem;\n font-size: 1.875rem;\n }\n p {\n color: #718096;\n font-size: 1.125rem;\n margin-bottom: 1.5rem;\n }\n .error {\n background: #fed7d7;\n color: #9b2c2c;\n padding: 0.75rem;\n border-radius: 0.5rem;\n margin-top: 1rem;\n font-family: monospace;\n font-size: 0.875rem;\n }\n .icon {\n font-size: 4rem;\n margin-bottom: 1rem;\n }\n </style>\n</head>\n<body>\n <div class="container">\n <div class="icon">❌</div>\n <h1>Authorization Failed</h1>\n <p>Please try again from your terminal.</p>\n <div class="error">'.concat(escapeHtml(error), "</div>\n </div>\n</body>\n</html>\n ").trim();
|
|
34
|
+
}
|
|
35
|
+
function escapeHtml(unsafe) {
|
|
36
|
+
return unsafe.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
37
|
+
}
|
|
38
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth/src/templates.ts"],"sourcesContent":["/**\n * HTML templates for OAuth callback pages\n *\n * These templates are shown in the browser after OAuth authorization\n * for loopback OAuth flows (RFC 8252).\n */\n\n/**\n * HTML template for successful OAuth authorization\n */\nexport function getSuccessTemplate(): string {\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Authorization Successful</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #e8ebf7 0%, #ede9f2 100%);\n }\n .container {\n background: white;\n padding: 3rem;\n border-radius: 1rem;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n text-align: center;\n max-width: 400px;\n }\n h1 {\n color: #2d3748;\n margin-bottom: 1rem;\n font-size: 1.875rem;\n }\n p {\n color: #718096;\n font-size: 1.125rem;\n margin-bottom: 1.5rem;\n }\n .icon {\n font-size: 4rem;\n margin-bottom: 1rem;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"icon\">✅</div>\n <h1>Authorization Successful!</h1>\n <p>You can close this window and return to your terminal.</p>\n </div>\n <script>\n // Auto-close after 3 seconds\n setTimeout(() => {\n window.close();\n }, 3000);\n </script>\n</body>\n</html>\n `.trim();\n}\n\n/**\n * HTML template for OAuth error\n */\nexport function getErrorTemplate(error: string): string {\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Authorization Failed</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #fce9f7 0%, #faebed 100%);\n }\n .container {\n background: white;\n padding: 3rem;\n border-radius: 1rem;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n text-align: center;\n max-width: 400px;\n }\n h1 {\n color: #2d3748;\n margin-bottom: 1rem;\n font-size: 1.875rem;\n }\n p {\n color: #718096;\n font-size: 1.125rem;\n margin-bottom: 1.5rem;\n }\n .error {\n background: #fed7d7;\n color: #9b2c2c;\n padding: 0.75rem;\n border-radius: 0.5rem;\n margin-top: 1rem;\n font-family: monospace;\n font-size: 0.875rem;\n }\n .icon {\n font-size: 4rem;\n margin-bottom: 1rem;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"icon\">❌</div>\n <h1>Authorization Failed</h1>\n <p>Please try again from your terminal.</p>\n <div class=\"error\">${escapeHtml(error)}</div>\n </div>\n</body>\n</html>\n `.trim();\n}\n\n/**\n * Escape HTML special characters to prevent XSS\n */\nexport function escapeHtml(unsafe: string): string {\n return unsafe.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"').replace(/'/g, ''');\n}\n"],"names":["escapeHtml","getErrorTemplate","getSuccessTemplate","trim","error","unsafe","replace"],"mappings":"AAAA;;;;;CAKC,GAED;;CAEC;;;;;;;;;;;QA+HeA;eAAAA;;QAjEAC;eAAAA;;QA7DAC;eAAAA;;;AAAT,SAASA;IACd,OAAO,yuCAsDLC,IAAI;AACR;AAKO,SAASF,iBAAiBG,KAAa;IAC5C,OAAO,AAAC,gyCAsDiC,OAAlBJ,WAAWI,QAAO,0CAIvCD,IAAI;AACR;AAKO,SAASH,WAAWK,MAAc;IACvC,OAAOA,OAAOC,OAAO,CAAC,MAAM,SAASA,OAAO,CAAC,MAAM,QAAQA,OAAO,CAAC,MAAM,QAAQA,OAAO,CAAC,MAAM,UAAUA,OAAO,CAAC,MAAM;AACzH"}
|