我终于让这件事以一种不奇怪的方式工作,用户必须授权两次或其他事情。
流程说明:
- 用户第一次尝试使用身份提供者进行身份验证 => PreSignUp lambda 启动并通过电子邮件检查用户是否存在
1a。如果用户存在,它将抛出一个错误,例如。我在客户端上捕获的
CONFIRM_IDENTITY_LINK_令牌。令牌是带有用户名和身份 ID 的 base64 字符串(“用户名:facebook_123456”)
1b。如果用户名不存在,我会使用临时密码创建一个新用户并抛出错误 FORCE_CHANGE_PASSWORD_ token。相同的令牌,但我这次添加了临时密码。
- 在客户端中,我有一个回调路由“/authorize”=> 这是您在 Cognito 中设置为回调 URL 的路由,还有 2 个额外的路由:“/confirm-password”和“/configure-password”。
在 /authorize 路由中,我正在捕获错误并获取附加的令牌并重定向到额外的路由: 1a => /configure-password?token= token和 1b => /confirm-password?token= token
对于“/confirm-password”,我要求用户确认其当前密码以授权与提供者的链接,然后使用令牌以身份 ID 作为 clientMetadata 登录他,例如“{”LINK_PROVIDER”:“facebbok_12345678” }"
登录时,我有一个 PostAuthentication lambda,它检查 clientMetadata 中的“LINK_PROVIDER”,并将其链接到用户。
对于“/configure-password”,我解析令牌并使用来自令牌的凭据和身份 ID 作为客户端元数据(与上面相同)进行“浅”登录,然后提示用户为其帐户配置新密码。
我知道这似乎有点限制,但我发现它比授权两次要好。
此外,这不会为用户池中的身份创建额外的用户。
代码示例:
PreSignUp lambda
export async function handler(event: PreSignUpTriggerEvent) {
try {
const { userPoolId, triggerSource, request, userName } = event
if (triggerSource === 'PreSignUp_ExternalProvider') {
// Check if user exists in cognito
let currentUser = await getUserByEmail(userPoolId, request.userAttributes.email)
if (currentUser) {
// User exists, thow error with identity id
const identity = Buffer.from(`${currentUser}:${userName}`).toString('base64')
throw new Error(`CONFIRM_USER_IDENTITY_${identity}`)
}
// Create new Cognito user with temp password
const tempPassword = generatePassword()
currentUser = await createNewUser(userPoolId, request.userAttributes, tempPassword)
// Throw error with token
const state = Buffer.from(`${currentUser}:${tempPassword}:${userName}`).toString('base64')
throw new Error(`FORCE_CHANGE_PASSWORD_${state}`)
}
return event
} catch (error) {
throw new Error(error)
}
}
PostAuthentication lambda
export async function handler(event: PostAuthenticationTriggerEvent) {
try {
const { userPoolId, request, userName } = event
if (request.clientMetadata?.LINK_IDENTITY) {
const identity = request.clientMetadata['LINK_IDENTITY']
// Link identity to user
await linkIdentityProvider(userPoolId, userName, identity)
}
return event
} catch (error) {
console.error(error)
throw new Error('Internal server error')
}
}