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에서는 클라이언트 측의 개별 제어가 가능하도록 되어 있는지 확인해 보았습니다.

액세스 관리 6.5 릴리스 노트 을 확인하면 아래의 설정으로 클라이언트에 강제하도록 할지 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

    좋은 웹페이지 즐겨찾기