OpenAM 13.0 のPKCEについて調べてみた


きっかけ

OAuth 2.0 Security Best Current Practiceのdraftにも
AS MUST support PKCE という内容が追記されていました。

OpenAM を OAuth2 Provider として使う場合にOpenAM 13.0 のPKCE対応ってどうなってるの?と思い、
調べてみることにしました。

1.ForgeRockのナレッジベースを確認

ForgeRockのknowledgebase によると対応しているのは AccessManagement 6.5 からと記載されています。

これは OpenAM 13.0 より2つ以上メジャーバージョンアップしたもので、もうOpenAMのようにソースは公開されていません。

※ ForgeRockのknowledgebaseは情報が充実しているので、OpenAMやOpenDJを利用する際にはとても便利です。

2.OpenAM 13.0 のドキュメントを確認

OpenAM 13.0 でも何かしらの対応がなされていないかを確認したところOAuth2 Providerの設定に
Code Verifier Parameter Required
なる設定があることを確認しました。

この設定値を true にして試してみます。

3. OpenAM 13.0 で試してみる

3-1. 準備

まずOpenAMの各種設定を ssoadm で行います

文字通り forgerock-oauth2-provider-code-verifier-enforced=true のパラメータ値が、

/oauth2/realmname/authorize 認可エンドポイントへのリクエストを行う際に、
code_verifier パラメータを強制させるフラグになります


# pkce_test レルムの作成
./ssoadm create-realm --realm pkce_test --adminid amAdmin --password-file /opt/ampwd 

# pkce_test レルムにOAuth2Providerの作成サービス&設定
./ssoadm add-svc-realm --realm pkce_test --adminid amAdmin --password-file /opt/ampwd --servicename OAuth2Provider --attributevalues forgerock-oauth2-provider-code-verifier-enforced=true --attributevalues forgerock-oauth2-provider-supported-scopes=profile

./ssoadm set-realm-svc-attrs --realm pkce_test --adminid amAdmin --password-file /opt/ampwd --servicename OAuth2Provider --attributevalues forgerock-oauth2-provider-code-verifier-enforced=true

./ssoadm set-realm-svc-attrs --realm pkce_test --adminid amAdmin --password-file /opt/ampwd --servicename OAuth2Provider  --attributevalues forgerock-oauth2-provider-supported-scopes=profile

# pkce_test レルムにOAuth2 クライアントの設定
./ssoadm create-agent --realm pkce_test --adminid amAdmin --password-file /opt/ampwd --agentname test_client --agenttype OAuth2Client --attributevalues userpassword=test_client

./ssoadm update-agent --realm pkce_test --adminid amAdmin --password-file /opt/ampwd --agentname test_client --set --attributevalues com.forgerock.openam.oauth2provider.redirectionURIs[0]="http://localhost/callback"

./ssoadm update-agent --realm pkce_test --adminid amAdmin --password-file /opt/ampwd --agentname test_client --set --attributevalues com.forgerock.openam.oauth2provider.scopes[0]=profile

./ssoadm update-agent --realm pkce_test --adminid amAdmin --password-file /opt/ampwd --agentname test_client --set --attributevalues com.forgerock.openam.oauth2provider.clientName[0]=test_client

3-2. 実行

これで準備はできたので、あとは認可コードフローの順番で実行してみます

まずは code_challenge パラメータを指定しなかった場合を試します
error_description=Missing parameter, 'code_challenge' がリダイレクトURIのパラメータに付与されたレスポンスが返ってきました

# curl -i -X GET  'http://openam.auth.local:8080/auth/oauth2/pkce_test/authorize?response_type=code&state=abcdefghijklmn&client_id=test_client&redirect_uri=http://localhost/callback&scope=profile'

HTTP/1.1 302 Found
Cache-Control: no-store
Date: Sat, 03 Aug 2019 08:39:55 GMT
Accept-Ranges: bytes
Location: http://localhost/callback?error_description=Missing%20parameter%2C%20%27code_challenge%27&state=abcdefghijklmn&error=invalid_request
Server: Restlet-Framework/2.3.4
Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
Pragma: no-cache
Content-Length: 0

続いて code_challenge パラメータと code_challenge_method をつけてリクエストしてみます

code verifier と code_challenge については Online PKCE Generator Toolという便利なツールがあるので、今回はこちらを利用させてもらいます

先ほどのようなエラーのパラメータはありませんでした

# curl -i -X GET  'http://openam.auth.local:8080/auth/oauth2/pkce_test/authorize?response_type=code&state=abcdefghijklmn&client_id=test_client&redirect_uri=http://localhost/callback&scope=profile&code_challenge=1J6dh2tsu3xIomhdN2-KG1NLhR3YKPT8X3Fi5_FgCSQ&code_challenge_method=S256'

HTTP/1.1 301 Moved Permanently
Cache-Control: no-store
Date: Sat, 03 Aug 2019 09:01:14 GMT
Accept-Ranges: bytes
Location: http://openam.auth.local:8080/auth/UI/Login?realm=%2Fpkce_test&goto=http%3A%2F%2Fopenam.auth.local%3A8080%2Fauth%2Foauth2%2Fpkce_test%2Fauthorize%3Fresponse_type%3Dcode%26state%3Dabcdefghijklmn%26client_id%3Dtest_client%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%252Fcallback%26scope%3Dprofile%26code_challenge%3D1J6dh2tsu3xIomhdN2-KG1NLhR3YKPT8X3Fi5_FgCSQ
Server: Restlet-Framework/2.3.4
Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
Pragma: no-cache
Content-Length: 0

それではアクセストークンの取得まで一通りやってみます

# curl -i -X GET  'http://openam.auth.local:8080/auth/oauth2/pkce_test/authorize?response_type=code&state=abcdefghijklmn&client_id=test_client&redirect_uri=http://localhost/callback&scope=profile&code_challenge=1J6dh2tsu3xIomhdN2-KG1NLhR3YKPT8X3Fi5_FgCSQ&code_challenge_method=S256'

HTTP/1.1 301 Moved Permanently
Cache-Control: no-store
Date: Sat, 03 Aug 2019 09:29:59 GMT
Accept-Ranges: bytes
Location: http://openam.auth.local:8080/auth/UI/Login?realm=%2Fpkce_test&goto=http%3A%2F%2Fopenam.auth.local%3A8080%2Fauth%2Foauth2%2Fpkce_test%2Fauthorize%3Fresponse_type%3Dcode%26state%3Dabcdefghijklmn%26client_id%3Dtest_client%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%252Fcallback%26scope%3Dprofile%26code_challenge%3D1J6dh2tsu3xIomhdN2-KG1NLhR3YKPT8X3Fi5_FgCSQ%26code_challenge_method%3DS256
Server: Restlet-Framework/2.3.4
Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
Pragma: no-cache
Content-Length: 0

同意画面が表示されました。

同意すると認可コードを取得できました。

http://localhost/callback?code=7a792613-5888-4586-b911-cc8884ce3756&scope=profile&state=abcdefghijklmn

アクセストークンを取得します。その際に code_verifierのパラメータを指定します。

curl -i -X POST \
>    -H "Content-Type:application/x-www-form-urlencoded" \
>    -H "Authorization:Basic dGVzdF9jbGllbnQ6dGVzdF9jbGllbnQ=" \
>    -d \
> 'grant_type=authorization_code&code=7a792613-5888-4586-b911-cc8884ce3756&redirect_uri=http://localhost/callback' \
>  'http://openam.auth.local:8080/auth/oauth2/pkce_test/access_token?code_verifier=ND1eMqjT4Ss7lN4j7DP1M4WEJo37qNcdrjbVvmSMZo0uA7b3uR94t6LmEptTCEyj7H8n7p095hqYOz25av1RdHvWXcu2zoLAaVhrY6FYnBFYCpre1sQUP4XPq9g9lFGf'

HTTP/1.1 200 OK
Cache-Control: no-store
Date: Sat, 03 Aug 2019 09:37:27 GMT
Accept-Ranges: bytes
Server: Restlet-Framework/2.3.4
Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
Pragma: no-cache
Content-Type: application/json
Transfer-Encoding: chunked

{"access_token":"6fe31629-c329-42d8-a394-b531f683a8dc","refresh_token":"7fa1fa43-2b62-4a32-bdc4-b7a6e2f642a5","scope":"profile","token_type":"Bearer","expires_in":3599}

access_tokenの取得までできました。
OpenAM 13.0 でも上記のような対応はできるようです。
OpenAM 13.0 はRFCに準拠したところまでは実装されていないのかもしれません。
ただ、OAuth2 Provider側の設定があるだけで、OAuth2クライアント側で強制するなどの個別制御の設定は見当たらなかったので、クライアント単位の制御はできないようです。

4.Access Management 6.5 での対応状況について

Access Management 6.5 ではクライアント側の個別制御ができるようになっているのか確認してみました

Access Management 6.5 Release Notesを確認すると以下の設定でクライアントに強制させるようにするかをOAuth2 Provider側の設定で実現できるようです。

  • 全クライアント必須
  • パブリッククライアントは必須
  • パスワードなしのパブリッククライアントは必須
  • 全クライアント不要

5.keycloakの対応状況について

これまでOpenAMの対応状況について書いてきましたが、keycloakの対応状況についても確認しておきます。

keycloak は Access Managementとは違い、OAuth2 クライアント側の設定で個別設定が可能です。
GitHubおよびJiraには次期バージョンの7.0.0から利用できるようになることが記載されていました。

KEYCLOAK-10747 Explicit Proof Key for Code Exchange Activation Settings
Explicit Proof Key for Code Exchange Activation Settings