Created
April 26, 2023 17:51
-
-
Save dnys1/a4ecd81d3415538c4e0f009fbc88052a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { PreSignUpTriggerHandler } from "aws-lambda"; | |
import { CognitoIdentityProvider } from "@aws-sdk/client-cognito-identity-provider"; | |
const CLIENT = new CognitoIdentityProvider({}); | |
export const handler: PreSignUpTriggerHandler = async (event) => { | |
console.log(`Got event: ${JSON.stringify(event, null, 2)}`); | |
const { | |
triggerSource, | |
userPoolId, | |
userName, | |
request: { | |
userAttributes: { email }, | |
}, | |
} = event; | |
const listResp = await listUsersByEmail({ | |
userPoolId, | |
email, | |
}); | |
const usersForEmail = listResp.Users || []; | |
console.log(`Users for ${email}: ${JSON.stringify(usersForEmail, null, 2)}`); | |
if (triggerSource !== 'PreSignUp_ExternalProvider' || usersForEmail.length === 0) { | |
event.response = { | |
autoConfirmUser: true, | |
autoVerifyEmail: true, | |
autoVerifyPhone: false, | |
} | |
return event; | |
} | |
const existingUser = usersForEmail[0]; | |
const existingUsername = usersForEmail[0].Username!; | |
const [providerName, providerUserId] = userName.split("_"); | |
console.log(`Found existing native account: ${JSON.stringify(existingUser, null, 2)}`); | |
console.log(`Linking external account "${userName}" into native account "${existingUsername}"`); | |
const linkedAccounts = await adminLinkUserAccounts({ | |
username: existingUsername, | |
userPoolId, | |
providerName, | |
providerUserId, | |
}); | |
console.log(`Linked accounts: ${JSON.stringify(linkedAccounts, null, 2)}`); | |
event.response = { | |
autoConfirmUser: true, | |
autoVerifyEmail: true, | |
autoVerifyPhone: false, | |
} | |
return event; | |
}; | |
export const listUsersByEmail = async ({ | |
userPoolId, | |
email, | |
}: { | |
userPoolId: string; | |
email: string; | |
}) => { | |
return CLIENT.listUsers({ | |
UserPoolId: userPoolId, | |
Filter: `email = "${email}"`, | |
}); | |
}; | |
export const adminLinkUserAccounts = async ({ | |
username, | |
userPoolId, | |
providerName, | |
providerUserId, | |
}: { | |
username: string; | |
userPoolId: string; | |
providerName: string; | |
providerUserId: string; | |
}) => { | |
return CLIENT.adminLinkProviderForUser({ | |
DestinationUser: { | |
ProviderAttributeValue: username, | |
ProviderName: "Cognito", | |
}, | |
SourceUser: { | |
ProviderAttributeName: "Cognito_Subject", | |
ProviderAttributeValue: providerUserId, | |
ProviderName: providerName, | |
}, | |
UserPoolId: userPoolId, | |
}); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { IdentityPool, UserPoolAuthenticationProvider } from '@aws-cdk/aws-cognito-identitypool-alpha'; | |
import { App, CfnOutput, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; | |
import { ProviderAttribute, UserPool, UserPoolIdentityProviderAmazon, UserPoolIdentityProviderGoogle, UserPoolIdentityProviderOidc } from 'aws-cdk-lib/aws-cognito'; | |
import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; | |
import { Runtime } from 'aws-cdk-lib/aws-lambda'; | |
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; | |
import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; | |
import { Construct } from 'constructs'; | |
export class MyStack extends Stack { | |
constructor(scope: Construct, id: string, props: StackProps = {}) { | |
super(scope, id, props); | |
const preSignUp = new NodejsFunction(this, 'pre-signup', { | |
runtime: Runtime.NODEJS_18_X, | |
}); | |
const userPool = new UserPool(this, 'UserPool', { | |
removalPolicy: RemovalPolicy.DESTROY, | |
selfSignUpEnabled: true, | |
lambdaTriggers: { | |
preSignUp, | |
} | |
}); | |
const policy = new Policy(this, "PreSignUpPolicy", { | |
statements: [ | |
new PolicyStatement({ | |
resources: [userPool.userPoolArn], | |
actions: [ | |
"cognito-idp:ListUsers", | |
"cognito-idp:AdminLinkProviderForUser", | |
"cognito-idp:AdminCreateUser", | |
"cognito-idp:AdminSetUserPassword", | |
] | |
}) | |
] | |
}); | |
preSignUp.role!.attachInlinePolicy(policy); | |
const amazonCredentials = Secret.fromSecretNameV2( | |
this, | |
'AmazonCredentials', | |
'hosted-ui-merge/amazon', | |
); | |
const amazonProvider = new UserPoolIdentityProviderAmazon( | |
this, | |
'AmazonProvider', | |
{ | |
userPool, | |
attributeMapping: { | |
email: ProviderAttribute.AMAZON_EMAIL, | |
givenName: ProviderAttribute.AMAZON_NAME, | |
}, | |
clientId: amazonCredentials.secretValueFromJson('clientId').toString(), | |
clientSecret: amazonCredentials.secretValueFromJson('clientSecret').toString(), | |
scopes: ['profile', 'email', 'openid'], | |
}, | |
); | |
const googleCredentials = Secret.fromSecretNameV2( | |
this, | |
'GoogleCredentials', | |
'hosted-ui-merge/google', | |
); | |
const googleProvider = new UserPoolIdentityProviderGoogle( | |
this, | |
'GoogleProvider', | |
{ | |
userPool, | |
attributeMapping: { | |
email: ProviderAttribute.GOOGLE_EMAIL, | |
givenName: ProviderAttribute.GOOGLE_GIVEN_NAME, | |
familyName: ProviderAttribute.GOOGLE_FAMILY_NAME, | |
phoneNumber: ProviderAttribute.GOOGLE_PHONE_NUMBERS, | |
}, | |
clientId: googleCredentials.secretValueFromJson('clientId').toString(), | |
clientSecretValue: googleCredentials | |
.secretValueFromJson('clientSecret'), | |
scopes: ['profile', 'email', 'openid'], | |
}, | |
); | |
const microsoftCredentials = Secret.fromSecretNameV2( | |
this, | |
'MicrosoftCredentials', | |
'hosted-ui-merge/microsoft', | |
); | |
const microsoftProvider = new UserPoolIdentityProviderOidc( | |
this, | |
'MicrosoftProvider', | |
{ | |
userPool, | |
name: 'Microsoft', | |
issuerUrl: microsoftCredentials.secretValueFromJson('issuerUrl').toString(), | |
attributeMapping: { | |
email: ProviderAttribute.other('email'), | |
preferredUsername: ProviderAttribute.other('preferred_username'), | |
}, | |
clientId: microsoftCredentials.secretValueFromJson('clientId').toString(), | |
clientSecret: microsoftCredentials.secretValueFromJson('clientSecret').toString(), | |
scopes: ['profile', 'email', 'openid'], | |
}, | |
); | |
userPool.registerIdentityProvider(amazonProvider); | |
userPool.registerIdentityProvider(googleProvider); | |
userPool.registerIdentityProvider(microsoftProvider); | |
const userPoolClient = userPool.addClient('UserPoolClient', { | |
authFlows: { | |
userSrp: true, | |
}, | |
oAuth: { | |
flows: { | |
authorizationCodeGrant: true, | |
}, | |
callbackUrls: [ | |
"http://localhost:3000/", | |
"myapp://" | |
], | |
logoutUrls: [ | |
"http://localhost:3000/", | |
"myapp://" | |
], | |
}, | |
}); | |
const domain = userPool.addDomain('Domain', { | |
cognitoDomain: { | |
domainPrefix: 'hosted-ui-merge', | |
}, | |
}); | |
const identityPool = new IdentityPool(this, 'IdentityPool', { | |
allowUnauthenticatedIdentities: true, | |
authenticationProviders: { | |
userPools: [ | |
new UserPoolAuthenticationProvider({ | |
userPool, | |
userPoolClient, | |
}) | |
] | |
} | |
}); | |
new CfnOutput(this, "UserPoolId", { | |
value: userPool.userPoolId, | |
}); | |
new CfnOutput(this, "UserPoolClientId", { | |
value: userPoolClient.userPoolClientId, | |
}); | |
new CfnOutput(this, "UserPool.HostedUI.Domain", { | |
value: domain.baseUrl(), | |
}); | |
new CfnOutput(this, "IdentityPoolId", { | |
value: identityPool.identityPoolId, | |
}); | |
} | |
} | |
const app = new App(); | |
new MyStack(app, 'hosted-ui-merge', { | |
env: { | |
account: process.env.CDK_DEFAULT_ACCOUNT, | |
region: process.env.CDK_DEFAULT_REGION, | |
} | |
}); | |
app.synth(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment