From 4c5bfc33229b0fac8c7126c50d92fc50f56a1afd Mon Sep 17 00:00:00 2001 From: gkemmey Date: Tue, 7 Oct 2025 11:49:42 -0400 Subject: [PATCH] add pkce support --- README.md | 2 +- app.py | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 271890b..652607d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PCO API OAuth/OIDC Example - Flask + Python +# PCO API OAuth / OIDC / PKCE Example - Flask + Python This is an example Flask app for demonstrating how one might build an app to authenticate any PCO user and then subsequently use that authentication to query the API. diff --git a/app.py b/app.py index 9fd2b75..5a9482b 100644 --- a/app.py +++ b/app.py @@ -1,9 +1,11 @@ from requests_oauthlib import OAuth2Session from flask import Flask, request, redirect, render_template, session, url_for +import base64 +import hashlib import json +import jwt import os import pprint -import jwt app = Flask(__name__) app.secret_key = os.urandom(24) @@ -51,6 +53,13 @@ def parse_id_token(id_token): print(f"Failed to decode ID token: {e}") return None +def gen_code_verifier(): + return base64.urlsafe_b64encode(os.urandom(64)).decode().rstrip("=") + +def gen_code_challenge(verifier): + digest = hashlib.sha256(verifier.encode()).digest() + return base64.urlsafe_b64encode(digest).decode().rstrip("=") + @app.route("/") def index(): if "oauth_token" in session: @@ -60,14 +69,21 @@ def index(): @app.route("/auth") def auth(): - authorization_url, state = pco.authorization_url(f"{api_url}/oauth/authorize", prompt="select_account") + code_verifier = gen_code_verifier() + code_challenge = gen_code_challenge(code_verifier) + + authorization_url, state = pco.authorization_url(f"{api_url}/oauth/authorize", prompt="select_account", code_challenge=code_challenge, code_challenge_method='S256') + + session['code_verifier'] = code_verifier session['oauth_state'] = state + return redirect(authorization_url) @app.route("/auth/complete", methods=["GET"]) def callback(): token = pco.fetch_token(token_url, client_secret=client_secret, - authorization_response=request.url) + authorization_response=request.url, + code_verifier=session.pop('code_verifier', '')) session["oauth_token"] = token if 'id_token' in token: