Java用SDKからCognito User Poolsを使用する

ブラウザからのユーザーアクセスであれば[amazon-cognito-identity-js](https://github.com/aws/amazon-cognito-identity-js)を使ってユーザーの追加、ログインは簡単にできますが、どうにかサーバーサイドでできないものか、調査しました。
Javaのプログラムからユーザー追加やログイン（トークンの取得）ができましたので紹介します。

# 前準備（マネジメントコンソール）

ユーザープールを作成します。ユーザーの登録以降のフローは異なりますが、ユーザープールを作成する手順までは[こちらのサイト](http://www.h4a.jp/detail/25148)が参考になると思います。

# 前準備（Java）

AWS SDKを使う設定をします。Gradleの場合は、build.gradleに以下の内容を追加します。

```groovy:build.gradle
    // https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-cognitoidp
    compile group: 'com.amazonaws', name: 'aws-java-sdk-cognitoidp', version: '1.11.123'
```

# AWSCognitoIdentityProviderの作成

AWSCredentialsProviderとリージョン（今回は東京リージョン）を引数に渡して、AWSCognitoIdentityProviderを作成します。

AWSCredentialsProviderの作成方法は[色々](http://dev.classmethod.jp/cloud/aws/iam-bestpractice-2/)あるようですが、今回は[こちら](http://dev.classmethod.jp/cloud/aws/aws-sdk-for-java_get_your_credential/)を参考にProfileCredentialsProviderを使用しました。

```java
AWSCredentialsProvider credentialsProvider = new ProfileCredentialsProvider("プロファイル名");

AWSCognitoIdentityProvider client = AWSCognitoIdentityProviderClientBuilder.standard()
                                        .withCredentials(credentialsProvider)
                                        .withRegion(Regions.AP_NORTHEAST_1)
                                        .build();
```

# ユーザーの新規作成

ユーザーを新規作成します。
ユーザープールIDはマネジメントコンソールで確認できます。

```java
AdminCreateUserRequest adminCreateUserRequest = new AdminCreateUserRequest();
adminCreateUserRequest
    .withUserPoolId("ユーザープールID")
    .withUsername("新規作成するユーザーのユーザー名")
    .withTemporaryPassword("新規作成するユーザーの一時パスワード");
AdminCreateUserResult response = client.adminCreateUser(adminCreateUserRequest);
```

# トークンの取得

ユーザーIDとパスワードを使ってトークンを取得します。クライアントIDはマネジメントコンソール上の「Apps」のところにあります。
※Appsの設定の`Enable sign-in API for server-based authentication (ADMIN_NO_SRP_AUTH)`は有効になっている必要があります。

```java
Map<String, String> authParameters = new HashMap<>();
authParameters.put("USERNAME", "ユーザー名");
authParameters.put("PASSWORD", "パスワード");

AdminInitiateAuthRequest request = new AdminInitiateAuthRequest();
request
    .withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH)
    .withUserPoolId("ユーザープールID")
    .withClientId("クライアントID")
    .withAuthParameters(authParameters);

AdminInitiateAuthResult response = client.adminInitiateAuth(request);
```

成功するとトークンが取得できます。（加工しています）

```java
System.out.println(response.getAuthenticationResult().toString());
```

```bash:トークン取得成功時
{
  AccessToken: xxxxx,
  ExpiresIn: 3600,
  TokenType: Bearer,
  RefreshToken: yyyyy,
  IdToken: zzzzz,
}
```


ただし、新規作成したばかりのユーザーは、パスワード変更の必要があるため、トークンは取得できません。その場合、ChallengeNameが「NEW_PASSWORD_REQUIRED」となってしまいますので、次の一時パスワード変更を行います。
(トークン取得成功時はChallengeNameがnull、トークン取得失敗時はAuthenticationResultがnullになるようです）

```java
System.out.println(response.getChallengeName());
```

```bash:トークン取得失敗時
NEW_PASSWORD_REQUIRED
```

# 一時パスワード変更

一時パスワード変更処理です。adminInitiateAuthのレスポンスで受け取るChallengeNameとSessionが必要になります。

```java
Map<String, String> challengeResponses = new HashMap<>();
challengeResponses.put("USERNAME", "ユーザー名");
challengeResponses.put("NEW_PASSWORD", "変更後の新しいパスワード");

AdminRespondToAuthChallengeRequest request = new AdminRespondToAuthChallengeRequest();
request  
    .withChallengeName(adminInitiateAuthResult.getChallengeName())
    .withUserPoolId("ユーザープールID")
    .withClientId("クライアントID")
    .withSession(adminInitiateAuthResult.getSession())
    .withChallengeResponses(challengeResponses);

AdminRespondToAuthChallengeResult response = client.adminRespondToAuthChallenge(request);
```

上手く行けば、この手順を持ってトークンが取得できます。

```java
System.out.println(response.getAuthenticationResult().toString());
```

```bash
{
  AccessToken: xxxxx,
  ExpiresIn: 3600,
  TokenType: Bearer,
  RefreshToken: yyyyy,
  IdToken: zzzzz,
}
```

# トークンの更新
リフレッシュトークンを使用してのトークンの更新は、トークンの取得と同様`adminInitiateAuth`メソッドにて行います。
`authParameters`の値と`AuthFlow`が違うだけです

```java
Map<String, String> authParameters = new HashMap<>();
authParameters.put("REFRESH_TOKEN", "リフレッシュトークン");

AdminInitiateAuthRequest request = new AdminInitiateAuthRequest();
request
    .withAuthFlow(AuthFlowType.REFRESH_TOKEN_AUTH)
    .withUserPoolId("ユーザープールID")
    .withClientId("クライアントID")
    .withAuthParameters(authParameters);

AdminInitiateAuthResult response = client.adminInitiateAuth(request);
```

# 取得したトークンの利用方法など

勉強中です。

* API Gateway の認証に利用
　http://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html
* ユーザープールのトークンの使用
 http://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html