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;
|
|
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
|
|
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
|
|
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":";;
|
|
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
|
+
"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
|
|
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
|
|
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
|
};
|
package/tests/identity.test.ts
CHANGED
|
@@ -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', () => {
|