15

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

我使用 Google 和 Facebook 进行社交登录,目前我在用户第一次登录时在“用户”表中插入一行,其中包括 cognitoId、姓名、个人资料图片 URL 等。

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

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

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

我最接近的是这篇文章,但我在任何地方都找不到访问令牌:How to get user attributes (username, email, etc.) using cognito identity id

这篇文章表明我可能能够使用 GetUser,但我再次不知道 AccessToken 来自哪里:create user using AWS cognito identity

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

我一直在尝试解决这个问题一段时间,我觉得我错过了一些非常简单的东西!

4

3 回答 3

4

首先,进入 Cognito 身份提供商(在 Cognito 控制台中)并确保您的提供商“授权范围”是合适的。例如,如果您单击 Google 提供商,您的授权范围可能是“个人资料电子邮件 openid”。范围因提供商而异,但无论您使用什么范围,它都必须提供对用户电子邮件的访问权限。

当您的用户使用外部身份提供商(比如说 Facebook)登录时,Cognito 会与 Facebook 协商,然后调用您的回调 URL,该 URL 在 Cognito 控制台的“应用程序客户端设置”部分中设置。该回调包含一个名为“代码”的参数 - 该参数设置在回调的 URL 中,使我的 Cognito。该代码是 OAuth 令牌。

现在您的客户端中有一个 OAuth 令牌,您需要将其发布到AWS Token Endpoint。令牌端点在响应中返回三个新令牌;一个 JWT ID 令牌、一个 JWT 访问令牌和一个刷新令牌。从端点响应中获取“id_token”属性。将该 id_token 解析为 json 字符串,并获取 'email' 元素。现在您应该拥有用户的电子邮件地址。

这是我在 Java 中的工作示例。这是一个被 Cognito 回调调用的 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();
    }
}
于 2017-12-13T14:57:50.040 回答
2

You also need to add Attribute mappings in your user pool. Check if you have forgotten to add the mappings. You can find "attribute mappings" tab under "federation" inside your User Pool settings

于 2018-11-08T04:51:57.717 回答
0

要获取电子邮件,您必须向身份提供商(facebook、google、用户池)请求它。

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

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 不会保存电子邮件。

于 2017-04-06T23:32:16.563 回答