首页 文章

如何在Cognito Federated Identities中访问用户的电子邮件地址?

提问于
浏览
14

我正在尝试 Build 一个基本网站(AWS上无服务器),允许访问者使用Google和/或Facebook登录 . 目前我计划使用S3,Cognito with Federated Identities,API Gateway,Lambda(NodeJS)和DynamoDB . 客户端应用程序将使用Angular .

我有谷歌和Facebook工作的社交登录,目前我在第一次登录时,在“用户”表中插入一行,其中包括cognitoId,名称, Profiles 图片URL等 .

我还认为以用他们的电子邮件地址作为密钥存储用户信息是一个很好的设计,而不是像cognitoId那样,以便用户可以使用不同的提供商登录并查看相同的数据 . 所以我需要知道经过身份验证的用户的电子邮件地址,但我认为它应该来自Cognito而不是直接来自用户(因为客户端应用程序不应该被信任) .

我相信Cognito存储用户的电子邮件地址,因为我在用户池中根据需要启用了该字段 .

我遇到的问题是我找不到任何有关如何从Cognito获取用户电子邮件地址的信息 .

我最接近任何地方找到访问令牌:How to get user attributes (username, email, etc.) using cognito identity id

这篇文章表明我可以使用GetUser,但我再次不知道AccessToken的来源:creating user using AWS cognito identity

如果我确实需要使用GetUser和AccessToken,它来自何处,以及如何生成它?它是来自客户端,还是我可以使用AWS.config.credentials在Lambda中获取它?

我一直试图弄清楚这一段时间,我觉得我错过了一些非常简单的东西!

3 回答

  • 0

    首先,进入Cognito Identity提供程序(在Cognito控制台中)并确保您的提供程序“授权范围”是合适的 . 例如,如果您点击Google提供商,则授权范围可能是“ Profiles 电子邮件openid” . 范围因提供商而异,但无论您使用何种范围,它都必须提供对用户电子邮件的访问权限 .

    当您的用户使用外部身份提供商(简称Facebook)登录时,Cognito会与Facebook协商,然后调用您的回拨URL,该URL在Cognito控制台的“App Client设置”部分中设置 . 该Callback包含一个名为“code”的参数 - 该参数在回调制作我的Cognito的URL中设置 . 代码是OAuth令牌 .

    现在您的客户端中有一个OAuth令牌,您需要将其发送到AWS Token Endpoint . 令牌 endpoints 返回three new tokens in the response; JWT ID令牌,JWT访问令牌和刷新令牌 . 从 endpoints 响应中获取"id_token"属性 . 将id_token解析为json字符串,并取'email'元素 . 现在您应该拥有用户的电子邮件地址 .

    这是我在Java中的工作示例 . 这是一个由Cognito Callback调用的servlet .

    import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
    import com.nimbusds.jwt.SignedJWT;
    import net.minidev.json.JSONArray;
    import org.json.simple.JSONObject;
    import org.json.simple.parser.JSONParser;
    import org.json.simple.parser.ParseException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.URL;
    import java.net.URLConnection;
    
    public class CognitoLandingServlet extends HttpServlet {
    
        static final Logger LOG = LoggerFactory.getLogger(CognitoLandingServlet.class);
        private static final long serialVersionUID = 1L;
    
        public CognitoLandingServlet() {
            super();
        }
    
        @Override
        protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
                throws ServletException, IOException {
    
            // Get the OpenID Connect (OAuth2) token passed back from the hosted Cognito
            // Login Page
            final String code = request.getParameter("code");
            LOG.debug(String.format("Cognito OAuth2 code received from Cognito: %s.", code));
    
            if (code != null) {
                // do nothing, we have a code as expected
            } else {
                LOG.debug(String.format(
                        "Landing page requested without a Cognito code, the request probably didn't come from Cognito"));
                // we dont have a token so redirect the user to the application sign in
                // page
                request.getRequestDispatcher("/signin").forward(request, response);
            }
            // Exchange the OIDC token for Cognito Access and ID JWT tokens using AWS
            // Token
            // Endpoint
            // There does not appear to be a Java SDK to handle this :(
            final String cognitoClientId = System.getProperty("CognitoClientId");
            final String redirectUri = System.getProperty("CognitoCallBackUrl");
            final String awsTokenEndpoint = System.getProperty("AwsTokenEndpoint");
            final String jwt = swapOauthForJWT(cognitoClientId, code, redirectUri, awsTokenEndpoint);
            // Complete the login using the JWT token string
            loginWithJWT(jwt, request, response);
        }
    
        @Override
        protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
                throws ServletException, IOException {
    
        }
    
        private void loginWithJWT(final String jwtString, final HttpServletRequest request,
                                  final HttpServletResponse response) {
    
            final JSONParser parser = new JSONParser();
            SignedJWT signedIdJWT;
    
            try {
                // Take the id token
                final JSONObject json = (JSONObject) parser.parse(jwtString);
                final String idToken = (String) json.get("id_token");
    
                // Access token is not currently used
                // String accessToken = (String) json.get("access_token");
    
                // Process the id token
                signedIdJWT = SignedJWT.parse(idToken);
                final String userId = signedIdJWT.getJWTClaimsSet().getSubject();
    
                // Start NEW Session and start adding attributes
                final HttpSession session = request.getSession(true);
                session.setAttribute("userId", userId);
    
                final String cognitoUsername = (String) signedIdJWT.getJWTClaimsSet()
                        .getClaim("cognito:username");
                if (cognitoUsername != null) {
                    user.setUserName(cognitoUsername);
                    session.setAttribute("username", cognitoUsername);
                }
                final String email = (String) signedIdJWT.getJWTClaimsSet().getClaim("email");
                if (email != null) {
                    user.setEmail(email);
                    session.setAttribute("email", email);
                }
                // Save the user to a database (code removed for stack overflow)
    
                //request.getRequestDispatcher("/dashboard").forward(request, response);
                response.sendRedirect("/dashboard");
    
                LOG.info(
                        String.format("A user with userid %s and email %s successfully signed in", userId, email));
    
            } catch (final java.text.ParseException e) {
                LOG.error(
                        String.format("The JWT token could not be parsed by JOSE library. %s", e.getMessage()));
            } catch (final ParseException e) {
                LOG.error(String.format("The JWT token could not be parsed by JSON simple library. %s",
                        e.getMessage()));
            } catch (final IOException e) {
                LOG.error(String.format("Failed to request webpage at the end of the login process - io. %s",
                        e.getMessage()));
            }
        }
    
        private String swapOauthForJWT(final String cognitoClientId, final String oauthCode,
                                       final String redirectUri, final String awsTokenEndpoint) throws IOException {
    
            // Build the URL to post to the AWS Token Endpoint
            final String urlParameters = String.format(
                    "Content-Type=application/x-www-form-urlencoded&grant_type=authorization_code&client_id=%s&code=%s&redirect_uri=%s",
                    cognitoClientId, oauthCode, redirectUri);
            LOG.debug(String.format("User is swapping OAuth token for a JWT using URL %s", urlParameters));
            final URL url = new URL(awsTokenEndpoint);
            final URLConnection conn = url.openConnection();
            conn.setDoOutput(true);
            final OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
            writer.write(urlParameters);
            writer.flush();
            // Read the data returned from the AWS Token Endpoint
            final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            final StringBuilder responseStrBuilder = new StringBuilder();
            String inputStr;
            while ((inputStr = reader.readLine()) != null) {
                responseStrBuilder.append(inputStr);
            }
            // Close the connection
            writer.close();
            reader.close();
            LOG.debug(String.format("Finished swapping OAuth token for a JWT"));
    
            return responseStrBuilder.toString();
        }
    }
    
  • 2

    要获取电子邮件,您必须向身份提供商(Facebook,谷歌,用户池)请求 .

    要从用户池中获取电子邮件,您必须执行以下操作:

    cognitoUser.getUserAttributes(function(err, result) {
        if (err) {
            alert(err);
            return;
        }
        for (i = 0; i < result.length; i++) {
            console.log('attribute ' + result[i].getName() + ' has value ' + result[i].getValue());
        }
    });
    

    Cognito Identity不保存电子邮件 .

  • 0

    您还需要在用户池中添加属性映射 . 检查您是否忘记添加映射 . 您可以在“用户池”设置中的“联盟”下找到“属性映射”选项卡

相关问题