Ale*_*exR 28 amazon-web-services google-login amazon-cognito aws-lambda amazon-cognito-facebook
当用户通过 Google 和 Facebook 身份提供商使用相同的电子邮件地址登录时,AWS Cognito 在用户池中创建多个条目,每个身份提供商使用一个条目:
我使用本教程中提供的示例代码来设置 AWS Cognito:使用 Amplify 框架进行用户身份验证的完整指南
Pha*_*iệt 31
是的。您可以使用AdminLinkProviderForUser https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminLinkProviderForUser.html
这个想法是:
import CognitoIdentityServiceProvider from 'aws-sdk/clients/cognitoidentityserviceprovider'
const cognitoIdp = new CognitoIdentityServiceProvider()
const getUserByEmail = async (userPoolId, email) => {
const params = {
UserPoolId: userPoolId,
Filter: `email = "${email}"`
}
return cognitoIdp.listUsers(params).promise()
}
const linkProviderToUser = async (username, userPoolId, providerName, providerUserId) => {
const params = {
DestinationUser: {
ProviderAttributeValue: username,
ProviderName: 'Cognito'
},
SourceUser: {
ProviderAttributeName: 'Cognito_Subject',
ProviderAttributeValue: providerUserId,
ProviderName: providerName
},
UserPoolId: userPoolId
}
const result = await (new Promise((resolve, reject) => {
cognitoIdp.adminLinkProviderForUser(params, (err, data) => {
if (err) {
reject(err)
return
}
resolve(data)
})
}))
return result
}
exports.handler = async (event, context, callback) => {
if (event.triggerSource === 'PreSignUp_ExternalProvider') {
const userRs = await getUserByEmail(event.userPoolId, event.request.userAttributes.email)
if (userRs && userRs.Users.length > 0) {
const [ providerName, providerUserId ] = event.userName.split('_') // event userName example: "Facebook_12324325436"
await linkProviderToUser(userRs.Users[0].Username, event.userPoolId, providerName, providerUserId)
} else {
console.log('user not found, skip.')
}
}
return callback(null, event)
}
Run Code Online (Sandbox Code Playgroud)
注意:您可能会在用户池 UI 中看到 2 条记录,但是在访问用户记录详细信息时,它们已合并。
我一直在摆弄同样的问题。接受的答案是有效的,但并不涵盖所有场景。主要的一点是,一旦用户使用外部登录进行注册,他们将永远无法使用用户名和密码进行注册。目前,Cognito 不允许将 Cognito 用户链接到外部用户。
我的场景如下:
username在所有链接的用户之间有一个共同点,将其用作其他服务中的唯一 id。我建议的解决方案是始终首先创建 Cognito 用户并将所有外部用户链接到它。
user already exists错误消息。在这种情况下,他们可以使用forgot password流恢复然后登录。const {
CognitoIdentityServiceProvider
} = require('aws-sdk');
const handler = async event => {
const userPoolId = event.userPoolId;
const trigger = event.triggerSource;
const email = event.request.userAttributes.email;
const givenName = event.request.userAttributes.given_name;
const familyName = event.request.userAttributes.family_name;
const emailVerified = event.request.userAttributes.email_verified;
const identity = event.userName;
const client = new CognitoIdentityServiceProvider();
if (trigger === 'PreSignUp_ExternalProvider') {
await client.listUsers({
UserPoolId: userPoolId,
AttributesToGet: ['email', 'family_name', 'given_name'],
Filter: `email = "${email}"`
})
.promise()
.then(({
Users
}) => Users.sort((a, b) => (a.UserCreateDate > b.UserCreateDate ? 1 : -1)))
.then(users => users.length > 0 ? users[0] : null)
.then(user => {
// user with username password already exists, do nothing
if (user) {
return user;
}
// user with username password does not exists, create one
const newUser = await client.adminCreateUser({
UserPoolId: userPoolId,
Username: email,
MessageAction: 'SUPPRESS', // dont send email to user
UserAttributes: [{
Name: 'given_name',
Value: givenName
},
{
Name: 'family_name',
Value: familyName
},
{
Name: 'email',
Value: email
},
{
Name: 'email_verified',
Value: emailVerified
}
]
})
.promise();
// gotta set the password, else user wont be able to reset it
await client.adminSetUserPassword({
UserPoolId: userPoolId,
Username: newUser.Username,
Password: '<generate random password>',
Permanent: true
}).promise();
return newUser.Username;
}).then(username => {
// link external user to cognito user
const split = identity.split('_');
const providerValue = split.length > 1 ? split[1] : null;
const provider = ['Google', 'Facebook'].find(
val => split[0].toUpperCase() === val.toUpperCase()
);
if (!provider || !providerValue) {
return Promise.reject(new Error('Invalid external user'));
}
return client.adminLinkProviderForUser({
UserPoolId: userPoolId,
DestinationUser: {
ProviderName: 'Cognito',
ProviderAttributeValue: username
},
SourceUser: {
ProviderName: provider,
ProviderAttributeName: 'Cognito_Subject',
ProviderAttributeValue: providerValue
}
})
.promise()
});
}
return event;
};
module.exports = {
handler
};
Run Code Online (Sandbox Code Playgroud)
我认为,我创建的解决方案可以处理所有情况。它还解决了 Cognito 的一些常见问题。
请注意,在关联帐户时,Cognito 预注册触发器会返回“已找到用户名条目”错误。您的客户端应处理此问题并重新尝试身份验证,或要求用户再次登录。更多信息在这里:
Cognito 身份验证流程失败,并显示“已找到用户名 Facebook_10155611263153532 的条目”
这是我的 lambda,在 Cognito 预注册触发器上执行
const AWS = require("aws-sdk");
const cognito = new AWS.CognitoIdentityServiceProvider();
exports.handler = (event, context, callback) => {
function checkForExistingUsers(event, linkToExistingUser) {
console.log("Executing checkForExistingUsers");
var params = {
UserPoolId: event.userPoolId,
AttributesToGet: ['sub', 'email'],
Filter: "email = \"" + event.request.userAttributes.email + "\""
};
return new Promise((resolve, reject) =>
cognito.listUsers(params, (err, result) => {
if (err) {
reject(err);
return;
}
if (result && result.Users && result.Users[0] && result.Users[0].Username && linkToExistingUser) {
console.log("Found existing users: ", result.Users);
if (result.Users.length > 1){
result.Users.sort((a, b) => (a.UserCreateDate > b.UserCreateDate) ? 1 : -1);
console.log("Found more than one existing users. Ordered by createdDate: ", result.Users);
}
linkUser(result.Users[0].Username, event).then(result => {
resolve(result);
})
.catch(error => {
reject(err);
return;
});
} else {
resolve(result);
}
})
);
}
function linkUser(sub, event) {
console.log("Linking user accounts with target sub: " + sub + "and event: ", event);
//By default, assume the existing account is a Cognito username/password
var destinationProvider = "Cognito";
var destinationSub = sub;
//If the existing user is in fact an external user (Xero etc), override the the provider
if (sub.includes("_")) {
destinationProvider = sub.split("_")[0];
destinationSub = sub.split("_")[1];
}
var params = {
DestinationUser: {
ProviderAttributeValue: destinationSub,
ProviderName: destinationProvider
},
SourceUser: {
ProviderAttributeName: 'Cognito_Subject',
ProviderAttributeValue: event.userName.split("_")[1],
ProviderName: event.userName.split("_")[0]
},
UserPoolId: event.userPoolId
};
console.log("Parameters for adminLinkProviderForUser: ", params);
return new Promise((resolve, reject) =>
cognito.adminLinkProviderForUser(params, (err, result) => {
if (err) {
console.log("Error encountered whilst linking users: ", err);
reject(err);
return;
}
console.log("Successfully linked users.");
resolve(result);
})
);
}
console.log(JSON.stringify(event));
if (event.triggerSource == "PreSignUp_SignUp" || event.triggerSource == "PreSignUp_AdminCreateUser") {
checkForExistingUsers(event, false).then(result => {
if (result != null && result.Users != null && result.Users[0] != null) {
console.log("Found at least one existing account with that email address: ", result);
console.log("Rejecting sign-up");
//prevent sign-up
callback("An external provider account alreadys exists for that email address", null);
} else {
//proceed with sign-up
callback(null, event);
}
})
.catch(error => {
console.log("Error checking for existing users: ", error);
//proceed with sign-up
callback(null, event);
});
}
if (event.triggerSource == "PreSignUp_ExternalProvider") {
checkForExistingUsers(event, true).then(result => {
console.log("Completed looking up users and linking them: ", result);
callback(null, event);
})
.catch(error => {
console.log("Error checking for existing users: ", error);
//proceed with sign-up
callback(null, event);
});
}
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7855 次 |
| 最近记录: |