CognitoにSDKやAWSCLI無しでアクセスする(curlなど)

# はじめに
AWSのAPIGatewyの認証としてLambdaAuthorizerは使用したことがありますが、Congnito認証は使用したことが無かったため試してみました。

またクライアントがCognitoにアクセスする際、ググるとほとんどがSDKやAWSCLIを使用していました。AWSのサポートに「curlなどで普通にアクセスできないのか?」と質問をしたところ、以下の情報を教えて貰えました。


> Cognito UserPool へのサインイン時には、以下の InitiateAuth API が呼ばれているため、Cognito UserPool のエンドポイントに対して InitiateAuth を curl コマンドで呼び出すことで、実現すること自体は可能です。

>[InitiateAuth - Amazon Cognito Identity Provider](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html)

> [Amazon Cognito ID エンドポイントとクォータ - AWS 全般のリファレンス](https://docs.aws.amazon.com/ja_jp/general/latest/gr/cognito_identity.html)

これらの情報をもとに、Cognito認証に最低限必要と思われるcurlでも叩けるようなAPIリクエストのサンプルをまとめておきます。

# 共通で必要な情報
**Endpoint(東京リージョンの場合)**
https://cognito-idp.ap-northeast-1.amazonaws.com/

**Method**
POST

**Header**
Content-Type: `application/x-amz-json-1.1`
X-Amz-User-Agent: `test-requets` ※これは無くても大丈夫

## サインイン
### Request
**Header**
X-Amz-Target: `AWSCognitoIdentityProviderService.InitiateAuth`

**Body**

```json
{
	"AuthFlow": "USER_PASSWORD_AUTH",
    	"ClientId": "${クライアントID}",
        "AuthParameters": {
            "USERNAME": "${ユーザ名}",
            "PASSWORD": "${パスワード}"
        }
}
```


### Response
**Body(初回サインイン時)**
初回サインイン時は仮パスワード状態であり、まだトークンは貰えません。
そのかわりに、パスワード変更時に必要なセッション情報が渡ってきます。

```json
{
    "ChallengeName": "NEW_PASSWORD_REQUIRED",
    "ChallengeParameters": {
        "USER_ID_FOR_SRP": "${ユーザ名}",
        "requiredAttributes": "[]",
        "userAttributes": "{\"email_verified\":\"true\",\"email\":\"${メールアドレス}\"}"
    },
    "Session": "${セッション情報}"
}
```

**Body通常サインイン時**

```json
```

## 初回パスワード変更
### Request
**Header**
X-Amz-Target: `AWSCognitoIdentityProviderService.RespondToAuthChallenge`

**Body**

```json
{
	"ClientId": "${クライアントID}",
	"ChallengeName": "NEW_PASSWORD_REQUIRED",
    "ChallengeResponses": {
        "NEW_PASSWORD": "${新しいパスワード}",
        "USERNAME": "${ユーザ名}"
    },
    "Session" : "${初回サインインのレスポンスに詰まっているセッション情報}"}

```


### Response
**Body**

```json
{
    "AuthenticationResult": {
        "AccessToken": "${アクセストークン}",
        "ExpiresIn": 3600,
        "IdToken": "${IDトークン}",
        "RefreshToken": "${リフレッシュトークン}",
        "TokenType": "Bearer"
    },
    "ChallengeParameters": {}
}
```

## トークンの更新
### Request
**Header**
X-Amz-Target: `AWSCognitoIdentityProviderService.InitiateAuth`

**Body**

```json
{
	"AuthFlow": "REFRESH_TOKEN_AUTH",
    	"ClientId": "${クライアントID}",
        "AuthParameters": {
            "USERNAME": "${ユーザ名}",
            "REFRESH_TOKEN": "${リフレッシュトークン}"        
        }
}
```

### Response
**Body**

```json
{
    "AuthenticationResult": {
        "AccessToken": "${新しいアクセストークン}",
        "ExpiresIn": 3600,
        "IdToken": "${新しいIDトークン}",
        "TokenType": "Bearer"
    },
    "ChallengeParameters": {}
}
```

## パスワード変更(初回以外)
### Request
**Header**
X-Amz-Target: `AWSCognitoIdentityProviderService.ChangePassword`

**Body**

```json
{
   "AccessToken": "${アクセストークン}",
   "PreviousPassword": "${今までのパスワード}",
   "ProposedPassword": "${新しく設定したいパスワード}"
}
```

### Response
**Body**
HTTPステータスコード: `200`

```json
{}
```
※書き漏れではなく空の`{}`が返ってきました。

# その他メモ(そのうち別記事にする予定)
### トークンの有効期限が切れた際のレスポンス

ステータスコード:  401
特徴的なヘッダ(x-amzn-ErrorType): UnauthorizedException
body: 

```json
{
    "message": "The incoming token has expired"
}
```