serverless-event-orchestrator 1.2.3 → 1.2.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.
@@ -1,8 +1,4 @@
1
1
  import { IdentityContext } from '../types/routes.js';
2
- /**
3
- * Extracts identity context from API Gateway authorizer claims
4
- * Supports Cognito User Pools and custom authorizers
5
- */
6
2
  /**
7
3
  * Extracts identity information from the event's authorizer context
8
4
  * @param event - Raw API Gateway event
@@ -1 +1 @@
1
- {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/identity/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,GAAG,eAAe,GAAG,SAAS,CAcvE;AAcD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAMhF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,EAAE,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAKzG;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAInG;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAIrG"}
1
+ {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/identity/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAoDrD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,GAAG,eAAe,GAAG,SAAS,CAevE;AAcD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAMhF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,EAAE,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAKzG;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAInG;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAIrG"}
@@ -7,22 +7,63 @@ exports.hasAnyGroup = hasAnyGroup;
7
7
  exports.hasAllGroups = hasAllGroups;
8
8
  /**
9
9
  * Extracts identity context from API Gateway authorizer claims
10
- * Supports Cognito User Pools and custom authorizers
10
+ * Supports multiple API Gateway formats:
11
+ * - REST API with Cognito User Pool Authorizer
12
+ * - HTTP API with JWT Authorizer
13
+ * - REST API with Custom Lambda Authorizer
14
+ * - HTTP API with Lambda Authorizer
11
15
  */
16
+ /**
17
+ * Extracts claims from the authorizer context, handling multiple API Gateway formats
18
+ * @param authorizer - The authorizer object from requestContext
19
+ * @returns Claims object or undefined
20
+ */
21
+ function extractClaims(authorizer) {
22
+ if (!authorizer)
23
+ return undefined;
24
+ // 1. REST API with Cognito User Pool Authorizer: authorizer.claims
25
+ if (authorizer.claims && typeof authorizer.claims === 'object') {
26
+ return authorizer.claims;
27
+ }
28
+ // 2. HTTP API with JWT Authorizer: authorizer.jwt.claims
29
+ if (authorizer.jwt?.claims && typeof authorizer.jwt.claims === 'object') {
30
+ return authorizer.jwt.claims;
31
+ }
32
+ // 3. HTTP API with Lambda Authorizer: authorizer.lambda
33
+ if (authorizer.lambda && typeof authorizer.lambda === 'object') {
34
+ return authorizer.lambda;
35
+ }
36
+ // 4. REST API with Custom Lambda Authorizer: claims directly in authorizer
37
+ // Check if authorizer has identity-like properties (sub, userId, email, etc.)
38
+ if (hasIdentityProperties(authorizer)) {
39
+ return authorizer;
40
+ }
41
+ return undefined;
42
+ }
43
+ /**
44
+ * Checks if an object has identity-like properties
45
+ */
46
+ function hasIdentityProperties(obj) {
47
+ if (!obj || typeof obj !== 'object')
48
+ return false;
49
+ const identityKeys = ['sub', 'userId', 'user_id', 'email', 'cognito:username', 'iss', 'aud'];
50
+ return identityKeys.some(key => key in obj);
51
+ }
12
52
  /**
13
53
  * Extracts identity information from the event's authorizer context
14
54
  * @param event - Raw API Gateway event
15
55
  * @returns Identity context or undefined if not authenticated
16
56
  */
17
57
  function extractIdentity(event) {
18
- const claims = event?.requestContext?.authorizer?.claims;
58
+ const authorizer = event?.requestContext?.authorizer;
59
+ const claims = extractClaims(authorizer);
19
60
  if (!claims) {
20
61
  return undefined;
21
62
  }
22
63
  return {
23
- userId: claims.sub || claims['cognito:username'],
64
+ userId: claims.sub || claims['cognito:username'] || claims.userId || claims.user_id,
24
65
  email: claims.email,
25
- groups: parseGroups(claims['cognito:groups']),
66
+ groups: parseGroups(claims['cognito:groups'] || claims.groups),
26
67
  issuer: claims.iss,
27
68
  claims,
28
69
  };
@@ -1 +1 @@
1
- {"version":3,"file":"extractor.js","sourceRoot":"","sources":["../../src/identity/extractor.ts"],"names":[],"mappings":";;AAYA,0CAcC;AAmBD,8CAMC;AAQD,wCAKC;AAQD,kCAIC;AAQD,oCAIC;AAtFD;;;GAGG;AAEH;;;;GAIG;AACH,SAAgB,eAAe,CAAC,KAAU;IACxC,MAAM,MAAM,GAAG,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,CAAC;IAEzD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,kBAAkB,CAAC;QAChD,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAC7C,MAAM,EAAE,MAAM,CAAC,GAAG;QAClB,MAAM;KACP,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,MAAqC;IACxD,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAEzC,6DAA6D;IAC7D,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,SAAgB,iBAAiB,CAAC,MAA0B;IAC1D,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE9B,6CAA6C;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;;GAKG;AACH,SAAgB,cAAc,CAAC,QAAqC,EAAE,kBAA0B;IAC9F,IAAI,CAAC,QAAQ,EAAE,MAAM;QAAE,OAAO,KAAK,CAAC;IAEpC,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5D,OAAO,gBAAgB,KAAK,kBAAkB,CAAC;AACjD,CAAC;AAED;;;;;GAKG;AACH,SAAgB,WAAW,CAAC,QAAqC,EAAE,aAAuB;IACxF,IAAI,CAAC,QAAQ,EAAE,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEpE,OAAO,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AACvE,CAAC;AAED;;;;;GAKG;AACH,SAAgB,YAAY,CAAC,QAAqC,EAAE,cAAwB;IAC1F,IAAI,CAAC,QAAQ,EAAE,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEpE,OAAO,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AACzE,CAAC"}
1
+ {"version":3,"file":"extractor.js","sourceRoot":"","sources":["../../src/identity/extractor.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serverless-event-orchestrator",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "A lightweight, type-safe event dispatcher and middleware orchestrator for AWS Lambda. Designed for hexagonal architectures with support for segmented routing (public, private, backoffice), Cognito User Pool validation, and built-in infrastructure middlewares.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -2,25 +2,71 @@ import { IdentityContext } from '../types/routes.js';
2
2
 
3
3
  /**
4
4
  * Extracts identity context from API Gateway authorizer claims
5
- * Supports Cognito User Pools and custom authorizers
5
+ * Supports multiple API Gateway formats:
6
+ * - REST API with Cognito User Pool Authorizer
7
+ * - HTTP API with JWT Authorizer
8
+ * - REST API with Custom Lambda Authorizer
9
+ * - HTTP API with Lambda Authorizer
6
10
  */
7
11
 
12
+ /**
13
+ * Extracts claims from the authorizer context, handling multiple API Gateway formats
14
+ * @param authorizer - The authorizer object from requestContext
15
+ * @returns Claims object or undefined
16
+ */
17
+ function extractClaims(authorizer: any): Record<string, any> | undefined {
18
+ if (!authorizer) return undefined;
19
+
20
+ // 1. REST API with Cognito User Pool Authorizer: authorizer.claims
21
+ if (authorizer.claims && typeof authorizer.claims === 'object') {
22
+ return authorizer.claims;
23
+ }
24
+
25
+ // 2. HTTP API with JWT Authorizer: authorizer.jwt.claims
26
+ if (authorizer.jwt?.claims && typeof authorizer.jwt.claims === 'object') {
27
+ return authorizer.jwt.claims;
28
+ }
29
+
30
+ // 3. HTTP API with Lambda Authorizer: authorizer.lambda
31
+ if (authorizer.lambda && typeof authorizer.lambda === 'object') {
32
+ return authorizer.lambda;
33
+ }
34
+
35
+ // 4. REST API with Custom Lambda Authorizer: claims directly in authorizer
36
+ // Check if authorizer has identity-like properties (sub, userId, email, etc.)
37
+ if (hasIdentityProperties(authorizer)) {
38
+ return authorizer;
39
+ }
40
+
41
+ return undefined;
42
+ }
43
+
44
+ /**
45
+ * Checks if an object has identity-like properties
46
+ */
47
+ function hasIdentityProperties(obj: any): boolean {
48
+ if (!obj || typeof obj !== 'object') return false;
49
+ const identityKeys = ['sub', 'userId', 'user_id', 'email', 'cognito:username', 'iss', 'aud'];
50
+ return identityKeys.some(key => key in obj);
51
+ }
52
+
8
53
  /**
9
54
  * Extracts identity information from the event's authorizer context
10
55
  * @param event - Raw API Gateway event
11
56
  * @returns Identity context or undefined if not authenticated
12
57
  */
13
58
  export function extractIdentity(event: any): IdentityContext | undefined {
14
- const claims = event?.requestContext?.authorizer?.claims;
59
+ const authorizer = event?.requestContext?.authorizer;
60
+ const claims = extractClaims(authorizer);
15
61
 
16
62
  if (!claims) {
17
63
  return undefined;
18
64
  }
19
65
 
20
66
  return {
21
- userId: claims.sub || claims['cognito:username'],
67
+ userId: claims.sub || claims['cognito:username'] || claims.userId || claims.user_id,
22
68
  email: claims.email,
23
- groups: parseGroups(claims['cognito:groups']),
69
+ groups: parseGroups(claims['cognito:groups'] || claims.groups),
24
70
  issuer: claims.iss,
25
71
  claims,
26
72
  };
@@ -73,6 +73,100 @@ describe('extractIdentity', () => {
73
73
  const identity = extractIdentity(event);
74
74
  expect(identity?.groups).toEqual(['Admin', 'User']);
75
75
  });
76
+
77
+ // HTTP API with JWT Authorizer tests
78
+ it('should extract identity from HTTP API JWT Authorizer format', () => {
79
+ const event = {
80
+ requestContext: {
81
+ authorizer: {
82
+ jwt: {
83
+ claims: {
84
+ sub: 'user-456',
85
+ email: 'jwt@example.com',
86
+ iss: 'https://cognito-idp.us-east-1.amazonaws.com/us-east-1_JWT123'
87
+ }
88
+ }
89
+ }
90
+ }
91
+ };
92
+
93
+ const identity = extractIdentity(event);
94
+
95
+ expect(identity).toEqual({
96
+ userId: 'user-456',
97
+ email: 'jwt@example.com',
98
+ groups: [],
99
+ issuer: 'https://cognito-idp.us-east-1.amazonaws.com/us-east-1_JWT123',
100
+ claims: event.requestContext.authorizer.jwt.claims
101
+ });
102
+ });
103
+
104
+ // HTTP API with Lambda Authorizer tests
105
+ it('should extract identity from HTTP API Lambda Authorizer format', () => {
106
+ const event = {
107
+ requestContext: {
108
+ authorizer: {
109
+ lambda: {
110
+ userId: 'lambda-user-789',
111
+ email: 'lambda@example.com',
112
+ groups: ['Premium', 'Verified']
113
+ }
114
+ }
115
+ }
116
+ };
117
+
118
+ const identity = extractIdentity(event);
119
+
120
+ expect(identity?.userId).toBe('lambda-user-789');
121
+ expect(identity?.email).toBe('lambda@example.com');
122
+ expect(identity?.groups).toEqual(['Premium', 'Verified']);
123
+ });
124
+
125
+ // REST API with Custom Lambda Authorizer (flat structure)
126
+ it('should extract identity from custom Lambda Authorizer with flat structure', () => {
127
+ const event = {
128
+ requestContext: {
129
+ authorizer: {
130
+ sub: 'custom-user-101',
131
+ email: 'custom@example.com',
132
+ iss: 'https://custom-issuer.com'
133
+ }
134
+ }
135
+ };
136
+
137
+ const identity = extractIdentity(event);
138
+
139
+ expect(identity?.userId).toBe('custom-user-101');
140
+ expect(identity?.email).toBe('custom@example.com');
141
+ expect(identity?.issuer).toBe('https://custom-issuer.com');
142
+ });
143
+
144
+ it('should handle userId fallback from user_id in custom authorizer', () => {
145
+ const event = {
146
+ requestContext: {
147
+ authorizer: {
148
+ user_id: 'underscore-user',
149
+ email: 'test@example.com'
150
+ }
151
+ }
152
+ };
153
+
154
+ const identity = extractIdentity(event);
155
+ expect(identity?.userId).toBe('underscore-user');
156
+ });
157
+
158
+ it('should return undefined when authorizer has no identity properties', () => {
159
+ const event = {
160
+ requestContext: {
161
+ authorizer: {
162
+ principalId: 'some-principal',
163
+ integrationLatency: 123
164
+ }
165
+ }
166
+ };
167
+
168
+ expect(extractIdentity(event)).toBeUndefined();
169
+ });
76
170
  });
77
171
 
78
172
  describe('extractUserPoolId', () => {