Как получить доступ к электронному адресу пользователя в Cognito Federated Identities?

Я пытаюсь создать базовый веб-сайт (без сервера AWS), который позволит посетителям войти в систему с Google и/или Facebook. В настоящее время я планирую использовать S3, Cognito с Federated Identities, API Gateway, Lambda (NodeJS), с DynamoDB. Клиентское приложение будет использовать Angular.

У меня есть социальный логин с Google и Facebook, и в настоящее время я вставляю строку в таблицу "users", когда пользователь регистрируется в первый раз, включая URL-адрес cognitoId, имя, URL профиля и т.д.

Я также считаю, что было бы хорошей конструкцией для хранения информации о пользователе с их адресом электронной почты в качестве ключа, а не с помощью чего-то вроде cognitoId, чтобы пользователь мог войти в систему с помощью разных провайдеров и увидеть одни и те же данные. Поэтому мне нужно знать аутентифицированный адрес электронной почты пользователя, но я полагаю, что он должен поступать из Cognito, а не напрямую от пользователя (поскольку клиентскому приложению не следует доверять).

Я считаю, что Cognito хранит адрес электронной почты пользователя, потому что я включил это поле, как требуется, в пул пользователей.

Проблема заключается в том, что я не могу найти информацию о том, как получить адрес электронной почты пользователя от Cognito.

Самое близкое, что я пришел, это сообщение, но я не могу найти токен доступа где угодно: Как получить пользовательские атрибуты (имя пользователя, адрес электронной почты и т.д.) с помощью cognito идентификатор идентификатора

Это сообщение указывает, что я могу использовать GetUser, но я снова не знаю, откуда приходит AccessToken: создание пользователя с использованием азбуки AWS cognito

Если мне нужно использовать GetUser и AccessToken, откуда оно взялось, и как его создать? Это происходит от клиента, или я могу получить его в Lambda, используя AWS.config.credentials?

Я пытался понять это сейчас, и мне кажется, что я пропустил что-то очень простое!

Ответы

Ответ 1

Во-первых, зайдите в провайдера Cognito Identity (на консоли Cognito) и убедитесь, что ваш провайдер "Authorize Scope" подходит. Например, если вы нажмете на поставщика Google, область полномочий Authorize может быть "профилем электронной почты openid". Объем зависит от поставщика, но независимо от того, какой объем вы используете, он должен предоставлять доступ к электронной почте пользователей.

Когда ваш пользователь входит в систему с внешним провайдером идентификации (скажем, Facebook), Cognito ведет переговоры с Facebook, а затем вызывает ваш URL обратного вызова, который задан в разделе "Параметры клиента клиента" консоли Cognito. Этот обратный вызов содержит параметр "код" - параметр задается в URL-адресе Обратного вызова, сделанного моим Cognito. Код является токеном OAuth.

Теперь у вас есть токен OAuth в вашем клиенте, который вам нужен POST, чтобы конечная точка токена AWS. Конечная точка маркера возвращает три новых токена в ответе; токен идентификатора JWT, токен доступа JWT и токен обновления. Возьмите атрибут id_token из ответа конечной точки. Разберите этот id_token как строку json и возьмите элемент "email". Теперь у вас должен быть адрес электронной почты пользователей.

Вот мой рабочий пример в Java. Это сервлет, вызываемый обратным вызовом Cognito.

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, 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 не сохраняет электронные письма.