PKCE Clients
2 minute read
How do I implement a PKCE client with Authproject?
PKCE
PKCE, or Proof Key for Code Exchange, is an extension to OAuth2’s authorization code flow to enable “public” clients to exchange their credentials for tokens, while maintaining the security of the system. Postman has an excellent set of docs explaining what PKCE is, and how it works.
Setting Up
In order for PKCE to work, there are a couple small requirements. The first is that your client must be set to be a “public” client (NOTE: this is not the default for new OAuth clients created in Authproject). The second is that your client must not have a client secret. This is handled automatically by us - if you request a public client, a client secret will not be generated.
Python Example
In order to demonstrate how to use PKCE with Authproject, we have a sample Python program that will perform a request on behalf of the user, exchange the received authorization code for a token, and display that token.
import base64
import webbrowser
import secrets
import hashlib
from requests_oauthlib import OAuth2Session
# Client ID, provided by Authproject
client_id = "<your-client-id>"
# Base URL for authorization
authorization_base_url = "https://<your-auth-domain>/oauth2/authorize"
# Token URL for exchanging authorization code for access token
token_url = "https://<your-auth-domain>/oauth2/token"
# Redirect URI registered with the OAuth2 provider
redirect_uri = "https://localhost:9000/callback"
# OAuth2 scope (optional, depends on your server)
scope = ["openid", "profile", "email"]
def generate_pkce_pair():
# Generate a high-entropy code verifier
code_verifier = (
base64.urlsafe_b64encode(secrets.token_bytes(64)).rstrip(b"=").decode("utf-8")
)
# Create code challenge
code_challenge = (
base64.urlsafe_b64encode(hashlib.sha256(code_verifier.encode("utf-8")).digest())
.rstrip(b"=")
.decode("utf-8")
)
return code_verifier, code_challenge
def main():
code_verifier, code_challenge = generate_pkce_pair()
# Create an OAuth2 session
oauth = OAuth2Session(
client_id,
redirect_uri=redirect_uri,
scope=scope,
)
# Get authorization URL and state
authorization_url, state = oauth.authorization_url(
authorization_base_url,
code_challenge=code_challenge,
code_challenge_method="S256",
)
print("Open this URL in a browser and authorize:", authorization_url)
webbrowser.open(authorization_url)
# Get the authorization response from the user
redirect_response = input("Paste the full redirect URL here: ").strip()
# Fetch the access token
token = oauth.fetch_token(
token_url,
authorization_response=redirect_response,
include_client_id=True,
code_verifier=code_verifier,
)
print("\nAccess token:", token)
if __name__ == "__main__":
main()
Summary
When setting up PKCE with your client, there are some simple steps to follow:
- Create a “public” (not “confidential”) OAuth client in Authproject
- Create a PKCE pair in your application
- Give the user a URL to open that requests an authorization code, with
code_challengeandcode_challenge_methodin the request - Parse the response
- Exchange the authorization code for a token, including
code_verifierin the request - Use the access token to perform queries against Authproject