Amazon CognitoとLibertyをOpenID Connect Clientで連携してみる

# はじめに
- App IDとLibertyをOpenID Connect Clientで連携して、認証部分をそれっぽく実装したことはあったのですが、Cognitoでも同じことできるんだっけ？？と思ったので試してみたという話です。
- 基本はApp IDのドキュメントに記述されている[Liberty for Java SDK の構成](https://cloud.ibm.com/docs/appid?topic=appid-web-apps&locale=ja#web-configuring-liberty)と同じ手順で問題ありませんでしたが、当然ながらApp IDとCognitoでURLの指定など異なる部分もありますので、本記事ではLibertyとCognitoを連携するにあたって必要な部分のみを記述します。

# 注意
- この例では、ユーザープールをus-east-2に作っています。us-east-2と記述されている部分については、ご自身の環境に合わせて適宜読み替えてください。
- この例では、ローカル環境のLibertyからCognitoへの接続を例として記載しています。ドメインなどは、ご自身の環境に合わせて適宜読み替えてください。

# server.xmlの構成
① CognitoのユーザープールIDを取得します。
```sh
$ aws cognito-idp list-user-pools --max-results 10
{
    "UserPools": [
        {
            "Id": "us-east-2_xxxxxxxxx",
            "Name": "cognito-test",
            "LambdaConfig": {},
            "LastModifiedDate": "2022-06-02T17:16:58.221000+09:00",
            "CreationDate": "2022-06-02T14:11:36.733000+09:00"
        }
    ]
}
```

② ユーザープールIDをキーとして、client_idを取得します。
```sh
$ aws cognito-idp list-user-pool-clients --user-pool-id us-east-2_xxxxxxxxx
{
    "UserPoolClients": [
        {
            "ClientId": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
            "UserPoolId": "us-east-2_xxxxxxxxx",
            "ClientName": "test-client"
        }
    ]
}
```

③ ユーザープールIDとclient_idをキーとして、client_secretを取得します。
```sh
$ aws cognito-idp describe-user-pool-client --user-pool-id us-east-2_xxxxxxxxx --client-id xxxxxxxxxxxxxxxxxxxxxxxxxx
{
    "UserPoolClient": {
        "UserPoolId": "us-east-2_xxxxxxxxx",
        "ClientName": "test-client",
        "ClientId": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
        "ClientSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "LastModifiedDate": "2022-06-02T17:02:13.501000+09:00",
        "CreationDate": "2022-06-02T14:11:37.301000+09:00",
        "RefreshTokenValidity": 30,
        ... (略) ...
        "AllowedOAuthFlowsUserPoolClient": true,
        "PreventUserExistenceErrors": "ENABLED"
    }
}
```

④ ユーザープールIDをキーとして、Cognitoドメインを取得します。
```sh
$ aws cognito-idp describe-user-pool --user-pool-id us-east-2_xxxxxxxxx         
{
    "UserPool": {
        "Id": "us-east-2_xxxxxxxxx",
        "Name": "cognito-test",
        ... (略) ... 
        "Domain": "xxxxxxxxxx",
        "AdminCreateUserConfig": {
            "AllowAdminCreateUserOnly": true,
            "UnusedAccountValidityDays": 7
        },
        "UsernameConfiguration": {
            "CaseSensitive": false
        },
        "Arn": "arn:aws:cognito-idp:us-east-2:xxxxxxxxxxxx:userpool/us-east-2_xxxxxxxxx",
        "AccountRecoverySetting": {
            "RecoveryMechanisms": [
                {
                    "Priority": 1,
                    "Name": "verified_email"
                }
            ]
        }
    }
}
```

⑤ server.xmlにOpenID Connect Client Featureを作成し、下記の通り定義します。
※ httpsRequired="false"の記述は、ローカル環境などhttpで接続する環境の場合にのみ必要です。

```xml
<openidConnectClient id="{任意のid}"
    clientId="{②で取得したClientId}"
    clientSecret="{③で取得したClientSecret}"
    authorizationEndpointUrl="https://{④で取得したDomain}.auth.us-east-2.amazoncognito.com/oauth2/authorize"
    tokenEndpointUrl="https://{④で取得したDomain}.auth.us-east-2.amazoncognito.com/oauth2/token"
    jwkEndpointUrl="https://cognito-idp.us-east-2.amazonaws.com/{①で取得したId}/.well-known/jwks.json"
    issuerIdentifier="https://cognito-idp.us-east-2.amazonaws.com/{①で取得したId}"
    tokenEndpointAuthMethod="basic"
    signatureAlgorithm="RS256"
    scope="openid"
    httpsRequired="false"
    redirectToRPHostAndPort="http://localhost:9080"
/> 
```

# コールバックURLの設定
Cognitoからの戻り先を設定します。

```sh
$ aws cognito-idp update-user-pool-client --user-pool-id us-east-2_xxxxxxxxx --client-id xxxxxxxxxxxxxxxxxxxxxxxxxx --callback-urls "http://localhost:9080/oidcclient/redirect/{⑤で指定したid}"
{
    "UserPoolClient": {
        "UserPoolId": "us-east-2_xxxxxxxxx",
        "ClientName": "test-client",
        "ClientId": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
        "ClientSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "LastModifiedDate": "2022-06-03T12:25:21.303000+09:00",
        "CreationDate": "2022-06-02T14:11:37.301000+09:00",
        "RefreshTokenValidity": 30,
        "CallbackURLs": [
            "http://localhost:9080/oidcclient/redirect/{⑤で指定したid}"
        ],
        "AllowedOAuthFlowsUserPoolClient": false,
        "PreventUserExistenceErrors": "ENABLED"
    }
}
```

ここが適切に設定されていない場合、次のようなエラー（redirect_mismatch）になります。

![スクリーンショット 2022-06-03 12.32.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134250/430f1f05-1a75-e84c-edb5-50d33b280d72.png)

# Keystoreの構成
Keystoreに適切な署名者証明書が追加されていない場合、ログに下記のようなエラーが出力される場合があります。
```
[ERROR   ] CWPKI0823E: SSL ハンドシェークの失敗: SubjectDN [CN=*.auth.us-east-2.amazoncognito.com] の署名者がホスト [xxxxxxxxxx.auth.us-east-2.amazoncognito.com:443] から送信されました。  SSL 構成別名 [defaultSSLConfig] にあるトラストストア [...(略).../resources/security/key.p12] にこの署名者を追加する必要がある可能性があります。  SSL ハンドシェーク例外からの拡張エラー・メッセージ: [unable to find valid certification path to requested target]
[ERROR   ] CWWKS1708E: OpenID Connect クライアント [xxxxxxxxxxxxxxxxxxxxxxxxxx] は、[https://xxxxxxxxxx.auth.us-east-2.amazoncognito.com/oauth2/token] に OpenID Connect プロバイダーに接続して ID トークンを受け取ることができません。原因は [unable to find valid certification path to requested target] です。
```

このエラーが出力された場合は、接続先のCognitoホスト（*.auth.us-east-2.amazoncognito.com と cognito-idp.us-east-2.amazonaws.com）の証明書を取得し、Libertyのセキュリティ設定を適切に構成する必要があります。
Libertyに証明書を追加する方法は下記に記載されていますので、本記事では割愛します。

https://www.ibm.com/docs/ja/was-liberty/base?topic=liberty-adding-trusted-certificates-in

# 確認
- サーバーを起動し、アプリへアクセスした際にログイン画面が表示されれば成功です。
![スクリーンショット 2022-06-03 11.40.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134250/8e44e7f6-f91d-616d-deb1-cedd1a0d5377.png)