-
Notifications
You must be signed in to change notification settings - Fork 2
feat: Add DPoP authentication support #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
af89cf8
feat: Add DPoP authentication support
kishore7snehil a7a1b81
docs: add early access note for DPoP authentication feature
kishore7snehil 8f37411
ci: add GitHub Actions workflow for testing auth0-api-python package
kishore7snehil 7d154e0
fix: update import paths to use package namespace instead of src direβ¦
kishore7snehil 51e8987
chore: add ruff linting and apply code style fixes
kishore7snehil cfa18cf
docs: add examples for bearer and DPoP token authentication
kishore7snehil be536e1
docs: remove DPoP documentation link from README
kishore7snehil 2452937
feat: implement URL normalization using ada-url library and add test β¦
kishore7snehil 8bff8fc
chore: remove unused URL normalization test script
kishore7snehil d8ef382
test: add validation tests for edge case
kishore7snehil c9014aa
test: verify error message for htu mismatch in dpop proof validation
kishore7snehil 940c735
refactor: improve URL normalization and DPoP verification
kishore7snehil d5a1606
refactor: simplified JWK handling and iat error messages
kishore7snehil e6afc56
refactor: reorganize test cases
kishore7snehil 41fb87a
test: update error message assertions for DPoP validation failures
kishore7snehil b6c901e
feat: add include_jti flag to control jti claim inclusion in DPoP proβ¦
kishore7snehil 503a6a7
fix: improve error message formatting for DPoP scheme validation
kishore7snehil f6bd02c
Merge branch 'main' into feature/auth0-api-python/dpop-support
kishore7snehil 87916fd
Update packages/auth0_api_python/EXAMPLES.md
kishore7snehil 8cbbe9d
fix: preserve trailing slashes in DPoP proof URL normalization and adβ¦
kishore7snehil 5247857
fix: remove trailing whitespace in test_api_client.py URL parameter
kishore7snehil 94bae8b
fix: update error handling for authorization and DPoP validation to rβ¦
kishore7snehil 9249fa2
fix: improve error message for unsupported algorithm in DPoP proof vaβ¦
kishore7snehil 692e45a
fix: improve error handling for invalid authorization scheme and updaβ¦
kishore7snehil File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| name: Test auth0-api-python | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - feature/auth0-api-python | ||
| paths: | ||
| - 'packages/auth0_api_python/**' | ||
| pull_request: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - 'packages/auth0_api_python/**' | ||
|
|
||
| jobs: | ||
| test: | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| matrix: | ||
| python-version: [3.9, "3.10", "3.11", "3.12"] | ||
|
|
||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Python ${{ matrix.python-version }} | ||
| uses: actions/setup-python@v4 | ||
| with: | ||
| python-version: ${{ matrix.python-version }} | ||
|
|
||
| - name: Install Poetry | ||
| uses: snok/install-poetry@v1 | ||
| with: | ||
| version: latest | ||
| virtualenvs-create: true | ||
| virtualenvs-in-project: true | ||
| installer-parallel: true | ||
|
|
||
| - name: Load cached venv | ||
| id: cached-poetry-dependencies | ||
| uses: actions/cache@v3 | ||
| with: | ||
| path: packages/auth0_api_python/.venv | ||
| key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }} | ||
|
|
||
| - name: Install dependencies | ||
| if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' | ||
| working-directory: ./packages/auth0_api_python | ||
| run: poetry install --no-interaction --no-root | ||
|
|
||
| - name: Install package | ||
| working-directory: ./packages/auth0_api_python | ||
| run: poetry install --no-interaction | ||
|
|
||
| - name: Run tests with pytest | ||
kishore7snehil marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| working-directory: ./packages/auth0_api_python | ||
| run: | | ||
| poetry run pytest -v --cov=src --cov-report=term-missing --cov-report=xml | ||
|
|
||
| - name: Run ruff linting | ||
| working-directory: ./packages/auth0_api_python | ||
| run: | | ||
| poetry run ruff check . | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| line-length = 100 | ||
| target-version = "py39" | ||
| select = [ | ||
| "E", # pycodestyle errors | ||
| "W", # pycodestyle warnings | ||
| "F", # pyflakes | ||
| "I", # isort | ||
| "B", # flake8-bugbear | ||
| "C4", # flake8-comprehensions | ||
| "UP", # pyupgrade | ||
| "S", # bandit (security) | ||
| ] | ||
| ignore = ["E501", "B904"] # Line too long (handled by black), Exception handling without from | ||
|
|
||
| [per-file-ignores] | ||
| "tests/*" = ["S101", "S105", "S106"] # Allow assert and ignore hardcoded password warnings in test files |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| # Auth0 API Python Examples | ||
|
|
||
| This document provides examples for using the `auth0-api-python` package to validate Auth0 tokens in your API. | ||
|
|
||
| ## Bearer Authentication | ||
|
|
||
| Bearer authentication is the standard OAuth 2.0 token authentication method. | ||
|
|
||
| ### Using verify_access_token | ||
|
|
||
| ```python | ||
| import asyncio | ||
| from auth0_api_python import ApiClient, ApiClientOptions | ||
|
|
||
| async def validate_bearer_token(headers): | ||
| api_client = ApiClient(ApiClientOptions( | ||
| domain="your-tenant.auth0.com", | ||
| audience="https://api.example.com" | ||
| )) | ||
|
|
||
| try: | ||
| # Extract the token from the Authorization header | ||
| auth_header = headers.get("authorization", "") | ||
| if not auth_header.startswith("Bearer "): | ||
| return {"error": "Missing or invalid authorization header"}, 401 | ||
|
|
||
| token = auth_header.split(" ")[1] | ||
|
|
||
| # Verify the access token | ||
| claims = await api_client.verify_access_token(token) | ||
| return {"success": True, "user": claims["sub"]} | ||
| except Exception as e: | ||
| return {"error": str(e)}, getattr(e, "get_status_code", lambda: 401)() | ||
|
|
||
| # Example usage | ||
| headers = {"authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."} | ||
| result = asyncio.run(validate_bearer_token(headers)) | ||
| ``` | ||
|
|
||
| ### Using verify_request | ||
|
|
||
| ```python | ||
| import asyncio | ||
| from auth0_api_python import ApiClient, ApiClientOptions | ||
| from auth0_api_python.errors import BaseAuthError | ||
|
|
||
| async def validate_request(headers): | ||
| api_client = ApiClient(ApiClientOptions( | ||
| domain="your-tenant.auth0.com", | ||
| audience="https://api.example.com" | ||
| )) | ||
|
|
||
| try: | ||
| # Verify the request with Bearer token | ||
| claims = await api_client.verify_request( | ||
| headers=headers | ||
| ) | ||
| return {"success": True, "user": claims["sub"]} | ||
| except BaseAuthError as e: | ||
| return {"error": str(e)}, e.get_status_code(), e.get_headers() | ||
|
|
||
| # Example usage | ||
| headers = {"authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."} | ||
| result = asyncio.run(validate_request(headers)) | ||
| ``` | ||
|
|
||
|
|
||
| ## DPoP Authentication | ||
|
|
||
| [DPoP](https://www.rfc-editor.org/rfc/rfc9449.html) (Demonstrating Proof of Posession) is an application-level mechanism for sender-constraining OAuth 2.0 access and refresh tokens by proving that the client application is in possession of a certain private key. | ||
|
|
||
| This guide covers the DPoP implementation in `auth0-api-python` with complete examples for both operational modes. | ||
|
|
||
| For more information about DPoP specification, see [RFC 9449](https://tools.ietf.org/html/rfc9449). | ||
|
|
||
| ## Configuration Modes | ||
|
|
||
| ### 1. Allowed Mode (Default) | ||
| ```python | ||
| from auth0_api_python import ApiClient, ApiClientOptions | ||
|
|
||
| api_client = ApiClient(ApiClientOptions( | ||
| domain="your-tenant.auth0.com", | ||
| audience="https://api.example.com", | ||
| dpop_enabled=True, # Default: enables DPoP support | ||
| dpop_required=False # Default: allows both Bearer and DPoP | ||
| )) | ||
| ``` | ||
|
|
||
| ### 2. Required Mode | ||
| ```python | ||
| api_client = ApiClient(ApiClientOptions( | ||
| domain="your-tenant.auth0.com", | ||
| audience="https://api.example.com", | ||
| dpop_required=True # Enforces DPoP-only authentication | ||
| )) | ||
| ``` | ||
|
|
||
| ## Getting Started | ||
|
|
||
| ### Basic Usage with verify_request() | ||
|
|
||
| The `verify_request()` method automatically detects the authentication scheme: | ||
|
|
||
| ```python | ||
| import asyncio | ||
| from auth0_api_python import ApiClient, ApiClientOptions | ||
|
|
||
| async def handle_api_request(headers, http_method, http_url): | ||
| api_client = ApiClient(ApiClientOptions( | ||
| domain="your-tenant.auth0.com", | ||
| audience="https://api.example.com" | ||
| )) | ||
|
|
||
| try: | ||
| # Automatically handles both Bearer and DPoP schemes | ||
| claims = await api_client.verify_request( | ||
| headers=headers, | ||
| http_method=http_method, | ||
| http_url=http_url | ||
| ) | ||
| return {"success": True, "user": claims["sub"]} | ||
| except Exception as e: | ||
| return {"error": str(e)}, e.get_status_code() | ||
|
|
||
| # Example usage | ||
| headers = { | ||
| "authorization": "DPoP eyJ0eXAiOiJKV1Q...", | ||
| "dpop": "eyJ0eXAiOiJkcG9wK2p3dC..." | ||
| } | ||
| result = asyncio.run(handle_api_request(headers, "GET", "https://api.example.com/data")) | ||
| ``` | ||
|
|
||
| ### Direct DPoP Proof Verification | ||
|
|
||
| For more control, use `verify_dpop_proof()` directly: | ||
|
|
||
| ```python | ||
| async def verify_dpop_token(access_token, dpop_proof, http_method, http_url): | ||
| api_client = ApiClient(ApiClientOptions( | ||
| domain="your-tenant.auth0.com", | ||
| audience="https://api.example.com" | ||
| )) | ||
|
|
||
| # First verify the access token | ||
| token_claims = await api_client.verify_access_token(access_token) | ||
|
|
||
| # Then verify the DPoP proof | ||
| proof_claims = await api_client.verify_dpop_proof( | ||
| access_token=access_token, | ||
| proof=dpop_proof, | ||
| http_method=http_method, | ||
| http_url=http_url | ||
| ) | ||
|
|
||
| return { | ||
| "token_claims": token_claims, | ||
| "proof_claims": proof_claims | ||
| } | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.