Source code for simple_openid_connect.pkce
"""
Implementation of `PKCE <https://datatracker.ietf.org/doc/html/rfc7636>`_ code challenge and verifier generation.
Original code from `@RomeoDespres <https://github.com/RomeoDespres/>`_ in their `pkce repository <https://github.com/RomeoDespres/pkce/blob/b74b1864dc8a2018ca86566a5cfa2fd9fe751c4d/pkce/__init__.py>`_:
Examples
--------
>>> from simple_openid_connect import pkce
>>> code_verifier, code_challenge = pkce.generate_pkce_pair()
>>> from simple_openid_connect import pkce
>>> code_verifier = pkce.generate_code_verifier(length=128)
>>> code_challenge = pkce.get_code_challenge(code_verifier)
"""
import base64
import hashlib
import secrets
from typing import Tuple
[docs]
def generate_code_verifier(length: int = 128) -> str:
"""
Return a random PKCE-compliant code verifier.
:param length: Code verifier length. Must be betwen 43 and 128.
:raises ValueError: When `length` is not between 43 and 128.
:returns: A url-safe string ready to be used as a code verifier.
"""
if not 43 <= length <= 128:
msg = "Parameter `length` must verify `43 <= length <= 128`."
raise ValueError(msg)
code_verifier = secrets.token_urlsafe(96)[:length]
return code_verifier
[docs]
def generate_pkce_pair(code_verifier_length: int = 128) -> Tuple[str, str]:
"""
Return random PKCE-compliant code verifier and its corresponding code challenge.
:param code_verifier_length: Length of the generated code verifier. Must be between 43 and 128.
:raises ValueError: When `code_verifier_length` is not between 43 and 128.
:returns code_verifier, code_challenge: A tuple containing the code verifier along with its corresponding challenge.
"""
if not 43 <= code_verifier_length <= 128:
msg = "Parameter `code_verifier_length` must verify "
msg += "`43 <= code_verifier_length <= 128`."
raise ValueError(msg)
code_verifier = generate_code_verifier(code_verifier_length)
code_challenge = get_code_challenge(code_verifier)
return code_verifier, code_challenge
[docs]
def get_code_challenge(code_verifier: str) -> str:
"""
Generate the corresponding code challenge for a given verifier.
:param code_verifier: The code verifier from which a challenge should be derived.
:raises ValueError: When `code_verifier` is not bettween 43 and 128 long.
:returns: The url-safe challenge string that corresponds to the given verifier.
"""
if not 43 <= len(code_verifier) <= 128:
msg = "Parameter `code_verifier` must verify "
msg += "`43 <= len(code_verifier) <= 128`."
raise ValueError(msg)
hashed = hashlib.sha256(code_verifier.encode("ascii")).digest()
encoded = base64.urlsafe_b64encode(hashed)
code_challenge = encoded.decode("ascii")[:-1]
return code_challenge