Cognito Identity Pool 未認証ユーザーで 一時クレデンシャルを AWS SDK for Java 2.x で取得してみる

# はじめに

[前回の記事](https://qiita.com/sugimountaws/items/49ce163a703b555fd88a) では、Cognito Identity Pool で未認証ユーザーによる一時的なクレデンシャルの取得をやってみました。今回の記事では、よりプログラム側に寄せて、AWS SDK for Java 2.x を使って同様に未認証ユーザーによる一時的なクレデンシャルの取得を行ってみます。一時的に取得したクレデンシャルを使って、Textract を使った文字起こしを行ってみます。



Java の開発環境に利用している主なものは以下の通りです。

- AWS SDK for Java 2.x
- Maven
- Visual Studio Code



# Maven の設定

Maven を使って適当に Project を作成したあとに、`pom.xml` を編集します。変更点の概要は次の通りです。

- `maven.compiler.source` や `maven.compiler.target` を使って、コンパイルに使うバージョンを指定
- AWS SDK for Java 2.x を依存関係に指定
- Cognito, IAM, Textract を依存関係に指定

```xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>cognito-unauth-identity</artifactId>
  <version>@</version>
  <packaging>jar</packaging>

  <name>cognito-unauth-identity</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>bom</artifactId>
        <version>2.17.100</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>cognitoidentity</artifactId>
    </dependency>
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>cognitoidentityprovider</artifactId>
    </dependency>
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>iam</artifactId>
    </dependency>
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>textract</artifactId>
    </dependency>
  </dependencies>
</project>
```



# Java Code

まず全体的なコードを載せます。

```java
package com.example;

import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cognitoidentity.CognitoIdentityClient;
import software.amazon.awssdk.services.cognitoidentity.model.GetCredentialsForIdentityRequest;
import software.amazon.awssdk.services.cognitoidentity.model.GetCredentialsForIdentityResponse;
import software.amazon.awssdk.services.cognitoidentity.model.GetIdRequest;
import software.amazon.awssdk.services.cognitoidentity.model.GetIdResponse;
import software.amazon.awssdk.services.cognitoidentityprovider.model.CognitoIdentityProviderException;
import software.amazon.awssdk.services.textract.TextractClient;
import software.amazon.awssdk.services.textract.model.Block;
import software.amazon.awssdk.services.textract.model.DetectDocumentTextRequest;
import software.amazon.awssdk.services.textract.model.DetectDocumentTextResponse;
import software.amazon.awssdk.services.textract.model.Document;
import software.amazon.awssdk.services.textract.model.DocumentMetadata;
import software.amazon.awssdk.services.textract.model.TextractException;

public class App {
    public static void main(String[] args) {
        // Congiton Identity Pool ID を指定
        String identityPoolId = "ap-northeast-1:fc81b5de-4fa4-4087-adb5-4598fd25982a";

        // Cognito の Client を生成する。未認証のユーザーなので AnonymousCredentialsProvider を使う
        AwsCredentialsProvider provider = AnonymousCredentialsProvider.create();
        CognitoIdentityClient cognitoClient = CognitoIdentityClient.builder()
                .region(Region.AP_NORTHEAST_1)
                .credentialsProvider(provider)
                .build();

        // Identity Pool から、Identity ID を取得する
        String identityId = getIdentityID(cognitoClient, identityPoolId);

        // 取得した Identity ID を使って、一時的なクレデンシャルを取得する
        AWSCredentials awsCreds = getCredsForIdentity(cognitoClient, identityId);

        // 取得してきた 一時的なクレデンシャルを使って、Textract の Client を生成する。AccessKeyID と SecretAccessKey
        // と SessionToken を使って、AwsSessionCredentials を生成する
        AwsSessionCredentials credentials = AwsSessionCredentials.create(awsCreds.accessKeyID, awsCreds.secretAccessKey,
                awsCreds.sessitonToken);
        TextractClient textractClient = TextractClient.builder()
                .region(Region.US_EAST_1)
                .credentialsProvider((StaticCredentialsProvider.create(credentials)))
                .build();

        // Textract の文字起こしを行う
        detectDocText(textractClient, "textract001.png");

        // Client を閉じる
        cognitoClient.close();
        textractClient.close();
    }

    // Identity Pool から、Identity ID を取得する
    public static String getIdentityID(CognitoIdentityClient cognitoClient, String identityPoolId) {
        String identityId = null;

        try {
            GetIdRequest request = GetIdRequest.builder()
                    .identityPoolId(identityPoolId)
                    .build();

            GetIdResponse response = cognitoClient.getId(request);
            identityId = response.identityId();
            System.out.println("Identity ID " + identityId);

        } catch (CognitoIdentityProviderException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }

        return identityId;
    }

    // 取得した Identity ID を使って、一時的なクレデンシャルを取得する
    public static AWSCredentials getCredsForIdentity(CognitoIdentityClient cognitoClient,
            String identityId) {

        AWSCredentials creds = new AWSCredentials();

        try {
            GetCredentialsForIdentityRequest getCredentialsForIdentityRequest = GetCredentialsForIdentityRequest
                    .builder()
                    .identityId(identityId)
                    .build();

            GetCredentialsForIdentityResponse response = cognitoClient
                    .getCredentialsForIdentity(getCredentialsForIdentityRequest);
            System.out.println(
                    "Identity ID " + response.identityId() + ", Access key ID " + response.credentials().accessKeyId());

            creds.accessKeyID = response.credentials().accessKeyId();
            creds.secretAccessKey = response.credentials().secretKey();
            creds.sessitonToken = response.credentials().sessionToken();

        } catch (CognitoIdentityProviderException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }

        return creds;
    }

    // Textract に Document を送付して文字起こしをする
    public static void detectDocText(TextractClient textractClient, String sourceDoc) {

        try {

            InputStream sourceStream = new FileInputStream(new File(sourceDoc));
            SdkBytes sourceBytes = SdkBytes.fromInputStream(sourceStream);

            // Document を byte として取得する
            Document myDoc = Document.builder()
                    .bytes(sourceBytes)
                    .build();

            DetectDocumentTextRequest detectDocumentTextRequest = DetectDocumentTextRequest.builder()
                    .document(myDoc)
                    .build();

            // Textract の Detect Operation を実行する
            DetectDocumentTextResponse textResponse = textractClient.detectDocumentText(detectDocumentTextRequest);

            List<Block> docInfo = textResponse.blocks();

            Iterator<Block> blockIterator = docInfo.iterator();

            while (blockIterator.hasNext()) {
                Block block = blockIterator.next();
                System.out.println("The block type is " + block.blockType().toString());
            }

            DocumentMetadata documentMetadata = textResponse.documentMetadata();
            System.out.println("The number of pages in the document is " + documentMetadata.pages());

        } catch (TextractException | FileNotFoundException e) {

            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
}

class AWSCredentials {
    String accessKeyID;
    String secretAccessKey;
    String sessitonToken;
}
```



まず、Coginito Identity Pool に関連する部分を抜粋します。コメントに処理内容が書かれているのでわかりやすいとおもいます。

- 作成した Identity Pool と連携するために、Identity Pool の ID が必要 (既にこの Identity Pool は削除しています)
- 未認証のユーザーをつかった Cognito Client が必要なため、`AnonymousCredentialsProvider` を利用する
- 生成した Cognito Client を使って、未認証ユーザー用の Identity ID を取得
- 取得した Identity ID を使って、一時的な AWS のクレデンシャルを取得

```java
        // Congiton Identity Pool ID を指定
        String identityPoolId = "ap-northeast-1:fc81b5de-4fa4-4087-adb5-4598fd25982a";

        // Cognito の Client を生成する。未認証のユーザーなので AnonymousCredentialsProvider を使う
        AwsCredentialsProvider provider = AnonymousCredentialsProvider.create();
        CognitoIdentityClient cognitoClient = CognitoIdentityClient.builder()
                .region(Region.AP_NORTHEAST_1)
                .credentialsProvider(provider)
                .build();

        // Identity Pool から、Identity ID を取得する
        String identityId = getIdentityID(cognitoClient, identityPoolId);

        // 取得した Identity ID を使って、一時的なクレデンシャルを取得する
        AWSCredentials awsCreds = getCredsForIdentity(cognitoClient, identityId);
```



Cognito Identity Pool から取得した一時的なクレデンシャルをつかって、Textract にリクエストを投げる部分がこちらです。

- 一時的なクレデンシャルを使って、`AwsSessionCredentials` を生成
- 生成した `AwsSessionCredentials`  を使って、`StaticCredentialsProvider` を生成

```java
        // 取得してきた 一時的なクレデンシャルを使って、Textract の Client を生成する。AccessKeyID と SecretAccessKey
        // と SessionToken を使って、AwsSessionCredentials を生成する
        AwsSessionCredentials credentials = AwsSessionCredentials.create(awsCreds.accessKeyID, awsCreds.secretAccessKey,
                awsCreds.sessitonToken);
        TextractClient textractClient = TextractClient.builder()
                .region(Region.US_EAST_1)
                .credentialsProvider((StaticCredentialsProvider.create(credentials)))
                .build();

        // Textract の文字起こしを行う
        detectDocText(textractClient, "textract001.png");
```



# IAM Role の設定

[前回の記事](https://qiita.com/sugimountaws/items/49ce163a703b555fd88a) で作成した Cogitno Identity Pool で作成した Unauth 用の Role に Textract の権限付与を行います。未認証向けの Role になるので、権限は最小限のものを指定するのがのぞましいです。対象の Role で Attach policies を選択します。

![image-20211219145931381.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/d072e59a-3186-9492-bf9a-221735b8abde.png)



Textract の権限を付与します。

![image-20211219150015484.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/badae77b-c61b-2563-31be-b5fd329c4f40.png)



付与されている状態になりました。

![image-20211219150031026.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/d9056451-2682-6352-4627-dfaf52283fdf.png)



# 実行例

ここまでくれば、Java のプログラムを実行できます。実行例が次の内容となっており、Cognito Identiy Pool から Access key などの一時クレデンシャル情報を取得できており、それをつかった Textract へ文字お越しもできます。

```
Identity ID ap-northeast-1:ddd90f8c-4d78-4ced-831a-b95cbb957318
Identity ID ap-northeast-1:ddd90f8c-4d78-4ced-831a-b95cbb957318, Access key ID xxxxxxxxxxxxxxxxxxx
The block type is PAGE
The block type is LINE
The block type is LINE
The block type is LINE
The block type is LINE
The block type is LINE
```



# まとめ

AWS SDK for Java 2.x と Cognito Identity Pool を使って、未認証のユーザーでも有効なクレデンシャル情報を取得できることがわかりました。AWS 外で Java のアプリケーションを動かす必要があり、なおかつ、未認証のユーザーから直接 AWS にアクセスしたいときに便利に利用できます。



# 参考URL

Sample Code for AWS SDK for Java 2.x
https://docs.aws.amazon.com/code-samples/latest/catalog/javav2-cognito-src-main-java-com-example-cognito-GetId.java.html
https://docs.aws.amazon.com/code-samples/latest/catalog/javav2-textract-src-main-java-com-example-textract-DetectDocumentText.java.html