feat(oauth): support public keys for PKCE flows#20151
feat(oauth): support public keys for PKCE flows#20151rawkode wants to merge 1 commit intogoauthentik:mainfrom
Conversation
✅ Deploy Preview for authentik-integrations ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for authentik-storybook ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for authentik-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
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_secretfield 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.
| 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."} | ||
| ) |
There was a problem hiding this comment.
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.
| request = self.factory.get("/") | ||
| request.session = {} | ||
| request.user = get_anonymous_user() | ||
| client = OAuth2Client(self.source, request) |
There was a problem hiding this comment.
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.
| 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) | ||
|
|
There was a problem hiding this comment.
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.
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.
Checklist
ak test authentik/)make lint-fix)If an API change has been made
make gen-build)If changes to the frontend have been made
make web)If applicable
make docs)