You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

77 lines
2.6 KiB

"""Tests for PKCE flow: code verifier and code challenge handling."""
import os
import re
import urllib.parse
import requests
from packaging.version import Version
from keycloak import KeycloakAdmin, KeycloakOpenID
from keycloak.pkce_utils import generate_code_challenge, generate_code_verifier
def test_pkce_auth_url_and_token(env: object, admin: KeycloakAdmin) -> None:
"""Test PKCE flow: auth_url includes code_challenge, token includes code_verifier."""
if os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"] != "latest" and Version(
os.environ["KEYCLOAK_DOCKER_IMAGE_TAG"],
) <= Version("22"):
return
client_representation = {
"clientId": "pkce-test",
"enabled": True,
"publicClient": True,
"standardFlowEnabled": True,
"directAccessGrantsEnabled": False,
"serviceAccountsEnabled": False,
"implicitFlowEnabled": False,
"redirectUris": ["http://test.test/callback"],
"webOrigins": ["*"],
}
admin.create_client(client_representation)
oid = KeycloakOpenID(
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
realm_name="master",
client_id="pkce-test",
)
code_verifier = generate_code_verifier()
code_challenge, code_challenge_method = generate_code_challenge(code_verifier)
# Build PKCE auth URL
url = oid.auth_url(
redirect_uri="http://test.test/callback",
code_challenge=code_challenge,
code_challenge_method=code_challenge_method,
scope="openid%20email",
)
assert f"code_challenge={code_challenge}" in url
assert f"code_challenge_method={code_challenge_method}" in url
session = requests.Session()
resp = session.get(url, allow_redirects=False)
cookies = resp.cookies.get_dict()
assert resp.status_code == 200
resp_url = re.findall(r"action=\"(.*)\" method", resp.text)[0]
resp = session.post(
resp_url,
data={"username": env.keycloak_admin, "password": env.keycloak_admin_password},
allow_redirects=False,
cookies=cookies,
)
assert resp.status_code == 302, resp.text
resp_code = urllib.parse.parse_qs(resp.headers["Location"])["code"][0]
access_token = oid.token(
grant_type="authorization_code",
code=resp_code,
redirect_uri="http://test.test/callback",
code_verifier=code_verifier,
)
info = oid.userinfo(access_token["access_token"])
assert info["preferred_username"] == env.keycloak_admin
# Cleanup
client_id = admin.get_client_id("pkce-test")
admin.delete_client(client_id)