Skip to content

Instantly share code, notes, and snippets.

@viralsavaniIM
Last active December 7, 2022 18:25
Show Gist options
  • Save viralsavaniIM/aa453b85ebdf915af163c356e8e99a2e to your computer and use it in GitHub Desktop.
Save viralsavaniIM/aa453b85ebdf915af163c356e8e99a2e to your computer and use it in GitHub Desktop.
Facebank OAuth Integration Guide: AWS Cognito Post Authentication trigger
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2021 Facebank, Inc. //
// All rights reserved. //
// //
// Redistribution and use in source and binary forms, with or without //
// modification, are permitted provided that the following conditions are //
// met: //
// //
// Redistributions of source code must retain the above copyright //
// notice, this list of conditions and the following disclaimer. //
// Redistributions in bytecode form must reproduce the above copyright //
// notice, this list of conditions and the following disclaimer in //
// the documentation and/or other materials provided with the //
// distribution. //
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS //
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT //
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR //
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT //
// HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, //
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS //
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND //
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR //
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE //
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH //
// DAMAGE. //
///////////////////////////////////////////////////////////////////////////////
const AWS = require("aws-sdk");
const cognitoIdp = new AWS.CognitoIdentityServiceProvider();
/**
* The name you gave the Facebank OIDC provider when conneting it in the Cognito
*/
const FACEBANK_COGNITO_PROVIDER_NAME = "FacebankApp";
/**
* The key of client metadata you pass to the cognito's authenticate method
* Note: This SHOULD BE same as client metadata key sent by the client (mobile app, web app, etc)
*/
const EXTERNAL_IDENTITY_LINK_CLIENT_METADATA_KEY = 'CONFIRM_IDENTITY_LINK';
/**
* The prefix of the string after which the valid token starts
* Note: This SHOULD BE same as `EXTERNAL_IDENTITY_LINK_TOKEN_PREFIX` in PreSignUp Trigger
*/
const EXTERNAL_IDENTITY_LINK_TOKEN_PREFIX = `CONFIRM_IDENTITY_LINK_`;
/**
* Runs AdminLinkProviderForUser linking current user in cognito to user retrurned by external provider
* {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#adminLinkProviderForUser-property}
* @param {String} userPoolId Cognito pool id
* @param {String} cognitoNativeUserID user id of a native Cognito user
* @param {String} providerName name of the Federated provider
* @param {String} providerUserID user id given to the user from the Federated provider
*/
async function adminLinkUserAccounts(
userPoolId,
cognitoNativeUserID,
providerName,
providerUserID
) {
const params = {
DestinationUser: {
ProviderAttributeValue: cognitoNativeUserID,
ProviderName: "Cognito",
},
SourceUser: {
ProviderAttributeName: "Cognito_Subject",
ProviderAttributeValue: providerUserID,
ProviderName: providerName,
},
UserPoolId: userPoolId,
};
return new Promise((resolve, reject) => {
cognitoIdp.adminLinkProviderForUser(params, (err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
});
});
}
/**
* Will convert the given string payload to utf-8
* @param {String} payload, base-64 encoded string
* @return {String} utf-8 string
*/
function convertBase64ToUtf8(payload) {
return Buffer.from(payload, 'base64').toString('utf8');
}
exports.handler = async (event, context, callback) => {
console.log(`Event - \n ${JSON.stringify(event, null, 2)}`);
const userPoolId = event.userPoolId;
const cognitUserName = event.userName;
/**
* Check if the incoming request has `clientMetada` set.
* If so, check if the `clientMetadata` has an object with `EXTERNAL_IDENTITY_LINK_CLIENT_METADATA_KEY`
*/
if (event.request.hasOwnProperty('clientMetadata') && event.request.clientMetadata.hasOwnProperty(EXTERNAL_IDENTITY_LINK_CLIENT_METADATA_KEY)) {
const externalIdentityLink = event.request.clientMetadata[EXTERNAL_IDENTITY_LINK_CLIENT_METADATA_KEY];
// exmaple: CONFIRM_IDENTITY_LINK_ZmFjZWJhbmtpbkOTZiNw==
// after replace: ZmFjZWJhbmtpbkOTZiNw==
const encodedPayload = externalIdentityLink.replace(EXTERNAL_IDENTITY_LINK_TOKEN_PREFIX, '');
//example: facebankapp_412639q6-c1df-4cp9-9425-09uweob1d96b7
const decodedPayload = convertBase64ToUtf8(encodedPayload);
// after split: 412639q6-c1df-4cp9-9425-09uweob1d96b7
const [decodedExternalProvideName, decodedExternalUserId] = decodedPayload.split("_");
// extra check
if (FACEBANK_COGNITO_PROVIDER_NAME.toLowerCase() === decodedExternalProvideName.toLowerCase()) {
await adminLinkUserAccounts(
userPoolId,
cognitUserName,
FACEBANK_COGNITO_PROVIDER_NAME,
decodedExternalUserId
);
}
}
// Return to Amazon Cognito
callback(null, event);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment