简体   繁体   English

我应该将cognito用户池与其他社交idps联合,还是通过userpool本身进行社交登录

[英]Should I federate cognito user pools along with other social idps, or have social sign in via the userpool itself

I am building a social chat application and initially had a cognito user pool that was federated alongside Google/Facebook. 我正在构建一个社交聊天应用程序,最初有一个与Google / Facebook联合的cognito用户池。 I was storing user data based on the user-sub for cognito users and the identity id for google/facebook. 我正在根据cognito用户的用户sub和google / facebook的身份ID存储用户数据。 Then in my lambda-gql resolvers, I would authenticate via the AWS-sdk: 然后在我的lambda-gql解析器中,我将通过AWS-sdk进行身份验证:

  AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: process.env.IDENTITY_POOL_ID,
    Logins: {
      [`cognito-idp.us-east-1.amazonaws.com/${
        process.env.COGNITO_USERPOOL_ID
      }`]: Authorization,
    },
  });

Because all users are equal and I don't need fine grained controls over access to aws-resources, it seems like it would be preferable to instead have all authentication handled via the userpool and to get rid of the identity pool entirely. 因为所有用户都是相同的,并且我不需要对访问aws资源的细粒度控制,所以似乎最好通过用户池处理所有身份验证并完全摆脱身份池。

For example, if I wanted to ban a user's account, it seems that I would first have to lookup the provider based on identity-id and then perform a different action based on the provider. 例如,如果我想禁止用户的帐户,似乎我首先必须根据identity-id查找提供程序,然后根据提供程序执行不同的操作。

So my questions are: 1. Is this even possible? 所以我的问题是:1。这甚至可能吗? - https://github.com/aws-amplify/amplify-js/issues/565 - https://www.reddit.com/r/aws/comments/92ye5s/is_it_possible_to_add_googlefacebook_user_to/ - https://github.com/aws-amplify/amplify-js/issues/565 - https://www.reddit.com/r/aws/comments/92ye5s/is_it_possible_to_add_googlefacebook_user_to/

There seems to be a lot of confusion, and the aws docs are less clear than usual (which isn't much imo). 似乎存在很多混淆,并且aws文档不像通常那样清晰(这不是很多)。 https://docs.aws.amazon.com/cognito/latest/developerguide/authentication.html https://docs.aws.amazon.com/cognito/latest/developerguide/authentication.html

It seems that there is clearly a method to do this. 似乎有一种方法可以做到这一点。 I followed the above guide and am getting errors with the hosted UI endpoint, but that's probably on me ( https://forums.aws.amazon.com/thread.jspa?threadID=262736 ). 我遵循了上面的指南,并且在托管的UI端点上出现了错误,但这可能在我身上( https://forums.aws.amazon.com/thread.jspa?threadID=262736 )。 However, I do not want the hosted UI endpoint, I would like cognito users to sign in through my custom form and then social sign in users to click a "continue with fb" button and have that automatically populate my userpool. 但是,我不希望托管的UI端点,我希望cognito用户通过我的自定义表单登录,然后社交登录用户点击“继续使用fb”按钮并自动填充我的用户池。

Then replace the code above with the following to validate all users: 然后使用以下代码替换上面的代码以验证所有用户:

const validate = token => new Promise(async (resolve) => {
  const {
    data: { keys },
  } = await axios(url);

  const { sub, ...res } = decode(token, { complete: true });
  const { kid } = decode(token, { header: true });
  const jwk = R.find(R.propEq('kid', kid))(keys);
  const pem = jwkToPem(jwk);
  const response = res && res['cognito:username']
    ? { sub, user: res['cognito:username'] }
    : { sub };
  try {
    await verify(token, pem);
    resolve(response);
  } catch (error) {
    resolve(false);
  }
});
  1. If it is possible, what is the correct mechanism that would replace the following: 如果可能,那么取代以下内容的正确机制是什么:
      Auth.federatedSignIn('facebook', { token: accessToken, expires_at }, user)
        .then(credentials => Auth.currentAuthenticatedUser())
        .then((user) => {
          onStateChange('signedIn', {});
        })
        .catch((e) => {
          console.log(e);
        });

From what I have seen, there does not appear to be a method with Amplify to accomplish this. 从我所看到的,似乎没有一种方法使用Amplify来实现这一目标。 Is there some way to do this with the aws-sdk? 有没有办法用aws-sdk做到这一点? What about mapping the callback from the facebook api to create a cognito user client-side? 如何映射来自facebook api的回调以创建一个cognito用户客户​​端? It seems like that could get quite messy. 看起来这可能会变得非常混乱。

  1. If there is no mechanism to accomplish the above, should I federate cognito users with social sign ins? 如果没有机制来实现上述目标,我应该将Cognito用户与社交登录联合起来吗?

  2. And then what should I use to identify users in my database? 然后我应该用什么来识别我的数据库中的用户? Am currently using username and sub for cognito and identity id for federated users. 我目前正在为联合用户使用username和sub for cognito和identity id。 Extracting the sub from the Auth token server-side and then on the client: 从Auth令牌服务器端提取子,然后在客户端上提取:

  Auth.currentSession()
    .then((data) => {
      const userSub = R.path(['accessToken', 'payload', 'sub'], data);
      resolve(userSub);
    })
    .catch(async () => {
      try {
        const result = await Auth.currentCredentials();
        const credentials = Auth.essentialCredentials(result);
        resolve(removeRegionFromId(credentials.identityId));
      } catch (error) {
        resolve(false);
      }
    });

If anyone could provide the detailed authoritative answer I have yet to find concerning the use of cognito user pools in place of federating that would be great. 如果有人能够提供详细的权威答案,我还没有找到关于使用认知用户池来代替联合会这将是伟大的。 Otherwise a general outline of the correct approach to take would be much appreciated. 否则,将非常感谢正确的采取方法的大纲。

Here's what I ended up doing for anyone in a similar position, this isn't comprehensive: 这就是我最后为类似职位的人做的事情,这不是很全面:

  1. Create a userpool, do not specify client secret or any required attributes that could conflict with whats returned from Facebook/Google. 创建一个用户池,不要指定客户端密钥或任何可能与Facebook / Google返回的内容冲突的必需属性。
  2. Under domains, in the Cognito sidebar, add what ever you want yours to be. 在域下,在Cognito侧边栏中,添加您希望的内容。
  3. The add your identity provided from Cognito, for FB you want them to be comma seperated like so: openid, phone, email, profile, aws.cognito.signin.user.admin 添加您从Cognito提供的身份,对于FB,您希望它们像以下一样以逗号分隔:openid,phone,email,profile,aws.cognito.signin.user.admin
  4. Enable FB from app client settings, select implicit grant. 从应用客户端设置启用FB,选择隐式授权。 I belive, but am not positive, openid is required for generating a access key and signin.user.admin for getting a RS256 token to verify with the public key. 我相信,但不是肯定的,生成访问密钥需要openid,并且使用signin.user.admin获取RS256令牌以使用公钥进行验证。
  5. The from FB dev console, https://yourdomain.auth.us-east-1.amazoncognito.com/oauth2/idpresponse , as valid oauth redirects. 来自FB dev控制台, https ://yourdomain.auth.us-east-1.amazoncognito.com/oauth2/idpresponse,作为有效的oauth重定向。
  6. Then, still on FB, go to settings (general not app specific), and enter https://yourdomain.auth.us-east-1.amazoncognito.com/oauth2/idpresponse 然后,仍然在FB上,转到设置(一般不是特定于应用),然后输入https://yourdomain.auth.us-east-1.amazoncognito.com/oauth2/idpresponse
  7. https://yourdomain.auth.us-east-1.amazoncognito.com/oauth2/idpresponse for your site url. https://yourdomain.auth.us-east-1.amazoncognito.com/oauth2/idpresponse为您的网站网址。
  8. Then for the login in button you can add the following code, 然后,对于登录按钮,您可以添加以下代码,
const authenticate = callbackFn => () => {
  const domain = process.env.COGNITO_APP_DOMAIN;
  const clientId = process.env.COGNITO_USERPOOL_CLIENT_ID;
  const type = 'token';
  const scope = 'openid phone email profile aws.cognito.signin.user.admin';
  const verification = generateVerification();
  const provider = 'Facebook';
  const callback = `${window.location.protocol}//${
    window.location.host
  }/callback`;

  const url = `${domain}/authorize?identity_provider=${provider}&response_type=${type}&client_id=${clientId}&redirect_uri=${callback}&state=${verification}&scope=${scope}`;
  window.open(url, '_self');
};
  1. Then on your redirect page: 然后在您的重定向页面上:
  useEffect(() => {
    // eslint-disable-next-line no-undef
    if (window.location.href.includes('#access_token')) {
      const callback = () => history.push('/');
      newAuthUser(callback);
    }
  }, []);
/* eslint-disable no-undef */
import { CognitoAuth } from 'amazon-cognito-auth-js';
import setToast from './setToast';

export default (callback) => {
  const AppWebDomain = process.env.COGNITO_APP_DOMAIN;
  // https://yourdomainhere.auth.us-east-1.amazoncognito.com'
  const TokenScopesArray = [
    'phone',
    'email',
    'profile',
    'openid',
    'aws.cognito.signin.user.admin',
  ];
  const redirect = 'http://localhost:8080/auth';
  const authData = {
    ClientId: process.env.COGNITO_USERPOOL_CLIENT_ID,
    AppWebDomain,
    TokenScopesArray,
    RedirectUriSignIn: redirect,
    RedirectUriSignOut: redirect,
    IdentityProvider: 'Facebook',
    UserPoolId: process.env.COGNITO_USERPOOL_ID,
    AdvancedSecurityDataCollectionFlag: true,
  };
  const auth = new CognitoAuth(authData);

  auth.userhandler = {
    onSuccess() {
      setToast('logged-in');
      callback();
    },
    onFailure(error) {
      setToast('auth-error', error);
      callback();
    },
  };

  const curUrl = window.location.href;
  auth.parseCognitoWebResponse(curUrl);
};

You can then use Auth.currentSession() to get user attributes from the client. 然后,您可以使用Auth.currentSession()从客户端获取用户属性。

  1. Then server-side you can validate all user like so: 然后在服务器端,您可以像这样验证所有用户:
const decode = require('jwt-decode');
const jwt = require('jsonwebtoken');
const jwkToPem = require('jwk-to-pem');
const axios = require('axios');
const R = require('ramda');
const logger = require('./logger');

const url = `https://cognito-idp.us-east-1.amazonaws.com/${
  process.env.COGNITO_USERPOOL_ID
}/.well-known/jwks.json`;

const verify = (token, n) => new Promise((resolve, reject) => {
  jwt.verify(token, n, { algorithms: ['RS256'] }, (err, decoded) => {
    if (err) {
      reject(new Error('invalid_token', err));
    } else {
      resolve(decoded);
    }
  });
});

const validate = token => new Promise(async (resolve) => {
  const {
    data: { keys },
  } = await axios(url);

  const { sub, ...res } = decode(token, { complete: true });
  const { kid } = decode(token, { header: true });
  const jwk = R.find(R.propEq('kid', kid))(keys);
  const pem = jwkToPem(jwk);
  const response = res && res['cognito:username']
    ? { sub, user: res['cognito:username'] }
    : { sub };
  try {
    await verify(token, pem);

    resolve(response);
  } catch (error) {
    logger['on-failure']('CHECK_CREDENTIALS', error);
    resolve(false);
  }
});

const checkCredentialsCognito = Authorization => validate(Authorization);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM