前陣子在規劃有關於 Cognito 需要整合 Federated Login 的案子,其中有一個需求是希望當使用者從 Facebook/Google 等 Social Login 登入時,可以將對應的 User Profile 資訊回填到 Cognito User Pool Attribute (Specifying Identity Provider Attribute Mappings for Your User Pool)
但事與願違,通常和 Third Party 串接總有一些意外,這一篇就是要講這個意外 …
由於 Cognito 的 Scheme 中至少有一項 email 或 phone number 必須被 verified 過,使用者要 “忘記密碼” 或 “重設密碼” 時才可以透過當時設定的 email / phone number 接收驗證訊息,所以這兩個值至少要一個能用,而在這個案例中採用的是 email 作為驗證。
一般來說 Facebook/Google 等都會至少進行 email verified 過,所以從這些 Social Provider 登入時基本上是可以信任 email 是可用的,但這些資訊要回填到 Cognito User Pool 時通常會參照從 Social Provider 拿回來的 User Profile 作為參考
以 Google OpenID 為例,從 OAuth 2.0 拿回來的 User Profile 就會有 email_verified
欄位 mapping 回 Cognito User Pool Attribute。但 Facebook 在某一版 User Profile API 把 email_verified
給拔掉了,也就是沒有對應的值可以回填到 Cognito。
Cognito 本身有提供 Pre Sign-up 時觸發 Lambda 將 autoVerifyEmail, autoVerifyPhone 值回給 Cognito 自動驗證的功能,但在 Federated sign up doesn’t set email_verified to true #5117 有講到 Federated Sign-up 時無法使用 Pre Sign-up autoVerify 的功能,但因為是執行 Lambda 所以 … 可以直接跑 SDK 修改 User Attributes
:
var aws = require('aws-sdk');
aws.config.update({region: 'eu-central-1'});
exports.handler = (event, context, callback) => {
if (event.request.userAttributes.email) {
if (event.request.userAttributes["cognito:user_status"] === "EXTERNAL_PROVIDER") {
const cognitoIdServiceProvider = new aws.CognitoIdentityServiceProvider({
apiVersion: '2016-04-18',
region: 'eu-central-1'
});
var params = {
UserAttributes: [
{
Name: "email_verified",
Value: "true"
}
],
UserPoolId: event.userPoolId,
Username: event.userName
}
cognitoIdServiceProvider.adminUpdateUserAttributes(params, function(err, data) {
if (err) {
callback(null, event);
} else {
callback(null, event);
}
});
} else {
callback(null, event);
}
} else {
callback(null, event);
}
};
這算是一個不漂亮,但很有效的 Workaround 解決方式。