PKCE Clients

Reference information on how to set up a PKCE client with Authproject

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:

  1. Create a “public” (not “confidential”) OAuth client in Authproject
  2. Create a PKCE pair in your application
  3. Give the user a URL to open that requests an authorization code, with code_challenge and code_challenge_method in the request
  4. Parse the response
  5. Exchange the authorization code for a token, including code_verifier in the request
  6. Use the access token to perform queries against Authproject