@enterprisestandard/esv 0.0.5-beta.20260115.3 → 0.0.5-beta.20260115.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/iam/index.d.ts +8 -5
  2. package/dist/iam/index.js +5755 -664
  3. package/dist/iam/index.js.map +1 -1
  4. package/dist/index.d.ts +90 -10
  5. package/dist/index.js +6897 -152
  6. package/dist/index.js.map +1 -1
  7. package/dist/runner.d.ts +0 -36
  8. package/dist/runner.js +11407 -283
  9. package/dist/runner.js.map +1 -1
  10. package/dist/server/index.d.ts +88 -14
  11. package/dist/server/index.js +1387 -33
  12. package/dist/server/index.js.map +1 -1
  13. package/dist/sso/index.d.ts +8 -5
  14. package/dist/sso/index.js +365 -357
  15. package/dist/sso/index.js.map +1 -1
  16. package/dist/{types.d.ts → types-Bn1pr_xY.d.ts} +13 -11
  17. package/dist/workload/index.d.ts +8 -5
  18. package/dist/workload/index.js +393 -403
  19. package/dist/workload/index.js.map +1 -1
  20. package/package.json +2 -4
  21. package/dist/iam/index.d.ts.map +0 -1
  22. package/dist/index.d.ts.map +0 -1
  23. package/dist/runner.d.ts.map +0 -1
  24. package/dist/server/crypto.d.ts +0 -46
  25. package/dist/server/crypto.d.ts.map +0 -1
  26. package/dist/server/crypto.js +0 -134
  27. package/dist/server/crypto.js.map +0 -1
  28. package/dist/server/iam.d.ts +0 -11
  29. package/dist/server/iam.d.ts.map +0 -1
  30. package/dist/server/iam.js +0 -402
  31. package/dist/server/iam.js.map +0 -1
  32. package/dist/server/index.d.ts.map +0 -1
  33. package/dist/server/server.d.ts +0 -66
  34. package/dist/server/server.d.ts.map +0 -1
  35. package/dist/server/server.js +0 -223
  36. package/dist/server/server.js.map +0 -1
  37. package/dist/server/sso.d.ts +0 -11
  38. package/dist/server/sso.d.ts.map +0 -1
  39. package/dist/server/sso.js +0 -428
  40. package/dist/server/sso.js.map +0 -1
  41. package/dist/server/state.d.ts +0 -137
  42. package/dist/server/state.d.ts.map +0 -1
  43. package/dist/server/state.js +0 -152
  44. package/dist/server/state.js.map +0 -1
  45. package/dist/server/vault.d.ts +0 -11
  46. package/dist/server/vault.d.ts.map +0 -1
  47. package/dist/server/vault.js +0 -92
  48. package/dist/server/vault.js.map +0 -1
  49. package/dist/server/workload.d.ts +0 -19
  50. package/dist/server/workload.d.ts.map +0 -1
  51. package/dist/server/workload.js +0 -226
  52. package/dist/server/workload.js.map +0 -1
  53. package/dist/sso/index.d.ts.map +0 -1
  54. package/dist/tenant/index.d.ts +0 -17
  55. package/dist/tenant/index.d.ts.map +0 -1
  56. package/dist/tenant/index.js +0 -300
  57. package/dist/tenant/index.js.map +0 -1
  58. package/dist/types.d.ts.map +0 -1
  59. package/dist/types.js +0 -2
  60. package/dist/types.js.map +0 -1
  61. package/dist/utils.d.ts +0 -75
  62. package/dist/utils.d.ts.map +0 -1
  63. package/dist/utils.js +0 -139
  64. package/dist/utils.js.map +0 -1
  65. package/dist/workload/index.d.ts.map +0 -1
@@ -1,223 +0,0 @@
1
- /**
2
- * ESV Mock Server
3
- *
4
- * A zero-dependency mock server that simulates Vault, SSO/IDP, IAM/SCIM,
5
- * and Workload Identity services for testing Enterprise Standard integrations.
6
- *
7
- * @example
8
- * ```typescript
9
- * import { startServer, stopServer } from '@enterprisestandard/esv/server';
10
- *
11
- * // Start before tests
12
- * await startServer();
13
- *
14
- * // Initialize with ES_VAULT_URL, ES_VAULT_TOKEN, and ES_VAULT_PATH set
15
- * const es = await enterpriseStandard(undefined, {
16
- * vaultUrl: process.env.ES_VAULT_URL,
17
- * vaultToken: process.env.ES_VAULT_TOKEN,
18
- * vaultPath: process.env.ES_VAULT_PATH,
19
- * });
20
- *
21
- * // Stop after tests
22
- * await stopServer();
23
- * ```
24
- */
25
- import { createServer } from 'node:http';
26
- import { initializeKeys } from './crypto.js';
27
- import { handleIamRequest } from './iam.js';
28
- import { handleSsoRequest } from './sso.js';
29
- import { resetState } from './state.js';
30
- import { handleVaultRequest } from './vault.js';
31
- import { handleWhoamiRequest, handleWorkloadRequest } from './workload.js';
32
- /**
33
- * Handle webhook requests for tenant status updates
34
- */
35
- async function handleWebhook(req, res) {
36
- if (req.method !== 'POST') {
37
- res.writeHead(405, { 'Content-Type': 'application/json' });
38
- res.end(JSON.stringify({ error: 'Method not allowed' }));
39
- return;
40
- }
41
- // Read request body
42
- let body = '';
43
- req.on('data', (chunk) => {
44
- body += chunk.toString();
45
- });
46
- req.on('end', () => {
47
- try {
48
- const payload = JSON.parse(body);
49
- // Just acknowledge the webhook - we don't need to do anything with it for testing
50
- res.writeHead(200, { 'Content-Type': 'application/json' });
51
- res.end(JSON.stringify({ received: true, payload }));
52
- }
53
- catch (_error) {
54
- res.writeHead(400, { 'Content-Type': 'application/json' });
55
- res.end(JSON.stringify({ error: 'Invalid JSON' }));
56
- }
57
- });
58
- }
59
- const DEFAULT_PORT = 3555;
60
- const DEFAULT_HOST = 'localhost';
61
- let server = null;
62
- let isStarted = false;
63
- /**
64
- * Request router
65
- */
66
- async function handleRequest(req, res, verbose) {
67
- const url = new URL(req.url || '/', `http://${req.headers.host}`);
68
- const pathname = url.pathname;
69
- if (verbose) {
70
- console.log(`[ESV Server] ${req.method} ${pathname}`);
71
- }
72
- // Add CORS headers for local development
73
- res.setHeader('Access-Control-Allow-Origin', '*');
74
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
75
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Vault-Token');
76
- // Handle preflight requests
77
- if (req.method === 'OPTIONS') {
78
- res.writeHead(204);
79
- res.end();
80
- return;
81
- }
82
- try {
83
- // Route to appropriate handler
84
- if (pathname.startsWith('/vault')) {
85
- handleVaultRequest(req, res, pathname);
86
- }
87
- else if (pathname.startsWith('/sso')) {
88
- await handleSsoRequest(req, res, pathname);
89
- }
90
- else if (pathname.startsWith('/iam')) {
91
- await handleIamRequest(req, res, pathname);
92
- }
93
- else if (pathname.startsWith('/workload')) {
94
- await handleWorkloadRequest(req, res, pathname);
95
- }
96
- else if (pathname === '/api/whoami') {
97
- // Mock whoami endpoint for testing workload authentication independently
98
- handleWhoamiRequest(req, res);
99
- }
100
- else if (pathname === '/webhook') {
101
- // Webhook endpoint for tenant status updates
102
- handleWebhook(req, res);
103
- }
104
- else if (pathname === '/health' || pathname === '/') {
105
- res.writeHead(200, { 'Content-Type': 'application/json' });
106
- res.end(JSON.stringify({ status: 'ok', service: 'esv-mock-server' }));
107
- }
108
- else {
109
- res.writeHead(404, { 'Content-Type': 'application/json' });
110
- res.end(JSON.stringify({ error: 'Not found' }));
111
- }
112
- }
113
- catch (error) {
114
- console.error('[ESV Server] Error handling request:', error);
115
- res.writeHead(500, { 'Content-Type': 'application/json' });
116
- res.end(JSON.stringify({ error: 'Internal server error' }));
117
- }
118
- }
119
- /**
120
- * Start the ESV mock server
121
- *
122
- * @param options - Server configuration options
123
- * @returns Promise that resolves when the server is listening
124
- */
125
- export function startServer(options = {}) {
126
- const { port = DEFAULT_PORT, host = DEFAULT_HOST, verbose = false } = options;
127
- if (isStarted && server) {
128
- if (verbose) {
129
- console.log('[ESV Server] Server already running');
130
- }
131
- return Promise.resolve();
132
- }
133
- return new Promise((resolve, reject) => {
134
- try {
135
- // Initialize cryptographic keys
136
- initializeKeys();
137
- // Reset state for fresh start
138
- resetState();
139
- server = createServer((req, res) => {
140
- handleRequest(req, res, verbose).catch((error) => {
141
- console.error('[ESV Server] Unhandled error:', error);
142
- if (!res.headersSent) {
143
- res.writeHead(500, { 'Content-Type': 'application/json' });
144
- res.end(JSON.stringify({ error: 'Internal server error' }));
145
- }
146
- });
147
- });
148
- server.on('error', (error) => {
149
- if (error.code === 'EADDRINUSE') {
150
- // Port already in use - maybe server is already running
151
- if (verbose) {
152
- console.log(`[ESV Server] Port ${port} already in use, assuming server is running`);
153
- }
154
- isStarted = true;
155
- resolve();
156
- }
157
- else {
158
- reject(error);
159
- }
160
- });
161
- server.listen(port, host, () => {
162
- isStarted = true;
163
- console.log(`[ESV Server] Running at http://${host}:${port}/`);
164
- console.log('[ESV Server] Endpoints:');
165
- console.log(` - Vault: http://${host}:${port}/vault/v1/secret/data/esv/config`);
166
- console.log(` - SSO: http://${host}:${port}/sso/*`);
167
- console.log(` - IAM: http://${host}:${port}/iam/*`);
168
- console.log(` - Workload: http://${host}:${port}/workload/*`);
169
- console.log(` - Whoami: http://${host}:${port}/api/whoami`);
170
- console.log(` - Webhook: http://${host}:${port}/webhook`);
171
- resolve();
172
- });
173
- }
174
- catch (error) {
175
- reject(error);
176
- }
177
- });
178
- }
179
- /**
180
- * Stop the ESV mock server
181
- *
182
- * @returns Promise that resolves when the server has stopped
183
- */
184
- export function stopServer() {
185
- return new Promise((resolve, reject) => {
186
- if (!server) {
187
- isStarted = false;
188
- resolve();
189
- return;
190
- }
191
- server.close((error) => {
192
- if (error) {
193
- // Ignore errors if server wasn't running
194
- if (error.code === 'ERR_SERVER_NOT_RUNNING') {
195
- server = null;
196
- isStarted = false;
197
- resolve();
198
- return;
199
- }
200
- reject(error);
201
- return;
202
- }
203
- server = null;
204
- isStarted = false;
205
- console.log('[ESV Server] Stopped');
206
- resolve();
207
- });
208
- });
209
- }
210
- /**
211
- * Check if the server is running
212
- */
213
- export function isServerRunning() {
214
- return isStarted;
215
- }
216
- /**
217
- * Get the server URL
218
- */
219
- export function getServerUrl(options = {}) {
220
- const { port = DEFAULT_PORT, host = DEFAULT_HOST } = options;
221
- return `http://${host}:${port}`;
222
- }
223
- //# sourceMappingURL=server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,YAAY,EAA0D,MAAM,WAAW,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE3E;;GAEG;AACH,KAAK,UAAU,aAAa,CAAC,GAAoB,EAAE,GAAmB;IACpE,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC1B,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC;QACzD,OAAO;IACT,CAAC;IAED,oBAAoB;IACpB,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;QACvB,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;QACjB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACjC,kFAAkF;YAClF,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;YAChB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,YAAY,GAAG,WAAW,CAAC;AAEjC,IAAI,MAAM,GAAkB,IAAI,CAAC;AACjC,IAAI,SAAS,GAAG,KAAK,CAAC;AAyBtB;;GAEG;AACH,KAAK,UAAU,aAAa,CAAC,GAAoB,EAAE,GAAmB,EAAE,OAAgB;IACtF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,UAAU,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAE9B,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,yCAAyC;IACzC,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAClD,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,wCAAwC,CAAC,CAAC;IACxF,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,4CAA4C,CAAC,CAAC;IAE5F,4BAA4B;IAC5B,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC7B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACnB,GAAG,CAAC,GAAG,EAAE,CAAC;QACV,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,+BAA+B;QAC/B,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClC,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACvC,MAAM,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC7C,CAAC;aAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACvC,MAAM,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC7C,CAAC;aAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5C,MAAM,qBAAqB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAClD,CAAC;aAAM,IAAI,QAAQ,KAAK,aAAa,EAAE,CAAC;YACtC,yEAAyE;YACzE,mBAAmB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAChC,CAAC;aAAM,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;YACnC,6CAA6C;YAC7C,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC1B,CAAC;aAAM,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;YACtD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC;QACxE,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAC;QAC7D,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,UAAyB,EAAE;IACrD,MAAM,EAAE,IAAI,GAAG,YAAY,EAAE,IAAI,GAAG,YAAY,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAE9E,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;QACxB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,CAAC;YACH,gCAAgC;YAChC,cAAc,EAAE,CAAC;YAEjB,8BAA8B;YAC9B,UAAU,EAAE,CAAC;YAEb,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;gBACjC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;oBAC/C,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;oBACtD,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;wBACrB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;wBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC,CAAC;oBAC9D,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAA4B,EAAE,EAAE;gBAClD,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;oBAChC,wDAAwD;oBACxD,IAAI,OAAO,EAAE,CAAC;wBACZ,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,6CAA6C,CAAC,CAAC;oBACtF,CAAC;oBACD,SAAS,GAAG,IAAI,CAAC;oBACjB,OAAO,EAAE,CAAC;gBACZ,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,KAAK,CAAC,CAAC;gBAChB,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;gBAC7B,SAAS,GAAG,IAAI,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,kCAAkC,IAAI,IAAI,IAAI,GAAG,CAAC,CAAC;gBAC/D,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;gBACvC,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,IAAI,IAAI,kCAAkC,CAAC,CAAC;gBACpF,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,IAAI,IAAI,QAAQ,CAAC,CAAC;gBAC1D,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,IAAI,IAAI,QAAQ,CAAC,CAAC;gBAC1D,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,IAAI,IAAI,aAAa,CAAC,CAAC;gBAC/D,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,IAAI,IAAI,aAAa,CAAC,CAAC;gBAC/D,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,IAAI,IAAI,UAAU,CAAC,CAAC;gBAC5D,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,SAAS,GAAG,KAAK,CAAC;YAClB,OAAO,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACrB,IAAI,KAAK,EAAE,CAAC;gBACV,yCAAyC;gBACzC,IAAK,KAA+B,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;oBACvE,MAAM,GAAG,IAAI,CAAC;oBACd,SAAS,GAAG,KAAK,CAAC;oBAClB,OAAO,EAAE,CAAC;oBACV,OAAO;gBACT,CAAC;gBACD,MAAM,CAAC,KAAK,CAAC,CAAC;gBACd,OAAO;YACT,CAAC;YAED,MAAM,GAAG,IAAI,CAAC;YACd,SAAS,GAAG,KAAK,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;YACpC,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,UAAyB,EAAE;IACtD,MAAM,EAAE,IAAI,GAAG,YAAY,EAAE,IAAI,GAAG,YAAY,EAAE,GAAG,OAAO,CAAC;IAC7D,OAAO,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC;AAClC,CAAC"}
@@ -1,11 +0,0 @@
1
- /**
2
- * SSO/OIDC endpoint handlers for the ESV mock server
3
- *
4
- * Implements a minimal OIDC provider for testing SSO integrations.
5
- */
6
- import type { IncomingMessage, ServerResponse } from 'node:http';
7
- /**
8
- * Handle SSO requests
9
- */
10
- export declare function handleSsoRequest(req: IncomingMessage, res: ServerResponse, pathname: string): Promise<void>;
11
- //# sourceMappingURL=sso.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sso.d.ts","sourceRoot":"","sources":["../../src/server/sso.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAqCjE;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDjH"}
@@ -1,428 +0,0 @@
1
- /**
2
- * SSO/OIDC endpoint handlers for the ESV mock server
3
- *
4
- * Implements a minimal OIDC provider for testing SSO integrations.
5
- */
6
- import { generateRandomString, generateUUID, getJwks, signJwt, verifyCodeChallenge } from './crypto.js';
7
- import { createSession, deleteAuthCode, deleteRefreshToken, deleteSession, getAuthCode, getRefreshToken, getTestUser, storeAuthCode, storeRefreshToken, } from './state.js';
8
- const ISSUER = 'http://localhost:3555/sso';
9
- const TOKEN_EXPIRY = 3600; // 1 hour
10
- const REFRESH_EXPIRY = 86400; // 24 hours
11
- const CODE_EXPIRY = 300; // 5 minutes
12
- /**
13
- * Parse request body as URL-encoded form data
14
- */
15
- async function parseFormBody(req) {
16
- return new Promise((resolve, reject) => {
17
- let body = '';
18
- req.on('data', (chunk) => {
19
- body += chunk.toString();
20
- });
21
- req.on('end', () => {
22
- resolve(new URLSearchParams(body));
23
- });
24
- req.on('error', reject);
25
- });
26
- }
27
- /**
28
- * Handle SSO requests
29
- */
30
- export async function handleSsoRequest(req, res, pathname) {
31
- // Remove /sso prefix
32
- const ssoPath = pathname.replace(/^\/sso/, '');
33
- // GET /sso/authorize - Authorization endpoint
34
- if (req.method === 'GET' && ssoPath === '/authorize') {
35
- await handleAuthorize(req, res);
36
- return;
37
- }
38
- // POST /sso/token - Token endpoint
39
- if (req.method === 'POST' && ssoPath === '/token') {
40
- await handleToken(req, res);
41
- return;
42
- }
43
- // GET /sso/certs - JWKS endpoint
44
- if (req.method === 'GET' && ssoPath === '/certs') {
45
- handleCerts(res);
46
- return;
47
- }
48
- // POST /sso/revoke - Token revocation
49
- if (req.method === 'POST' && ssoPath === '/revoke') {
50
- await handleRevoke(req, res);
51
- return;
52
- }
53
- // GET /sso/userinfo - User info endpoint
54
- if (req.method === 'GET' && ssoPath === '/userinfo') {
55
- handleUserInfo(req, res);
56
- return;
57
- }
58
- // GET /sso/logout - End session endpoint
59
- if (req.method === 'GET' && ssoPath === '/logout') {
60
- handleLogout(req, res);
61
- return;
62
- }
63
- // POST /sso/logout - Back-channel logout
64
- if (req.method === 'POST' && ssoPath === '/logout/backchannel') {
65
- await handleBackChannelLogout(req, res);
66
- return;
67
- }
68
- // GET /sso/.well-known/openid-configuration
69
- if (req.method === 'GET' && ssoPath === '/.well-known/openid-configuration') {
70
- handleOpenIDConfig(res);
71
- return;
72
- }
73
- res.writeHead(404, { 'Content-Type': 'application/json' });
74
- res.end(JSON.stringify({ error: 'not_found' }));
75
- }
76
- /**
77
- * Authorization endpoint - issues authorization codes
78
- *
79
- * In a real implementation, this would show a login form.
80
- * For testing, we auto-authenticate with the test user.
81
- */
82
- async function handleAuthorize(req, res) {
83
- const url = new URL(req.url || '', `http://${req.headers.host}`);
84
- const params = url.searchParams;
85
- const clientId = params.get('client_id');
86
- const redirectUri = params.get('redirect_uri');
87
- const responseType = params.get('response_type');
88
- const scope = params.get('scope') || 'openid';
89
- const state = params.get('state');
90
- const codeChallenge = params.get('code_challenge');
91
- const codeChallengeMethod = params.get('code_challenge_method');
92
- // Validate required parameters
93
- if (!clientId || !redirectUri || !responseType) {
94
- res.writeHead(400, { 'Content-Type': 'application/json' });
95
- res.end(JSON.stringify({
96
- error: 'invalid_request',
97
- error_description: 'Missing required parameters',
98
- }));
99
- return;
100
- }
101
- if (responseType !== 'code') {
102
- res.writeHead(400, { 'Content-Type': 'application/json' });
103
- res.end(JSON.stringify({
104
- error: 'unsupported_response_type',
105
- error_description: 'Only code response type is supported',
106
- }));
107
- return;
108
- }
109
- // Generate authorization code
110
- const testUser = getTestUser();
111
- const code = generateRandomString(32);
112
- const authCode = {
113
- code,
114
- userId: testUser.id,
115
- clientId,
116
- redirectUri,
117
- scope,
118
- codeChallenge: codeChallenge || undefined,
119
- codeChallengeMethod: codeChallengeMethod || undefined,
120
- state: state || undefined,
121
- createdAt: new Date(),
122
- expiresAt: new Date(Date.now() + CODE_EXPIRY * 1000),
123
- };
124
- storeAuthCode(authCode);
125
- // Redirect back with code
126
- const redirectUrl = new URL(redirectUri);
127
- redirectUrl.searchParams.set('code', code);
128
- if (state) {
129
- redirectUrl.searchParams.set('state', state);
130
- }
131
- res.writeHead(302, { Location: redirectUrl.toString() });
132
- res.end();
133
- }
134
- /**
135
- * Token endpoint - exchanges codes for tokens or refreshes tokens
136
- */
137
- async function handleToken(req, res) {
138
- const body = await parseFormBody(req);
139
- const grantType = body.get('grant_type');
140
- if (grantType === 'authorization_code') {
141
- await handleAuthorizationCodeGrant(body, res);
142
- }
143
- else if (grantType === 'refresh_token') {
144
- await handleRefreshTokenGrant(body, res);
145
- }
146
- else {
147
- res.writeHead(400, { 'Content-Type': 'application/json' });
148
- res.end(JSON.stringify({
149
- error: 'unsupported_grant_type',
150
- error_description: 'Only authorization_code and refresh_token are supported',
151
- }));
152
- }
153
- }
154
- /**
155
- * Handle authorization code exchange
156
- */
157
- async function handleAuthorizationCodeGrant(body, res) {
158
- const code = body.get('code');
159
- const redirectUri = body.get('redirect_uri');
160
- const codeVerifier = body.get('code_verifier');
161
- if (!code || !redirectUri) {
162
- res.writeHead(400, { 'Content-Type': 'application/json' });
163
- res.end(JSON.stringify({
164
- error: 'invalid_request',
165
- error_description: 'Missing code or redirect_uri',
166
- }));
167
- return;
168
- }
169
- const authCode = getAuthCode(code);
170
- if (!authCode) {
171
- res.writeHead(400, { 'Content-Type': 'application/json' });
172
- res.end(JSON.stringify({
173
- error: 'invalid_grant',
174
- error_description: 'Invalid or expired authorization code',
175
- }));
176
- return;
177
- }
178
- // Validate redirect URI
179
- if (authCode.redirectUri !== redirectUri) {
180
- res.writeHead(400, { 'Content-Type': 'application/json' });
181
- res.end(JSON.stringify({
182
- error: 'invalid_grant',
183
- error_description: 'Redirect URI mismatch',
184
- }));
185
- return;
186
- }
187
- // Validate PKCE if present
188
- if (authCode.codeChallenge && authCode.codeChallengeMethod === 'S256') {
189
- if (!codeVerifier || !verifyCodeChallenge(codeVerifier, authCode.codeChallenge)) {
190
- res.writeHead(400, { 'Content-Type': 'application/json' });
191
- res.end(JSON.stringify({
192
- error: 'invalid_grant',
193
- error_description: 'Invalid code verifier',
194
- }));
195
- return;
196
- }
197
- }
198
- // Delete used code
199
- deleteAuthCode(code);
200
- // Create session
201
- const sessionId = generateUUID();
202
- createSession({
203
- id: sessionId,
204
- userId: authCode.userId,
205
- createdAt: new Date(),
206
- lastActivityAt: new Date(),
207
- });
208
- // Generate tokens
209
- const testUser = getTestUser();
210
- const tokens = generateTokens(testUser, authCode.clientId, authCode.scope, sessionId);
211
- res.writeHead(200, { 'Content-Type': 'application/json' });
212
- res.end(JSON.stringify(tokens));
213
- }
214
- /**
215
- * Handle refresh token grant
216
- */
217
- async function handleRefreshTokenGrant(body, res) {
218
- const refreshToken = body.get('refresh_token');
219
- if (!refreshToken) {
220
- res.writeHead(400, { 'Content-Type': 'application/json' });
221
- res.end(JSON.stringify({
222
- error: 'invalid_request',
223
- error_description: 'Missing refresh_token',
224
- }));
225
- return;
226
- }
227
- const tokenData = getRefreshToken(refreshToken);
228
- if (!tokenData || tokenData.expiresAt < new Date()) {
229
- res.writeHead(400, { 'Content-Type': 'application/json' });
230
- res.end(JSON.stringify({
231
- error: 'invalid_grant',
232
- error_description: 'Invalid or expired refresh token',
233
- }));
234
- return;
235
- }
236
- // Delete old refresh token (rotation)
237
- deleteRefreshToken(refreshToken);
238
- // Generate new tokens
239
- const testUser = getTestUser();
240
- const tokens = generateTokens(testUser, tokenData.clientId, tokenData.scope, tokenData.sessionId);
241
- res.writeHead(200, { 'Content-Type': 'application/json' });
242
- res.end(JSON.stringify(tokens));
243
- }
244
- /**
245
- * Generate access token, ID token, and refresh token
246
- */
247
- function generateTokens(user, clientId, scope, sessionId) {
248
- const now = Math.floor(Date.now() / 1000);
249
- // ID Token claims
250
- const idTokenClaims = {
251
- iss: ISSUER,
252
- sub: user.id,
253
- aud: clientId,
254
- nonce: generateRandomString(16),
255
- sid: sessionId,
256
- auth_time: now,
257
- email: user.email,
258
- email_verified: true,
259
- name: user.name,
260
- given_name: user.givenName,
261
- family_name: user.familyName,
262
- preferred_username: user.userName,
263
- picture: user.picture,
264
- };
265
- // Access token claims
266
- const accessTokenClaims = {
267
- iss: ISSUER,
268
- sub: user.id,
269
- aud: clientId,
270
- scope,
271
- sid: sessionId,
272
- };
273
- const idToken = signJwt(idTokenClaims, TOKEN_EXPIRY);
274
- const accessToken = signJwt(accessTokenClaims, TOKEN_EXPIRY);
275
- const refreshTokenValue = generateRandomString(64);
276
- // Store refresh token
277
- const refreshToken = {
278
- token: refreshTokenValue,
279
- userId: user.id,
280
- clientId,
281
- scope,
282
- sessionId,
283
- createdAt: new Date(),
284
- expiresAt: new Date(Date.now() + REFRESH_EXPIRY * 1000),
285
- };
286
- storeRefreshToken(refreshToken);
287
- return {
288
- access_token: accessToken,
289
- token_type: 'Bearer',
290
- expires_in: TOKEN_EXPIRY,
291
- refresh_token: refreshTokenValue,
292
- refresh_expires_in: REFRESH_EXPIRY,
293
- id_token: idToken,
294
- scope,
295
- session_state: sessionId,
296
- };
297
- }
298
- /**
299
- * JWKS endpoint
300
- */
301
- function handleCerts(res) {
302
- const jwks = getJwks();
303
- res.writeHead(200, { 'Content-Type': 'application/json' });
304
- res.end(JSON.stringify(jwks));
305
- }
306
- /**
307
- * Token revocation endpoint
308
- */
309
- async function handleRevoke(req, res) {
310
- const body = await parseFormBody(req);
311
- const token = body.get('token');
312
- const tokenTypeHint = body.get('token_type_hint');
313
- if (token) {
314
- if (tokenTypeHint === 'refresh_token' || !tokenTypeHint) {
315
- deleteRefreshToken(token);
316
- }
317
- // Access tokens are stateless, can't be revoked
318
- }
319
- res.writeHead(200, { 'Content-Type': 'application/json' });
320
- res.end(JSON.stringify({ success: true }));
321
- }
322
- /**
323
- * User info endpoint
324
- */
325
- function handleUserInfo(req, res) {
326
- // Extract and validate token
327
- const authHeader = req.headers.authorization;
328
- if (!authHeader?.startsWith('Bearer ')) {
329
- res.writeHead(401, { 'Content-Type': 'application/json' });
330
- res.end(JSON.stringify({ error: 'invalid_token' }));
331
- return;
332
- }
333
- // For simplicity, we don't validate the token here - just return test user
334
- const user = getTestUser();
335
- res.writeHead(200, { 'Content-Type': 'application/json' });
336
- res.end(JSON.stringify({
337
- sub: user.id,
338
- email: user.email,
339
- email_verified: true,
340
- name: user.name,
341
- given_name: user.givenName,
342
- family_name: user.familyName,
343
- preferred_username: user.userName,
344
- picture: user.picture,
345
- }));
346
- }
347
- /**
348
- * End session / logout endpoint
349
- */
350
- function handleLogout(req, res) {
351
- const url = new URL(req.url || '', `http://${req.headers.host}`);
352
- const postLogoutRedirectUri = url.searchParams.get('post_logout_redirect_uri');
353
- if (postLogoutRedirectUri) {
354
- res.writeHead(302, { Location: postLogoutRedirectUri });
355
- res.end();
356
- }
357
- else {
358
- res.writeHead(200, { 'Content-Type': 'text/html' });
359
- res.end('<html><body><h1>Logged out</h1></body></html>');
360
- }
361
- }
362
- /**
363
- * Back-channel logout endpoint
364
- */
365
- async function handleBackChannelLogout(req, res) {
366
- const body = await parseFormBody(req);
367
- const logoutToken = body.get('logout_token');
368
- if (!logoutToken) {
369
- res.writeHead(400, { 'Content-Type': 'application/json' });
370
- res.end(JSON.stringify({ error: 'invalid_request', error_description: 'Missing logout_token' }));
371
- return;
372
- }
373
- // Parse the logout token to get sid
374
- try {
375
- const parts = logoutToken.split('.');
376
- if (parts.length >= 2) {
377
- const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));
378
- if (payload.sid) {
379
- deleteSession(payload.sid);
380
- }
381
- }
382
- }
383
- catch {
384
- // Ignore parsing errors
385
- }
386
- res.writeHead(200, { 'Content-Type': 'application/json' });
387
- res.end(JSON.stringify({ success: true }));
388
- }
389
- /**
390
- * OpenID Configuration endpoint
391
- */
392
- function handleOpenIDConfig(res) {
393
- res.writeHead(200, { 'Content-Type': 'application/json' });
394
- res.end(JSON.stringify({
395
- issuer: ISSUER,
396
- authorization_endpoint: `${ISSUER}/authorize`,
397
- token_endpoint: `${ISSUER}/token`,
398
- userinfo_endpoint: `${ISSUER}/userinfo`,
399
- jwks_uri: `${ISSUER}/certs`,
400
- end_session_endpoint: `${ISSUER}/logout`,
401
- revocation_endpoint: `${ISSUER}/revoke`,
402
- response_types_supported: ['code'],
403
- subject_types_supported: ['public'],
404
- id_token_signing_alg_values_supported: ['RS256'],
405
- scopes_supported: ['openid', 'profile', 'email'],
406
- token_endpoint_auth_methods_supported: ['client_secret_post', 'client_secret_basic'],
407
- claims_supported: [
408
- 'sub',
409
- 'iss',
410
- 'aud',
411
- 'exp',
412
- 'iat',
413
- 'nonce',
414
- 'email',
415
- 'email_verified',
416
- 'name',
417
- 'given_name',
418
- 'family_name',
419
- 'preferred_username',
420
- 'picture',
421
- 'sid',
422
- ],
423
- code_challenge_methods_supported: ['S256'],
424
- backchannel_logout_supported: true,
425
- backchannel_logout_session_supported: true,
426
- }));
427
- }
428
- //# sourceMappingURL=sso.js.map