Skip to content

Commit 2452335

Browse files
authored
Merge pull request #98 from admirkadriu/feat/cognito-pretoken-generation-v2-v3-support
Add Support for V2_0 and V3_0 Lambda Versions in PreTokenGeneration Cognito Triggers
2 parents e2127ef + 504adcb commit 2452335

File tree

4 files changed

+197
-3
lines changed

4 files changed

+197
-3
lines changed

docs/events/cognito-user-pool.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,45 @@ function handler(event, context, callback) {
140140
}
141141
```
142142

143+
### PreTokenGeneration Trigger
144+
145+
The PreTokenGeneration trigger supports multiple lambda versions for enhanced token customization:
146+
147+
```yml
148+
functions:
149+
preTokenGenerationV1:
150+
handler: preToken.handler
151+
events:
152+
- cognitoUserPool:
153+
pool: MyUserPool
154+
trigger: PreTokenGeneration
155+
# No lambdaVersion = V1_0 behavior (ID token customization only)
156+
157+
preTokenGenerationV2:
158+
handler: preToken.handler
159+
events:
160+
- cognitoUserPool:
161+
pool: MyUserPool
162+
trigger: PreTokenGeneration
163+
lambdaVersion: V2_0 # ID + Access token customization
164+
165+
preTokenGenerationV3:
166+
handler: preToken.handler
167+
events:
168+
- cognitoUserPool:
169+
pool: MyUserPool
170+
trigger: PreTokenGeneration
171+
lambdaVersion: V3_0 # Includes M2M client-credentials grants
172+
```
173+
174+
**Lambda Version Support:**
175+
176+
- `V1_0` (default): ID token customization only
177+
- `V2_0`: ID and access token customization
178+
- `V3_0`: Includes machine-to-machine (M2M) client-credentials grants
179+
180+
**NOTE:** V2_0 and V3_0 require your user pool to be on the Essentials or Plus feature plan, as documented [by AWS](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html).
181+
143182
### Custom Message Trigger Handlers
144183

145184
For custom messages, you will need to check `event.triggerSource` type inside your handler function:

lib/plugins/aws/custom-resources/resources/cognito-user-pool/lib/user-pool.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ async function updateConfiguration(config) {
9191
LambdaVersion: poolConfig.LambdaVersion,
9292
};
9393
LambdaConfig.KMSKeyID = poolConfig.KMSKeyID;
94+
} else if (poolConfig.Trigger === 'PreTokenGeneration' && poolConfig.LambdaVersion) {
95+
LambdaConfig.PreTokenGenerationConfig = {
96+
LambdaArn: lambdaArn,
97+
LambdaVersion: poolConfig.LambdaVersion,
98+
};
9499
} else {
95100
LambdaConfig[poolConfig.Trigger] = lambdaArn;
96101
}
@@ -131,6 +136,9 @@ async function removeConfiguration(config) {
131136
function removeExistingLambdas(lambdaConfig, lambdaArn) {
132137
const res = Object.fromEntries(
133138
Object.entries(lambdaConfig).filter(([key, value]) => {
139+
if (key === 'PreTokenGenerationConfig' && value && value.LambdaArn === lambdaArn) {
140+
return false;
141+
}
134142
return (
135143
!(customSenderSources.includes(key) && value.LambdaArn === lambdaArn) && value !== lambdaArn
136144
);

lib/plugins/aws/package/compile/events/cognito-user-pool.js

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ const validTriggerSources = [
2020
'UserMigration',
2121
].concat(customSenderSources);
2222

23-
const validLambdaVersions = ['V1_0'];
23+
const customSenderValidVersions = ['V1_0'];
24+
const preTokenGenerationValidVersions = ['V1_0', 'V2_0', 'V3_0'];
2425

2526
class AwsCompileCognitoUserPoolEvents {
2627
constructor(serverless, options) {
@@ -154,6 +155,8 @@ class AwsCompileCognitoUserPoolEvents {
154155
const { pool, trigger, forceDeploy, kmsKeyId, lambdaVersion } = event.cognitoUserPool;
155156
usesExistingCognitoUserPool = funcUsesExistingCognitoUserPool = true;
156157

158+
this.validateLambdaVersion(trigger, lambdaVersion);
159+
157160
if (!currentPoolName) {
158161
currentPoolName = pool;
159162
}
@@ -190,12 +193,14 @@ class AwsCompileCognitoUserPoolEvents {
190193
userPoolConfig = {
191194
...userPoolConfig,
192195
...{
193-
LambdaVersion: lambdaVersion || validLambdaVersions[0],
196+
LambdaVersion: lambdaVersion || customSenderValidVersions[0],
194197
},
195198
};
196199

197200
this.checkKmsArn(kmsKeyId, poolKmsIdMap, pool);
198201
userPoolConfig.KMSKeyID = kmsKeyId;
202+
} else if (trigger === 'PreTokenGeneration' && lambdaVersion) {
203+
userPoolConfig.LambdaVersion = lambdaVersion;
199204
}
200205

201206
if (numEventsForFunc === 1) {
@@ -310,6 +315,33 @@ class AwsCompileCognitoUserPoolEvents {
310315
poolKmsIdMap.set(currentPoolName, kmsKeyId);
311316
}
312317

318+
validateLambdaVersion(trigger, lambdaVersion) {
319+
if (customSenderSources.includes(trigger)) {
320+
if (lambdaVersion && !customSenderValidVersions.includes(lambdaVersion)) {
321+
throw new ServerlessError(
322+
`Invalid lambdaVersion "${lambdaVersion}" for trigger "${trigger}". Custom sender triggers only support: ${customSenderValidVersions.join(
323+
', '
324+
)}`,
325+
'COGNITO_INVALID_LAMBDA_VERSION'
326+
);
327+
}
328+
} else if (trigger === 'PreTokenGeneration') {
329+
if (lambdaVersion && !preTokenGenerationValidVersions.includes(lambdaVersion)) {
330+
throw new ServerlessError(
331+
`Invalid lambdaVersion "${lambdaVersion}" for trigger "${trigger}". PreTokenGeneration supports: ${preTokenGenerationValidVersions.join(
332+
', '
333+
)}`,
334+
'COGNITO_INVALID_LAMBDA_VERSION'
335+
);
336+
}
337+
} else if (lambdaVersion) {
338+
throw new ServerlessError(
339+
`lambdaVersion is not supported for trigger "${trigger}". It's only supported for: CustomSMSSender, CustomEmailSender, and PreTokenGeneration`,
340+
'COGNITO_LAMBDA_VERSION_NOT_SUPPORTED'
341+
);
342+
}
343+
}
344+
313345
findUserPoolsAndFunctions() {
314346
const userPools = [];
315347
const cognitoUserPoolTriggerFunctions = [];
@@ -323,6 +355,11 @@ class AwsCompileCognitoUserPoolEvents {
323355
if (event.cognitoUserPool) {
324356
if (event.cognitoUserPool.existing) return;
325357

358+
this.validateLambdaVersion(
359+
event.cognitoUserPool.trigger,
360+
event.cognitoUserPool.lambdaVersion
361+
);
362+
326363
// Save trigger functions so we can use them to generate
327364
// IAM permissions later
328365
cognitoUserPoolTriggerFunctions.push({
@@ -354,11 +391,18 @@ class AwsCompileCognitoUserPoolEvents {
354391
triggerObject = {
355392
[value.triggerSource]: {
356393
LambdaArn: resolveLambdaTarget(value.functionName, functionObj),
357-
LambdaVersion: value.lambdaVersion || validLambdaVersions[0],
394+
LambdaVersion: value.lambdaVersion || customSenderValidVersions[0],
358395
},
359396
};
360397
this.checkKmsArn(value.kmsKeyId, poolKmsIdMap, poolName);
361398
triggerObject.KMSKeyID = value.kmsKeyId;
399+
} else if (value.triggerSource === 'PreTokenGeneration' && value.lambdaVersion) {
400+
triggerObject = {
401+
PreTokenGenerationConfig: {
402+
LambdaArn: resolveLambdaTarget(value.functionName, functionObj),
403+
LambdaVersion: value.lambdaVersion,
404+
},
405+
};
362406
} else {
363407
triggerObject = {
364408
[value.triggerSource]: resolveLambdaTarget(value.functionName, functionObj),

test/unit/lib/plugins/aws/package/compile/events/cognito-user-pool.test.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,49 @@ const serverlessConfigurationExtension = {
162162
},
163163
};
164164

165+
const preTokenGenerationConfigurationExtension = {
166+
configValidationMode: 'off',
167+
functions: {
168+
preTokenGenerationV2: {
169+
handler: 'index.js',
170+
events: [
171+
{
172+
cognitoUserPool: {
173+
pool: 'PreTokenGenerationV2Pool',
174+
trigger: 'PreTokenGeneration',
175+
lambdaVersion: 'V2_0',
176+
},
177+
},
178+
],
179+
},
180+
preTokenGenerationV3: {
181+
handler: 'index.js',
182+
events: [
183+
{
184+
cognitoUserPool: {
185+
pool: 'PreTokenGenerationV3Pool',
186+
trigger: 'PreTokenGeneration',
187+
lambdaVersion: 'V3_0',
188+
},
189+
},
190+
],
191+
},
192+
preTokenGenerationV2Existing: {
193+
handler: 'index.js',
194+
events: [
195+
{
196+
cognitoUserPool: {
197+
pool: 'PreTokenGenerationV2PoolExisting',
198+
trigger: 'PreTokenGeneration',
199+
lambdaVersion: 'V2_0',
200+
existing: true,
201+
},
202+
},
203+
],
204+
},
205+
},
206+
};
207+
165208
describe('AwsCompileCognitoUserPoolEvents', () => {
166209
let serverless;
167210
let awsCompileCognitoUserPoolEvents;
@@ -1385,4 +1428,64 @@ describe('lib/plugins/aws/package/compile/events/cognito-user-pool.test.js', ()
13851428
});
13861429
});
13871430
});
1431+
1432+
describe('PreTokenGeneration Lambda Versions', () => {
1433+
describe('New Pools', () => {
1434+
it('should create PreTokenGenerationConfig for V2_0', async () => {
1435+
const { cfTemplate } = await runServerless({
1436+
fixture: 'cognito-user-pool',
1437+
configExt: preTokenGenerationConfigurationExtension,
1438+
command: 'package',
1439+
});
1440+
1441+
const userPoolResource = cfTemplate.Resources.CognitoUserPoolPreTokenGenerationV2Pool;
1442+
expect(userPoolResource.Properties.LambdaConfig).to.have.property(
1443+
'PreTokenGenerationConfig'
1444+
);
1445+
expect(
1446+
userPoolResource.Properties.LambdaConfig.PreTokenGenerationConfig.LambdaVersion
1447+
).to.equal('V2_0');
1448+
expect(userPoolResource.Properties.LambdaConfig).to.not.have.property('PreTokenGeneration');
1449+
});
1450+
1451+
it('should create PreTokenGenerationConfig for V3_0', async () => {
1452+
const { cfTemplate } = await runServerless({
1453+
fixture: 'cognito-user-pool',
1454+
configExt: preTokenGenerationConfigurationExtension,
1455+
command: 'package',
1456+
});
1457+
1458+
const userPoolResource = cfTemplate.Resources.CognitoUserPoolPreTokenGenerationV3Pool;
1459+
expect(userPoolResource.Properties.LambdaConfig).to.have.property(
1460+
'PreTokenGenerationConfig'
1461+
);
1462+
expect(
1463+
userPoolResource.Properties.LambdaConfig.PreTokenGenerationConfig.LambdaVersion
1464+
).to.equal('V3_0');
1465+
expect(userPoolResource.Properties.LambdaConfig).to.not.have.property('PreTokenGeneration');
1466+
});
1467+
});
1468+
1469+
describe('Existing Pools', () => {
1470+
it('should create correct UserPoolConfigs for V2_0', async () => {
1471+
const { cfTemplate } = await runServerless({
1472+
fixture: 'cognito-user-pool',
1473+
configExt: preTokenGenerationConfigurationExtension,
1474+
command: 'package',
1475+
});
1476+
1477+
const customResources = Object.values(cfTemplate.Resources).filter(
1478+
(resource) => resource.Type === 'Custom::CognitoUserPool'
1479+
);
1480+
const v2Resource = customResources.find(
1481+
(resource) => resource.Properties.UserPoolName === 'PreTokenGenerationV2PoolExisting'
1482+
);
1483+
1484+
expect(v2Resource.Properties.UserPoolConfigs[0]).to.deep.include({
1485+
Trigger: 'PreTokenGeneration',
1486+
LambdaVersion: 'V2_0',
1487+
});
1488+
});
1489+
});
1490+
});
13881491
});

0 commit comments

Comments
 (0)