阻止用户自行使用联盟身份提供商(FIP)进行注册,但允许用户使用FIP登录(如果由管理员添加) [英] Prevent users from signing up on their own with federated identity providers (FIP) but allow sign in with a FIP if added by an administrator

查看:132
本文介绍了阻止用户自行使用联盟身份提供商(FIP)进行注册,但允许用户使用FIP登录(如果由管理员添加)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经在Amazon Cognito中为我的Web应用程序设置了一个用户池.该应用程序不是公开的,仅允许特定用户登录.AmazonConsole中该用户池的策略仅允许管理员创建新用户.

I've set up a user pool in Amazon Cognito for my web application. The application is not meant to be public and only specific users are allowed to sign in. The policies of that user pool in the Amazon Console allow only administrators to create new users.

我已经通过Facebook和Google实施了登录.Cognito确实确实允许用户使用这些联合身份提供者登录应用程序,这很棒.但是,似乎拥有Facebook或Google帐户的任何人现在都可以注册自己.

I've implemented sign in through Facebook and Google. Cognito does indeed let users sign into the application with these federated identity providers, which is great. However, it seems that anybody with a Facebook or Google account can sign themselves up now.

因此,一方面,人们无法使用常规的Cognito凭据创建自己的用户,但另一方面,如果他们使用联合身份提供者,则可以在Cognito中创建新用户.

So, on one hand, people can not create their own user with regular Cognito credentials but, on the other hand, they can create a new user in Cognito if they use a federated identity provider.

是否可以将通过Facebook或Google登录到我的应用程序的方式限制为仅用户池中已经存在的用户?这样,管理员仍将能够控制到底谁可以访问该帐户.应用.我想使用联合身份提供者共享的电子邮件来检查是否允许他们登录.

Is there a way to restrict signing into my application with Facebook or Google to only users that already exist in the user pool? That way, administrators would still be able to control who exactly can access the application. I would like to use the email shared by the federated identity provider to check if they are allowed to sign in.

该应用程序是使用CloudFront设置的.我编写了一个Lambda,它可以拦截原始请求,以检查Cookie中的令牌并根据访问令牌的有效性授权访问.

The application is set up with CloudFront. I've written a Lambda that intercepts origin requests to check for tokens in cookies and authorize access based on the validity of the access token.

我想避免编写其他代码来防止用户使用Facebook或Google进行注册,但是如果没有其他方法,我将更新Lambda.

I would like to avoid writing additional code to prevent users to sign themselves up with Facebook or Google but if there is no other way, I'll update the Lambda.

推荐答案

因此,这是我最终编写的预注册Lambda触发器.我花时间使用 async / await 代替Promises.它运行良好,但存在一个已记录的错误,在该错误中,Cognito会强制首次使用外部身份提供程序的用户进行注册,然后进行签名(这样他们会两次看到auth页),然后才能访问该应用程序.我对如何解决此问题有一个想法,但与此同时,下面的Lambda可以满足我的要求.而且,事实证明,来自使用Amazon登录"的ID使用正确的大小写,因此我不得不手动重新格式化该ID,这是不幸的.让我觉得Cognito触发器的实现有点麻烦.

So, here is the pre sign-up Lambda trigger I ended up writing. I took the time to use async/await instead of Promises. It works nicely, except that there is a documented bug where Cognito forces users who use external identity providers for the first time to sign up and then sign (so they see the auth page twice) before they can access the application. I have an idea on how to fix this but in the meantime the Lambda below does what I wanted. Also, it turns out that the ID that comes from Login With Amazon is using the correct case, so I had to re-format that ID by hand, which is unfortunate. Makes me feel like the implementation of the triggers for Cognito is a bit buggy.

const PROVIDER_MAP = new Map([
    ['facebook', 'Facebook'],
    ['google', 'Google'],
    ['loginwithamazon', 'LoginWithAmazon'],
    ['signinwithapple', 'SignInWithApple']
]);

async function getFirstCognitoUserWithSameEmail(event) {
    const { region, userPoolId, request } = event;

    const AWS = require('aws-sdk');
    const cognito = new AWS.CognitoIdentityServiceProvider({
        region
    });

    const parameters = {
        UserPoolId: userPoolId,
        AttributesToGet: ['sub', 'email'], // We don't really need these attributes
        Filter: `email = "${request.userAttributes.email}"` // Unfortunately, only one filter can be applied at once
    };

    const listUserQuery = await cognito.listUsers(parameters).promise();

    if (!listUserQuery || !listUserQuery.Users) {
        return { error: 'Could not get list of users.' };
    }

    const { Users: users } = listUserQuery;

    const cognitoUsers = users.filter(
        user => user.UserStatus !== 'EXTERNAL_PROVIDER' && user.Enabled
    );

    if (cognitoUsers.length === 0) {
        console.log('No existing enabled Cognito user with same email address found.');
        return {
            error: 'User is not allowed to sign up.'
        };
    }

    if (cognitoUsers.length > 1) {
        cognitoUsers.sort((a, b) =>
            a.UserCreateDate > b.UserCreateDate ? 1 : -1
        );
    }

    console.log(
        `Found ${cognitoUsers.length} enabled Cognito user(s) with same email address.`
    );

    return { user: cognitoUsers[0], error: null };
}

// Only external users get linked with Cognito users by design
async function linkExternalUserToCognitoUser(event, existingUsername) {
    const { userName, region, userPoolId } = event;

    const [
        externalIdentityProviderName,
        externalIdentityUserId
    ] = userName.split('_');

    if (!externalIdentityProviderName || !externalIdentityUserId) {
        console.error(
            'Invalid identity provider name or external user ID. Should look like facebook_123456789.'
        );
        return { error: 'Invalid external user data.' };
    }

    const providerName = PROVIDER_MAP.get(externalIdentityProviderName);

    let userId = externalIdentityUserId;
    if (providerName === PROVIDER_MAP.get('loginwithamazon')) {
        // Amazon IDs look like amzn1.account.ABC123DEF456
        const [part1, part2, amazonId] = userId.split('.');
        const upperCaseAmazonId = amazonId.toUpperCase();
        userId = `${part1}.${part2}.${upperCaseAmazonId}`;
    }

    const AWS = require('aws-sdk');
    const cognito = new AWS.CognitoIdentityServiceProvider({
        region
    });

    console.log(`Linking ${userName} (ID: ${userId}).`);

    const parameters = {
        // Existing user in the user pool to be linked to the external identity provider user account.
        DestinationUser: {
            ProviderAttributeValue: existingUsername,
            ProviderName: 'Cognito'
        },
        // An external identity provider account for a user who does not currently exist yet in the user pool.
        SourceUser: {
            ProviderAttributeName: 'Cognito_Subject',
            ProviderAttributeValue: userId,
            ProviderName: providerName // Facebook, Google, Login with Amazon, Sign in with Apple
        },
        UserPoolId: userPoolId
    };

    // See https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminLinkProviderForUser.html
    await cognito.adminLinkProviderForUser(parameters).promise();

    console.log('Successfully linked external identity to user.');

    // TODO: Update the user created for the external identity and update the "email verified" flag to true. This should take care of the bug where users have to sign in twice when they sign up with an identity provider for the first time to access the website.
    // Bug is documented here: https://forums.aws.amazon.com/thread.jspa?threadID=267154&start=25&tstart=0

    return { error: null };
}

module.exports = async (event, context, callback) => {
    // See event structure at https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html
    const { triggerSource } = event;

    switch (triggerSource) {
        default: {
            return callback(null, event);
        }
        case 'PreSignUp_ExternalProvider': {
            try {
                const {
                    user,
                    error: getUserError
                } = await getFirstCognitoUserWithSameEmail(event);

                if (getUserError) {
                    console.error(getUserError);
                    return callback(getUserError, null);
                }

                const {
                    error: linkUserError
                } = await linkExternalUserToCognitoUser(event, user.Username);

                if (linkUserError) {
                    console.error(linkUserError);
                    return callback(linkUserError, null);
                }

                return callback(null, event);
            } catch (error) {
                const errorMessage =
                    'An error occurred while signing up user from an external identity provider.';
                console.error(errorMessage, error);

                return callback(errorMessage, null);
            }
        }
    }
};

这篇关于阻止用户自行使用联盟身份提供商(FIP)进行注册,但允许用户使用FIP登录(如果由管理员添加)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆