web-dev-qa-db-ja.com

Cognito Federated Identitiesでユーザーのメールアドレスにアクセスするにはどうすればよいですか?

訪問者がGoogleやFacebookでログインできるようにする基本的なWebサイト(AWSではサーバーレス)をセットアップしようとしています。現在、S3、フェデレーションIDを使用するCognito、API Gateway、Lambda(NodeJS)をDynamoDBで使用することを計画しています。クライアントアプリはAngularを使用します。

GoogleとFacebookでソーシャルログインを行っています。現在、ユーザーが初めてログインするときに、cognitoId、名前、プロフィール写真のURLなどを含む行を「users」テーブルに挿入しています。

また、ユーザーが異なるプロバイダーを使用してログインし、同じデータを表示できるように、cognitoIdのようなものではなく、電子メールアドレスをキーとしてユーザーの情報を保存することも良い設計だと思います。したがって、認証されたユーザーの電子メールアドレスを知る必要がありますが、それはユーザーから直接ではなく、Cognitoからのものである必要があると思います(クライアントアプリは信頼されるべきではないため)。

ユーザープールで必要に応じてそのフィールドを有効にしたため、Cognitoはユーザーのメールアドレスを保存していると思います。

私が抱えている問題は、Cognitoからユーザーのメールアドレスを取得する方法に関する情報が見つからないことです。

私が来た最も近いのはこの投稿ですが、アクセストークンがどこにも見つかりません: cognito ID IDを使用してユーザー属性(ユーザー名、電子メールなど)を取得する方法

この投稿は、GetUserを使用できる可能性があることを示していますが、AccessTokenがどこから来ているのかわかりません: AWS cognito IDを使用してユーザーを作成

GetUserとAccessTokenを使用する必要がある場合、それはどこから来て、どのように生成しますか?クライアントからのものですか、それともAWS.config.credentialsを使用してLambdaで取得できますか?

私はしばらくの間これを理解しようとしてきました、そして私は本当に単純な何かを逃しているように感じています!

15
destro

まず、(Cognitoコンソールで)Cognito IDプロバイダーに移動し、プロバイダーの「AuthorizeScope」が適切であることを確認します。たとえば、Googleプロバイダーをクリックした場合、承認スコープは「プロファイルメールopenid」である可能性があります。スコープはプロバイダーによって異なりますが、使用しているスコープが何であれ、ユーザーの電子メールへのアクセスを提供する必要があります。

ユーザーが外部IDプロバイダー(Facebookなど)でログインすると、CognitoはFacebookとネゴシエートし、Cognitoコンソールの[アプリクライアント設定]部分で設定されているコールバックURLを呼び出します。そのコールバックには「コード」と呼ばれるパラメータが含まれています。このパラメータは、私のCognitoを作成したコールバックのURLに設定されています。コードはOAuthトークンです。

これで、クライアントにOAuthトークンがあり、POSTそれを AWSトークンエンドポイント に送信する必要があります。トークンエンドポイントは-を返します。 応答内の3つの新しいトークン ; JWT IDトークン、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
F_SO_K

メールを受け取るには、IDプロバイダー(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());
    }
});

CognitoIdentityはメールを保存しません。

0
Ricardo Gamboa

また、ユーザープールに属性マッピングを追加する必要があります。マッピングの追加を忘れていないか確認してください。ユーザープール設定内の「フェデレーション」の下に「属性マッピング」タブがあります。

0
Sobhan Jachuck