@enterprisestandard/esv 0.0.5-beta.20260115.2 → 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,433 +1,423 @@
1
- /**
2
- * Workload Validation Tests
3
- *
4
- * These tests validate that an application correctly implements
5
- * Enterprise Standard Workload Identity authentication.
6
- */
7
- import { assert, assertValid, createFetcher, runTest, skipTest } from '../utils';
8
- /**
9
- * Gets a workload token response validator (workload tokens don't have id_token, only access_token)
10
- * This is different from OIDC tokenResponse which includes id_token
11
- */
12
- function getWorkloadTokenResponseValidator() {
13
- return {
14
- '~standard': {
15
- validate: (value) => {
16
- if (typeof value !== 'object' || value === null) {
17
- return {
18
- issues: [{ message: `Expected object, got ${typeof value}` }],
19
- };
20
- }
21
- const token = value;
22
- const issues = [];
23
- if (typeof token.access_token !== 'string') {
24
- issues.push({ message: 'Expected access_token to be string', path: ['access_token'] });
25
- }
26
- if (typeof token.token_type !== 'string') {
27
- issues.push({ message: 'Expected token_type to be string', path: ['token_type'] });
28
- }
29
- // expires_in is optional but if present should be a number
30
- if (token.expires_in !== undefined && typeof token.expires_in !== 'number') {
31
- issues.push({ message: 'Expected expires_in to be number or undefined', path: ['expires_in'] });
32
- }
33
- // scope is optional but if present should be a string
34
- if (token.scope !== undefined && typeof token.scope !== 'string') {
35
- issues.push({ message: 'Expected scope to be string or undefined', path: ['scope'] });
36
- }
37
- if (issues.length > 0) {
38
- return { issues };
39
- }
40
- return { value };
41
- },
1
+ // src/utils.ts
2
+ function createFetcher(config) {
3
+ const timeout = config.timeout ?? 5e3;
4
+ const headers = config.headers ?? {};
5
+ return async function fetcher(path, options = {}) {
6
+ const url = `${config.baseUrl}${path}`;
7
+ const controller = new AbortController();
8
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
9
+ try {
10
+ const response = await fetch(url, {
11
+ ...options,
12
+ signal: controller.signal,
13
+ headers: {
14
+ ...headers,
15
+ ...options.headers
42
16
  },
43
- };
17
+ redirect: "manual"
18
+ // Don't follow redirects automatically
19
+ });
20
+ return response;
21
+ } finally {
22
+ clearTimeout(timeoutId);
23
+ }
24
+ };
44
25
  }
45
- /**
46
- * Gets a JWKS key validator (simple inline validator for standard JWKS key structure)
47
- */
48
- function getJwksKeyValidator() {
49
- // JWKS keys have a standard structure - create a simple validator inline
50
- // This validates the minimal required fields: kty and kid
26
+ async function runTest(name, testFn) {
27
+ const start = performance.now();
28
+ try {
29
+ const result = await testFn();
51
30
  return {
52
- '~standard': {
53
- validate: (value) => {
54
- if (typeof value !== 'object' || value === null) {
55
- return {
56
- issues: [{ message: `Expected object, got ${typeof value}` }],
57
- };
58
- }
59
- const key = value;
60
- const issues = [];
61
- if (typeof key.kty !== 'string') {
62
- issues.push({ message: 'Expected kty to be string', path: ['kty'] });
63
- }
64
- if (typeof key.kid !== 'string') {
65
- issues.push({ message: 'Expected kid to be string', path: ['kid'] });
66
- }
67
- if (issues.length > 0) {
68
- return { issues };
69
- }
70
- return { value };
71
- },
72
- },
31
+ name,
32
+ passed: true,
33
+ duration: performance.now() - start,
34
+ details: result?.details
35
+ };
36
+ } catch (error) {
37
+ return {
38
+ name,
39
+ passed: false,
40
+ error: error instanceof Error ? error.message : String(error),
41
+ duration: performance.now() - start
73
42
  };
43
+ }
44
+ }
45
+ function skipTest(name, reason) {
46
+ return {
47
+ name,
48
+ passed: true,
49
+ duration: 0,
50
+ details: { skipped: true, reason }
51
+ };
52
+ }
53
+ function assert(condition, message) {
54
+ if (!condition) {
55
+ throw new Error(message);
56
+ }
57
+ }
58
+ function assertValid(data, validator, message) {
59
+ const result = validator["~standard"].validate(data);
60
+ if (result instanceof Promise) {
61
+ throw new Error(
62
+ message ?? "Async validators are not supported in assertValid. Use the validator directly with await."
63
+ );
64
+ }
65
+ if ("issues" in result) {
66
+ const issues = result.issues;
67
+ const errorMessages = issues.map((issue) => {
68
+ const path = issue.path ? issue.path.map(String).join(".") : "";
69
+ return path ? `${path}: ${issue.message}` : issue.message;
70
+ });
71
+ throw new Error(message ?? `Validation failed: ${errorMessages.join("; ")}`);
72
+ }
73
+ }
74
+
75
+ // src/workload/index.ts
76
+ function getWorkloadTokenResponseValidator() {
77
+ return {
78
+ "~standard": {
79
+ validate: (value) => {
80
+ if (typeof value !== "object" || value === null) {
81
+ return {
82
+ issues: [{ message: `Expected object, got ${typeof value}` }]
83
+ };
84
+ }
85
+ const token = value;
86
+ const issues = [];
87
+ if (typeof token.access_token !== "string") {
88
+ issues.push({ message: "Expected access_token to be string", path: ["access_token"] });
89
+ }
90
+ if (typeof token.token_type !== "string") {
91
+ issues.push({ message: "Expected token_type to be string", path: ["token_type"] });
92
+ }
93
+ if (token.expires_in !== void 0 && typeof token.expires_in !== "number") {
94
+ issues.push({ message: "Expected expires_in to be number or undefined", path: ["expires_in"] });
95
+ }
96
+ if (token.scope !== void 0 && typeof token.scope !== "string") {
97
+ issues.push({ message: "Expected scope to be string or undefined", path: ["scope"] });
98
+ }
99
+ if (issues.length > 0) {
100
+ return { issues };
101
+ }
102
+ return { value };
103
+ }
104
+ }
105
+ };
106
+ }
107
+ function getJwksKeyValidator() {
108
+ return {
109
+ "~standard": {
110
+ validate: (value) => {
111
+ if (typeof value !== "object" || value === null) {
112
+ return {
113
+ issues: [{ message: `Expected object, got ${typeof value}` }]
114
+ };
115
+ }
116
+ const key = value;
117
+ const issues = [];
118
+ if (typeof key.kty !== "string") {
119
+ issues.push({ message: "Expected kty to be string", path: ["kty"] });
120
+ }
121
+ if (typeof key.kid !== "string") {
122
+ issues.push({ message: "Expected kid to be string", path: ["kid"] });
123
+ }
124
+ if (issues.length > 0) {
125
+ return { issues };
126
+ }
127
+ return { value };
128
+ }
129
+ }
130
+ };
74
131
  }
75
- /**
76
- * Gets a token validation result validator (simple inline validator)
77
- */
78
132
  function getTokenValidationResultValidator() {
133
+ return {
134
+ "~standard": {
135
+ validate: (value) => {
136
+ if (typeof value !== "object" || value === null) {
137
+ return {
138
+ issues: [{ message: `Expected object, got ${typeof value}` }]
139
+ };
140
+ }
141
+ const result = value;
142
+ const issues = [];
143
+ if (typeof result.valid !== "boolean") {
144
+ issues.push({ message: "Expected valid to be boolean", path: ["valid"] });
145
+ }
146
+ if (issues.length > 0) {
147
+ return { issues };
148
+ }
149
+ return { value };
150
+ }
151
+ }
152
+ };
153
+ }
154
+ var DEFAULT_CONFIG = {
155
+ tokenPath: "/api/workload/token",
156
+ validatePath: "/api/workload/validate",
157
+ jwksPath: "/api/workload/jwks",
158
+ refreshPath: "/api/workload/refresh",
159
+ esvUrl: "http://localhost:3555"
160
+ };
161
+ async function testJwksEndpoint(config, fetch2) {
162
+ return runTest("Workload JWKS Endpoint", async () => {
163
+ const response = await fetch2(config.jwksPath);
164
+ assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
165
+ const data = await response.json();
166
+ assert(Array.isArray(data.keys), "JWKS response missing keys array");
167
+ const keyValidator = getJwksKeyValidator();
168
+ for (const key of data.keys) {
169
+ assertValid(key, keyValidator);
170
+ }
79
171
  return {
80
- '~standard': {
81
- validate: (value) => {
82
- if (typeof value !== 'object' || value === null) {
83
- return {
84
- issues: [{ message: `Expected object, got ${typeof value}` }],
85
- };
86
- }
87
- const result = value;
88
- const issues = [];
89
- if (typeof result.valid !== 'boolean') {
90
- issues.push({ message: 'Expected valid to be boolean', path: ['valid'] });
91
- }
92
- if (issues.length > 0) {
93
- return { issues };
94
- }
95
- return { value };
96
- },
97
- },
172
+ details: {
173
+ keyCount: data.keys.length,
174
+ algorithms: data.keys.map((k) => k.alg).filter(Boolean)
175
+ }
98
176
  };
177
+ });
99
178
  }
100
- /**
101
- * Default configuration for Workload validation
102
- */
103
- const DEFAULT_CONFIG = {
104
- tokenPath: '/api/workload/token',
105
- validatePath: '/api/workload/validate',
106
- jwksPath: '/api/workload/jwks',
107
- refreshPath: '/api/workload/refresh',
108
- esvUrl: 'http://localhost:3555',
109
- };
110
- /**
111
- * Test: JWKS endpoint returns valid JWKS structure
112
- */
113
- async function testJwksEndpoint(config, fetch) {
114
- return runTest('Workload JWKS Endpoint', async () => {
115
- const response = await fetch(config.jwksPath);
116
- // Should return 200 OK
117
- assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
118
- const data = await response.json();
119
- // Should have JWKS structure
120
- assert(Array.isArray(data.keys), 'JWKS response missing keys array');
121
- // Each key should have required fields - validate using validator
122
- const keyValidator = getJwksKeyValidator();
123
- for (const key of data.keys) {
124
- assertValid(key, keyValidator);
179
+ async function testTokenEndpoint(config, fetch2) {
180
+ const result = await runTest("Workload Token Endpoint", async () => {
181
+ const url = config.testScopes ? `${config.tokenPath}?scope=${encodeURIComponent(config.testScopes)}` : config.tokenPath;
182
+ const response = await fetch2(url);
183
+ if (response.status === 503) {
184
+ return {
185
+ details: {
186
+ skipped: true,
187
+ reason: "Workload authentication not configured"
125
188
  }
126
- return {
127
- details: {
128
- keyCount: data.keys.length,
129
- algorithms: data.keys.map((k) => k.alg).filter(Boolean),
130
- },
131
- };
132
- });
189
+ };
190
+ }
191
+ assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
192
+ const data = await response.json();
193
+ const validator = getWorkloadTokenResponseValidator();
194
+ assertValid(data, validator);
195
+ return {
196
+ details: {
197
+ tokenType: data.token_type,
198
+ hasToken: !!data.access_token,
199
+ token: data.access_token
200
+ }
201
+ };
202
+ });
203
+ return {
204
+ ...result,
205
+ token: result.details?.token
206
+ };
133
207
  }
134
- /**
135
- * Test: Token endpoint returns access token
136
- */
137
- async function testTokenEndpoint(config, fetch) {
138
- const result = await runTest('Workload Token Endpoint', async () => {
139
- const url = config.testScopes
140
- ? `${config.tokenPath}?scope=${encodeURIComponent(config.testScopes)}`
141
- : config.tokenPath;
142
- const response = await fetch(url);
143
- // Should return 200 OK (or 503 if workload not configured)
144
- if (response.status === 503) {
145
- return {
146
- details: {
147
- skipped: true,
148
- reason: 'Workload authentication not configured',
149
- },
150
- };
151
- }
152
- assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
153
- const data = await response.json();
154
- // Should have token response structure - validate using workload-specific validator
155
- // (workload tokens don't have id_token, only access_token)
156
- const validator = getWorkloadTokenResponseValidator();
157
- assertValid(data, validator);
158
- return {
159
- details: {
160
- tokenType: data.token_type,
161
- hasToken: !!data.access_token,
162
- token: data.access_token,
163
- },
164
- };
208
+ async function testValidateEndpoint(config, fetch2, token) {
209
+ return runTest("Workload Validate Endpoint", async () => {
210
+ const response = await fetch2(config.validatePath, {
211
+ method: "POST",
212
+ headers: {
213
+ Authorization: `Bearer ${token}`
214
+ }
165
215
  });
216
+ assert(response.status === 200, `Expected 200 OK for valid token, got ${response.status}`);
217
+ const data = await response.json();
218
+ const validationResultValidator = getTokenValidationResultValidator();
219
+ assertValid(data, validationResultValidator);
220
+ assert(data.valid === true, "Token should be valid");
166
221
  return {
167
- ...result,
168
- token: result.details?.token,
222
+ details: {
223
+ valid: data.valid,
224
+ claims: data.claims
225
+ }
169
226
  };
227
+ });
170
228
  }
171
- /**
172
- * Test: Token validation endpoint works
173
- */
174
- async function testValidateEndpoint(config, fetch, token) {
175
- return runTest('Workload Validate Endpoint', async () => {
176
- const response = await fetch(config.validatePath, {
177
- method: 'POST',
178
- headers: {
179
- Authorization: `Bearer ${token}`,
180
- },
181
- });
182
- // Should return 200 OK for valid token
183
- assert(response.status === 200, `Expected 200 OK for valid token, got ${response.status}`);
184
- const data = await response.json();
185
- // Should have validation result structure - validate using validator
186
- const validationResultValidator = getTokenValidationResultValidator();
187
- assertValid(data, validationResultValidator);
188
- assert(data.valid === true, 'Token should be valid');
189
- return {
190
- details: {
191
- valid: data.valid,
192
- claims: data.claims,
193
- },
194
- };
229
+ async function testValidateEndpointInvalid(config, fetch2) {
230
+ return runTest("Workload Validate Endpoint (Invalid Token)", async () => {
231
+ const response = await fetch2(config.validatePath, {
232
+ method: "POST",
233
+ headers: {
234
+ Authorization: "Bearer invalid.token.here"
235
+ }
195
236
  });
237
+ assert(response.status === 401, `Expected 401 Unauthorized for invalid token, got ${response.status}`);
238
+ const data = await response.json();
239
+ const validationResultValidator = getTokenValidationResultValidator();
240
+ assertValid(data, validationResultValidator);
241
+ assert(data.valid === false, "Invalid token should not validate");
242
+ return {
243
+ details: {
244
+ valid: data.valid,
245
+ error: data.error
246
+ }
247
+ };
248
+ });
196
249
  }
197
- /**
198
- * Test: Token validation rejects invalid tokens
199
- */
200
- async function testValidateEndpointInvalid(config, fetch) {
201
- return runTest('Workload Validate Endpoint (Invalid Token)', async () => {
202
- const response = await fetch(config.validatePath, {
203
- method: 'POST',
204
- headers: {
205
- Authorization: 'Bearer invalid.token.here',
206
- },
207
- });
208
- // Should return 401 for invalid token
209
- assert(response.status === 401, `Expected 401 Unauthorized for invalid token, got ${response.status}`);
210
- const data = await response.json();
211
- // Should indicate token is invalid - validate using validator
212
- const validationResultValidator = getTokenValidationResultValidator();
213
- assertValid(data, validationResultValidator);
214
- assert(data.valid === false, 'Invalid token should not validate');
215
- return {
216
- details: {
217
- valid: data.valid,
218
- error: data.error,
219
- },
220
- };
250
+ async function testValidateEndpointNoAuth(config, fetch2) {
251
+ return runTest("Workload Validate Endpoint (No Auth)", async () => {
252
+ const response = await fetch2(config.validatePath, {
253
+ method: "POST"
221
254
  });
255
+ assert(response.status === 401, `Expected 401 Unauthorized for missing auth, got ${response.status}`);
256
+ return {
257
+ details: {
258
+ status: response.status,
259
+ requiresAuth: true
260
+ }
261
+ };
262
+ });
222
263
  }
223
- /**
224
- * Test: Token validation rejects missing authorization header
225
- */
226
- async function testValidateEndpointNoAuth(config, fetch) {
227
- return runTest('Workload Validate Endpoint (No Auth)', async () => {
228
- const response = await fetch(config.validatePath, {
229
- method: 'POST',
230
- });
231
- // Should return 401 for missing auth
232
- assert(response.status === 401, `Expected 401 Unauthorized for missing auth, got ${response.status}`);
233
- return {
234
- details: {
235
- status: response.status,
236
- requiresAuth: true,
237
- },
238
- };
264
+ async function testRefreshEndpoint(config, fetch2) {
265
+ return runTest("Workload Refresh Endpoint", async () => {
266
+ const response = await fetch2(config.refreshPath, {
267
+ method: "POST"
239
268
  });
240
- }
241
- /**
242
- * Test: Refresh endpoint works
243
- */
244
- async function testRefreshEndpoint(config, fetch) {
245
- return runTest('Workload Refresh Endpoint', async () => {
246
- const response = await fetch(config.refreshPath, {
247
- method: 'POST',
248
- });
249
- // Should return 200 OK or 503 if not configured
250
- if (response.status === 503) {
251
- return {
252
- details: {
253
- skipped: true,
254
- reason: 'Workload authentication not configured',
255
- },
256
- };
269
+ if (response.status === 503) {
270
+ return {
271
+ details: {
272
+ skipped: true,
273
+ reason: "Workload authentication not configured"
257
274
  }
258
- assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
259
- const data = await response.json();
260
- // Should have token response structure - validate using workload-specific validator
261
- // (workload tokens don't have id_token, only access_token)
262
- const validator = getWorkloadTokenResponseValidator();
263
- assertValid(data, validator);
264
- return {
265
- details: {
266
- tokenType: data.token_type,
267
- hasToken: !!data.access_token,
268
- },
269
- };
270
- });
275
+ };
276
+ }
277
+ assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
278
+ const data = await response.json();
279
+ const validator = getWorkloadTokenResponseValidator();
280
+ assertValid(data, validator);
281
+ return {
282
+ details: {
283
+ tokenType: data.token_type,
284
+ hasToken: !!data.access_token
285
+ }
286
+ };
287
+ });
271
288
  }
272
- /**
273
- * Test: Whoami endpoint with workload authentication
274
- *
275
- * This test calls the ESV mock server's /api/whoami endpoint to validate
276
- * that the workload token issued by the application is valid and can be
277
- * used for cross-service authentication. This allows each application to
278
- * be tested independently without requiring other applications to be running.
279
- */
280
289
  async function testWhoamiWithWorkload(config, token) {
281
- return runTest('Workload Authentication (Whoami)', async () => {
282
- // Call the ESV mock server's whoami endpoint with the workload token
283
- const esvWhoamiUrl = `${config.esvUrl}/api/whoami`;
284
- const response = await globalThis.fetch(esvWhoamiUrl, {
285
- headers: {
286
- Authorization: `Bearer ${token}`,
287
- },
288
- });
289
- // Should return 200 OK
290
- if (response.status === 404) {
291
- return {
292
- details: {
293
- skipped: true,
294
- reason: 'ESV whoami endpoint not available',
295
- },
296
- };
297
- }
298
- assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
299
- const data = await response.json();
300
- // Should have workload identity in response
301
- assert(data.workload !== undefined, 'Response should include workload identity');
302
- return {
303
- details: {
304
- workloadId: data.workload?.workload_id,
305
- clientId: data.workload?.client_id,
306
- },
307
- };
290
+ return runTest("Workload Authentication (Whoami)", async () => {
291
+ const esvWhoamiUrl = `${config.esvUrl}/api/whoami`;
292
+ const response = await globalThis.fetch(esvWhoamiUrl, {
293
+ headers: {
294
+ Authorization: `Bearer ${token}`
295
+ }
308
296
  });
309
- }
310
- /**
311
- * Runs all Workload validation tests
312
- */
313
- export async function validateWorkload(config) {
314
- const startTime = performance.now();
315
- const mergedConfig = { ...DEFAULT_CONFIG, ...config };
316
- const fetch = createFetcher(mergedConfig);
317
- const tests = [];
318
- // Test JWKS endpoint
319
- tests.push(await testJwksEndpoint(mergedConfig, fetch));
320
- // Test token endpoint and get a token for further tests
321
- const tokenResult = await testTokenEndpoint(mergedConfig, fetch);
322
- tests.push(tokenResult);
323
- // Test validation endpoints
324
- tests.push(await testValidateEndpointNoAuth(mergedConfig, fetch));
325
- tests.push(await testValidateEndpointInvalid(mergedConfig, fetch));
326
- // If we have a token, test validation with valid token
327
- const token = tokenResult.token ?? config.validToken;
328
- if (token) {
329
- tests.push(await testValidateEndpoint(mergedConfig, fetch, token));
330
- tests.push(await testWhoamiWithWorkload(mergedConfig, token));
331
- }
332
- else {
333
- tests.push(skipTest('Workload Validate Endpoint', 'No valid token available'));
334
- tests.push(skipTest('Workload Authentication (Whoami)', 'No valid token available'));
297
+ if (response.status === 404) {
298
+ return {
299
+ details: {
300
+ skipped: true,
301
+ reason: "ESV whoami endpoint not available"
302
+ }
303
+ };
335
304
  }
336
- // Test refresh endpoint
337
- tests.push(await testRefreshEndpoint(mergedConfig, fetch));
338
- const duration = performance.now() - startTime;
339
- const passed = tests.filter((t) => t.passed).length;
340
- const failed = tests.filter((t) => !t.passed).length;
341
- const skipped = tests.filter((t) => t.details?.skipped).length;
305
+ assert(response.status === 200, `Expected 200 OK, got ${response.status}`);
306
+ const data = await response.json();
307
+ assert(data.workload !== void 0, "Response should include workload identity");
342
308
  return {
343
- suite: 'Workload',
344
- passed: failed === 0,
345
- tests,
346
- duration,
347
- summary: {
348
- total: tests.length,
349
- passed: passed - skipped,
350
- failed,
351
- skipped,
352
- },
309
+ details: {
310
+ workloadId: data.workload?.workload_id,
311
+ clientId: data.workload?.client_id
312
+ }
353
313
  };
314
+ });
354
315
  }
355
- /**
356
- * Creates Vitest-compatible test suite for Workload validation
357
- */
358
- export function createWorkloadTests(config) {
359
- const mergedConfig = { ...DEFAULT_CONFIG, ...config };
360
- const fetch = createFetcher(mergedConfig);
361
- // Store token for dependent tests
362
- let acquiredToken;
363
- return [
364
- {
365
- name: 'JWKS endpoint returns valid keys',
366
- fn: async () => {
367
- const result = await testJwksEndpoint(mergedConfig, fetch);
368
- if (!result.passed)
369
- throw new Error(result.error);
370
- },
371
- },
372
- {
373
- name: 'token endpoint returns access token',
374
- fn: async () => {
375
- const result = await testTokenEndpoint(mergedConfig, fetch);
376
- if (!result.passed && !result.details?.skipped)
377
- throw new Error(result.error);
378
- acquiredToken = result.token;
379
- },
380
- },
381
- {
382
- name: 'validate endpoint rejects missing auth',
383
- fn: async () => {
384
- const result = await testValidateEndpointNoAuth(mergedConfig, fetch);
385
- if (!result.passed)
386
- throw new Error(result.error);
387
- },
388
- },
389
- {
390
- name: 'validate endpoint rejects invalid tokens',
391
- fn: async () => {
392
- const result = await testValidateEndpointInvalid(mergedConfig, fetch);
393
- if (!result.passed)
394
- throw new Error(result.error);
395
- },
396
- },
397
- {
398
- name: 'validate endpoint accepts valid token',
399
- fn: async () => {
400
- const token = acquiredToken ?? config.validToken;
401
- if (!token) {
402
- console.warn('Skipping: No valid token available');
403
- return;
404
- }
405
- const result = await testValidateEndpoint(mergedConfig, fetch, token);
406
- if (!result.passed)
407
- throw new Error(result.error);
408
- },
409
- },
410
- {
411
- name: 'whoami endpoint works with workload authentication',
412
- fn: async () => {
413
- const token = acquiredToken ?? config.validToken;
414
- if (!token) {
415
- console.warn('Skipping: No valid token available');
416
- return;
417
- }
418
- const result = await testWhoamiWithWorkload(mergedConfig, token);
419
- if (!result.passed && !result.details?.skipped)
420
- throw new Error(result.error);
421
- },
422
- },
423
- {
424
- name: 'refresh endpoint works',
425
- fn: async () => {
426
- const result = await testRefreshEndpoint(mergedConfig, fetch);
427
- if (!result.passed && !result.details?.skipped)
428
- throw new Error(result.error);
429
- },
430
- },
431
- ];
316
+ async function validateWorkload(config) {
317
+ const startTime = performance.now();
318
+ const mergedConfig = { ...DEFAULT_CONFIG, ...config };
319
+ const fetch2 = createFetcher(mergedConfig);
320
+ const tests = [];
321
+ tests.push(await testJwksEndpoint(mergedConfig, fetch2));
322
+ const tokenResult = await testTokenEndpoint(mergedConfig, fetch2);
323
+ tests.push(tokenResult);
324
+ tests.push(await testValidateEndpointNoAuth(mergedConfig, fetch2));
325
+ tests.push(await testValidateEndpointInvalid(mergedConfig, fetch2));
326
+ const token = tokenResult.token ?? config.validToken;
327
+ if (token) {
328
+ tests.push(await testValidateEndpoint(mergedConfig, fetch2, token));
329
+ tests.push(await testWhoamiWithWorkload(mergedConfig, token));
330
+ } else {
331
+ tests.push(skipTest("Workload Validate Endpoint", "No valid token available"));
332
+ tests.push(skipTest("Workload Authentication (Whoami)", "No valid token available"));
333
+ }
334
+ tests.push(await testRefreshEndpoint(mergedConfig, fetch2));
335
+ const duration = performance.now() - startTime;
336
+ const passed = tests.filter((t) => t.passed).length;
337
+ const failed = tests.filter((t) => !t.passed).length;
338
+ const skipped = tests.filter((t) => t.details?.skipped).length;
339
+ return {
340
+ suite: "Workload",
341
+ passed: failed === 0,
342
+ tests,
343
+ duration,
344
+ summary: {
345
+ total: tests.length,
346
+ passed: passed - skipped,
347
+ failed,
348
+ skipped
349
+ }
350
+ };
351
+ }
352
+ function createWorkloadTests(config) {
353
+ const mergedConfig = { ...DEFAULT_CONFIG, ...config };
354
+ const fetch2 = createFetcher(mergedConfig);
355
+ let acquiredToken;
356
+ return [
357
+ {
358
+ name: "JWKS endpoint returns valid keys",
359
+ fn: async () => {
360
+ const result = await testJwksEndpoint(mergedConfig, fetch2);
361
+ if (!result.passed) throw new Error(result.error);
362
+ }
363
+ },
364
+ {
365
+ name: "token endpoint returns access token",
366
+ fn: async () => {
367
+ const result = await testTokenEndpoint(mergedConfig, fetch2);
368
+ if (!result.passed && !result.details?.skipped) throw new Error(result.error);
369
+ acquiredToken = result.token;
370
+ }
371
+ },
372
+ {
373
+ name: "validate endpoint rejects missing auth",
374
+ fn: async () => {
375
+ const result = await testValidateEndpointNoAuth(mergedConfig, fetch2);
376
+ if (!result.passed) throw new Error(result.error);
377
+ }
378
+ },
379
+ {
380
+ name: "validate endpoint rejects invalid tokens",
381
+ fn: async () => {
382
+ const result = await testValidateEndpointInvalid(mergedConfig, fetch2);
383
+ if (!result.passed) throw new Error(result.error);
384
+ }
385
+ },
386
+ {
387
+ name: "validate endpoint accepts valid token",
388
+ fn: async () => {
389
+ const token = acquiredToken ?? config.validToken;
390
+ if (!token) {
391
+ console.warn("Skipping: No valid token available");
392
+ return;
393
+ }
394
+ const result = await testValidateEndpoint(mergedConfig, fetch2, token);
395
+ if (!result.passed) throw new Error(result.error);
396
+ }
397
+ },
398
+ {
399
+ name: "whoami endpoint works with workload authentication",
400
+ fn: async () => {
401
+ const token = acquiredToken ?? config.validToken;
402
+ if (!token) {
403
+ console.warn("Skipping: No valid token available");
404
+ return;
405
+ }
406
+ const result = await testWhoamiWithWorkload(mergedConfig, token);
407
+ if (!result.passed && !result.details?.skipped) throw new Error(result.error);
408
+ }
409
+ },
410
+ {
411
+ name: "refresh endpoint works",
412
+ fn: async () => {
413
+ const result = await testRefreshEndpoint(mergedConfig, fetch2);
414
+ if (!result.passed && !result.details?.skipped) throw new Error(result.error);
415
+ }
416
+ }
417
+ ];
432
418
  }
419
+ export {
420
+ createWorkloadTests,
421
+ validateWorkload
422
+ };
433
423
  //# sourceMappingURL=index.js.map