Skip to content

feat(oauth): support public keys for PKCE flows#20151

Open
rawkode wants to merge 1 commit intogoauthentik:mainfrom
rawkode:feat/support-pkce-public-key
Open

feat(oauth): support public keys for PKCE flows#20151
rawkode wants to merge 1 commit intogoauthentik:mainfrom
rawkode:feat/support-pkce-public-key

Conversation

@rawkode
Copy link

@rawkode rawkode commented Feb 10, 2026

Details

I would like to use PKCE Public Key flows for my application, but at the moment client secrets are required. This PR attempts to make them optional and support the flow.

Unfortunately I couldn't get any of the make targets to work on my machine, I am unsure why. I may try and put together a Nix Flake for this environment to support me if the CI runs don't look good.

make lint-fix
      Built authentik @ file:///Users/dflanagan/Code/src/github.com/authentik
      Built ak-guardian @ file:///Users/dflanagan/Code/src/github.com/authentik/packages/ak-guardian
    Updated https://github.com/vsoch/oci-python (ceb4fcc090851717a3069d78e85ceb1e86c2740c)
      Built django-channels-postgres @ file:///Users/dflanagan/Code/src/github.com/authentik/packages/django-channels-postgres
      Built django-dramatiq-postgres @ file:///Users/dflanagan/Code/src/github.com/authentik/packages/django-dramatiq-postgres
      Built django-postgres-cache @ file:///Users/dflanagan/Code/src/github.com/authentik/packages/django-postgres-cache
      Built opencontainers @ git+https://github.com/vsoch/oci-python@ceb4fcc090851717a3069d78e85ceb1e86c2740c
      Built dumb-init==1.2.5.post1
      Built watchdog==6.0.0
      Built py-ubjson==0.16.1
  × Failed to build `psycopg-c==3.3.2`
  ├─▶ The build backend returned an error
  ╰─▶ Call to `cython_backend.build_wheel` failed (exit status: 1)

      [stdout]
      running bdist_wheel
      running build
      running build_py
      creating build/lib.macosx-11.0-arm64-cpython-314/psycopg_c
      copying psycopg_c/_uuid.py -> build/lib.macosx-11.0-arm64-cpython-314/psycopg_c
      copying psycopg_c/version.py -> build/lib.macosx-11.0-arm64-cpython-314/psycopg_c
      copying psycopg_c/__init__.py -> build/lib.macosx-11.0-arm64-cpython-314/psycopg_c
      running egg_info
      writing psycopg_c.egg-info/PKG-INFO
      writing dependency_links to psycopg_c.egg-info/dependency_links.txt
      writing top-level names to psycopg_c.egg-info/top_level.txt

      [stderr]
      /Users/dflanagan/.cache/uv/builds-v0/.tmpTIfIiJ/lib/python3.14/site-packages/setuptools/config/pyprojecttoml.py:72: _ExperimentalConfiguration:
      `[tool.setuptools.ext-modules]` in `pyproject.toml` is still *experimental* and likely to change in future releases.
        config = read_configuration(filepath, True, ignore_option_errors, dist)
      couldn't run 'pg_config' --includedir: [Errno 2] No such file or directory: 'pg_config'
      error: [Errno 2] No such file or directory: 'pg_config'

      hint: This usually indicates a problem with the package or the build environment.
  help: `psycopg-c` (v3.3.2) was included because `authentik` (v2026.2.0rc1) depends on `psycopg[c]` (v3.3.2) which depends on `psycopg-c`
LDFLAGS=" -L/opt/homebrew/opt/krb5/lib" CPPFLAGS=" -I/opt/homebrew/opt/krb5/include" PKG_CONFIG_PATH="/opt/homebrew/opt/krb5/lib/pkgconfig:" uv run codespell -w
  × Failed to build `psycopg-c==3.3.2`
  ├─▶ The build backend returned an error
  ╰─▶ Call to `cython_backend.build_wheel` failed (exit status: 1)

      [stdout]
      running bdist_wheel
      running build
      running build_py
      copying psycopg_c/_uuid.py -> build/lib.macosx-11.0-arm64-cpython-314/psycopg_c
      copying psycopg_c/version.py -> build/lib.macosx-11.0-arm64-cpython-314/psycopg_c
      copying psycopg_c/__init__.py -> build/lib.macosx-11.0-arm64-cpython-314/psycopg_c
      running egg_info
      writing psycopg_c.egg-info/PKG-INFO
      writing dependency_links to psycopg_c.egg-info/dependency_links.txt
      writing top-level names to psycopg_c.egg-info/top_level.txt

      [stderr]
      /Users/dflanagan/.cache/uv/builds-v0/.tmpgP0ibE/lib/python3.14/site-packages/setuptools/config/pyprojecttoml.py:72: _ExperimentalConfiguration:
      `[tool.setuptools.ext-modules]` in `pyproject.toml` is still *experimental* and likely to change in future releases.
        config = read_configuration(filepath, True, ignore_option_errors, dist)
      couldn't run 'pg_config' --includedir: [Errno 2] No such file or directory: 'pg_config'
      error: [Errno 2] No such file or directory: 'pg_config'

      hint: This usually indicates a problem with the package or the build environment.
  help: `psycopg-c` (v3.3.2) was included because `authentik` (v2026.2.0rc1) depends on `psycopg[c]` (v3.3.2) which depends on `psycopg-c`
make: *** [lint-codespell] Error 1

Checklist

  • Local tests pass (ak test authentik/)
  • The code has been formatted (make lint-fix)

If an API change has been made

  • The API schema has been updated (make gen-build)

If changes to the frontend have been made

  • The code has been formatted (make web)

If applicable

  • The documentation has been updated
  • The documentation has been formatted (make docs)

Copilot AI review requested due to automatic review settings February 10, 2026 18:03
@rawkode rawkode requested a review from a team as a code owner February 10, 2026 18:03
@netlify
Copy link

netlify bot commented Feb 10, 2026

Deploy Preview for authentik-integrations ready!

Name Link
🔨 Latest commit 24b8ddb
🔍 Latest deploy log https://app.netlify.com/projects/authentik-integrations/deploys/698b730c9c773900084acce1
😎 Deploy Preview https://deploy-preview-20151--authentik-integrations.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Feb 10, 2026

Deploy Preview for authentik-storybook ready!

Name Link
🔨 Latest commit 24b8ddb
🔍 Latest deploy log https://app.netlify.com/projects/authentik-storybook/deploys/698b730cce4930000886aef3
😎 Deploy Preview https://deploy-preview-20151--authentik-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Feb 10, 2026

Deploy Preview for authentik-docs ready!

Name Link
🔨 Latest commit 24b8ddb
🔍 Latest deploy log https://app.netlify.com/projects/authentik-docs/deploys/698b730c2864d600082436b7
😎 Deploy Preview https://deploy-preview-20151--authentik-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds support for public OAuth clients (clients without a consumer secret) to enable PKCE (Proof Key for Code Exchange) flows. The PR makes the consumer_secret field optional in the OAuthSource model while maintaining backward compatibility and adding validation to ensure Apple and OAuth1 sources still require secrets.

Changes:

  • Made consumer_secret field optional by allowing blank values in the model and migration
  • Updated OAuth2 and OpenIDConnect clients to handle empty secrets correctly for public client flows
  • Added API validation to ensure Apple and OAuth1 sources still require consumer secrets
  • Added test coverage for public client scenarios with both POST_BODY and OpenIDConnect authentication methods

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
authentik/sources/oauth/models.py Made consumer_secret field optional with blank=True and default=""
authentik/sources/oauth/migrations/0014_oauthsource_consumer_secret_optional.py Django migration to alter consumer_secret field to allow blank values
authentik/sources/oauth/clients/oauth2.py Updated OAuth2Client to conditionally include client_secret and handle public clients
authentik/sources/oauth/types/oidc.py Updated OpenIDConnectClient to support public clients with empty secrets
authentik/sources/oauth/api/source.py Added validation to require secrets for Apple and OAuth1 sources
authentik/sources/oauth/tests/test_client.py Added tests for public client scenarios with POST_BODY and OIDC

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +123 to +130
if source_type.name == "apple" and not consumer_secret:
raise ValidationError(
{"consumer_secret": "Consumer secret is required for Apple sources."}
)
if request_token_url and not consumer_secret:
raise ValidationError(
{"consumer_secret": "Consumer secret is required for OAuth1 sources."}
)
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test coverage for the new validation logic that requires consumer_secret for Apple sources. Add a test case in test_views.py that attempts to create or update an Apple source without a consumer_secret and verifies that validation fails with an appropriate error message. Also add a test for OAuth1 sources (those with request_token_url) requiring a secret.

Copilot uses AI. Check for mistakes.
request = self.factory.get("/")
request.session = {}
request.user = get_anonymous_user()
client = OAuth2Client(self.source, request)
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test should verify that get_access_token_auth() returns None for public clients. Add an assertion: self.assertIsNone(client.get_access_token_auth()) before line 60 to ensure that basic authentication is not used when there's no client secret.

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +63
def test_client_post_body_public(self):
"""Test public client post body without secret"""
self.source.provider_type = "github"
self.source.consumer_secret = ""
self.source.save()
request = self.factory.get("/")
request.session = {}
request.user = get_anonymous_user()
client = OAuth2Client(self.source, request)
args = client.get_access_token_args("", "")
self.assertIn("client_id", args)
self.assertNotIn("client_secret", args)

Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test coverage is missing for public client behavior with BASIC_AUTH mode (the default for most providers). Add a test case that verifies a public client (empty consumer_secret) with a provider that uses BASIC_AUTH (like Reddit) properly includes client_id in the token request args and doesn't attempt HTTP Basic Authentication. This scenario is handled by lines 89-90 in oauth2.py but is not tested.

Copilot uses AI. Check for mistakes.
@dewi-tik dewi-tik added the enhancement/confirmed Enhancements that will be implemented in the future label Feb 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement/confirmed Enhancements that will be implemented in the future

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

4 participants