diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..c0768f8
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,42 @@
+version: 2
+
+updates:
+- package-ecosystem: github-actions
+ directory: "/"
+ schedule:
+ interval: daily
+ time: "04:24"
+ target-branch: main
+ labels:
+ - CI/CD
+ - skip-changelog
+ groups:
+ actions:
+ applies-to: "version-updates"
+ patterns:
+ - "*"
+ actions-security:
+ applies-to: "security-updates"
+ patterns:
+ - "*"
+
+- package-ecosystem: pip
+ directories:
+ - "/"
+ - "/.github/utils"
+ schedule:
+ interval: weekly
+ time: "04:16"
+ target-branch: main
+ labels:
+ - dependencies
+ - skip-changelog
+ groups:
+ python:
+ applies-to: "version-updates"
+ patterns:
+ - "*"
+ python-security:
+ applies-to: "security-updates"
+ patterns:
+ - "*"
diff --git a/.github/utils/requirements_ci.txt b/.github/utils/requirements_ci.txt
new file mode 100644
index 0000000..b3f6f9f
--- /dev/null
+++ b/.github/utils/requirements_ci.txt
@@ -0,0 +1,2 @@
+pip-tools~=7.5
+pre-commit~=4.6
diff --git a/.github/workflows/cd_release.yml b/.github/workflows/cd_release.yml
new file mode 100644
index 0000000..398e7b1
--- /dev/null
+++ b/.github/workflows/cd_release.yml
@@ -0,0 +1,68 @@
+name: CD - Release
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ build:
+ name: External
+ uses: SINTEF/ci-cd/.github/workflows/cd_release.yml@v2.10.0
+ if: github.repository == 'SemanticMatter/httpx2_auth' && startsWith(github.ref, 'refs/tags/v')
+
+ permissions:
+ contents: write
+
+ with:
+ # General
+ git_username: ${{ vars.CI_CD_GIT_USER }}
+ git_email: ${{ vars.CI_CD_GIT_EMAIL }}
+ release_branch: main
+
+ # Python package
+ python_package: true
+ package_dirs: httpx2_auth
+ install_extras: "[dev]"
+ pip_index_url: "${{ vars.EXTERNAL_PYPI_INDEX_URL }}"
+ python_version_build: "3.10"
+ build_libs: build
+ build_cmd: "python -m build"
+ build_dir: "dist"
+ changelog_exclude_labels: skip-changelog,duplicate,question,invalid,wontfix
+ publish_on_pypi: false
+ upload_distribution: true
+ version_update_changes_separator: ","
+ version_update_changes: |
+ {package_dir}/__init__.py,__version__ *= *(?:'|\").*(?:'|\"),__version__ = \"{version}\"
+ .github/workflows/ci_tests.yml,dist/httpx2_auth-2\.[0-9]+\.[0-9]+\.[0-9]+,dist/httpx2_auth-{version}
+
+ # Documentation
+ update_docs: false
+
+ secrets:
+ PAT: ${{ secrets.BOT_PAT }}
+
+ publish:
+ name: Publish to SINTEF GitLab
+ needs: build
+ runs-on: ubuntu-latest
+
+ environment:
+ name: GitLab
+ url: ${{ vars.GITLAB_PACKAGE_URL }}
+
+ steps:
+ - name: Download distribution
+ uses: actions/download-artifact@v8
+ with:
+ name: dist # The artifact will always be called 'dist'
+ path: dist
+
+ - name: Publish to SemanticMatter @ SINTEF GitLab
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ # The path to the distribution to upload
+ packages-dir: dist
+ repository-url: ${{ vars.GITLAB_PACKAGE_REGISTRY_URL }}
+ user: ${{ secrets.DEPLOY_TOKEN_USERNAME }}
+ password: ${{ secrets.DEPLOY_TOKEN_PASSWORD }}
diff --git a/.github/workflows/ci_automerge.yml b/.github/workflows/ci_automerge.yml
new file mode 100644
index 0000000..c8c9c22
--- /dev/null
+++ b/.github/workflows/ci_automerge.yml
@@ -0,0 +1,13 @@
+name: CI - Activate auto-merging
+
+on:
+ pull_request_target:
+ branches: [main]
+
+jobs:
+ update-dependabot-branch:
+ name: External
+ uses: SINTEF/ci-cd/.github/workflows/ci_automerge_prs.yml@v2.10.0
+ if: github.repository_owner == 'SemanticMatter' && ( ( startsWith(github.event.pull_request.head.ref, 'dependabot/') && github.actor == 'dependabot[bot]' ) || ( github.event.pull_request.head.ref == 'pre-commit-ci-update-config' && github.actor == 'pre-commit-ci[bot]' ) )
+ secrets:
+ PAT: ${{ secrets.BOT_PAT }}
diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml
new file mode 100644
index 0000000..8bb72ef
--- /dev/null
+++ b/.github/workflows/ci_tests.yml
@@ -0,0 +1,120 @@
+name: CI - Tests
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+ - 'push-action/**' # Allow pushing to protected branches (using CasperWA/push-protected)
+
+env:
+ PIP_INDEX_URL: "${{ vars.EXTERNAL_PYPI_INDEX_URL }}"
+
+jobs:
+ pre-commit_audit:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout ${{ github.repository }}
+ uses: actions/checkout@v6
+
+ - name: Set up Python 3.10
+ uses: actions/setup-python@v6
+ with:
+ python-version: '3.10'
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -U setuptools wheel
+ pip install -U -r .github/utils/requirements_ci.txt
+
+ - name: Run pre-commit
+ # Ensure pip-audit is also run - if pre-commit fails, pip-audit will still run
+ # If pre-commit fails (but pip-audit does not), the job will fail from the last step
+ continue-on-error: true
+ id: pre_commit
+ run: pre-commit run --all-files
+
+ - name: Create requirements.txt file for pip-audit
+ run: pip-compile --index-url="${{ env.PIP_INDEX_URL }}" --output-file="${{ runner.temp }}/requirements.txt" --all-extras --verbose --color "${{ github.workspace }}/pyproject.toml"
+
+ - name: Run pip-audit
+ uses: pypa/gh-action-pip-audit@v1.1.0
+ with:
+ extra-index-urls: ${{ env.PIP_INDEX_URL }}
+ no-deps: true
+ inputs: ${{ runner.temp }}/requirements.txt
+
+ - name: Fail if pre-commit failed
+ if: steps.pre_commit.outcome == 'failure'
+ run: |
+ echo "❌ pre-commit run failed (see step 'Run pre-commit' above for details)"
+ exit 1
+
+ pytest:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python-version: ['3.10', '3.11', '3.12', '3.13']
+
+ steps:
+ - name: Checkout ${{ github.repository }}
+ uses: actions/checkout@v6
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v6
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -U setuptools wheel
+ pip install -U -e .[testing]
+
+ - name: Run pytest
+ run: pytest -vv --color=yes --doctest-modules --cov-report=xml ./tests
+
+ - name: Upload coverage
+ if: github.repository_owner == 'SemanticMatter'
+ uses: codecov/codecov-action@v6
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ fail_ci_if_error: true
+ env_vars: PYTHON
+ flags: local
+ env:
+ PYTHON: ${{ matrix.python-version }}
+
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout ${{ github.repository }}
+ uses: actions/checkout@v6
+
+ - name: Set up Python 3.10
+ uses: actions/setup-python@v6
+ with:
+ python-version: '3.10'
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -U setuptools wheel
+ pip install -U build
+
+ - name: Build package
+ run: |
+ python -m build .
+ rm -Rf httpx2_auth
+
+ - name: Install wheel
+ run: |
+ python -m pip install dist/httpx2_auth-2.0.1.0-py3-none-any.whl --force-reinstall
+ python -c 'import httpx2_auth'
+
+ - name: Install source distribution
+ run: |
+ python -m pip install dist/httpx2_auth-2.0.1.0.tar.gz --force-reinstall
+ python -c 'import httpx2_auth'
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index 5338b25..0000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-name: Release
-
-on:
- push:
- branches:
- - master
-
-jobs:
- build:
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v4
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: '3.13'
- - name: Create packages
- run: |
- python -m pip install build
- python -m build .
- - name: Publish packages
- run: |
- python -m pip install twine
- python -m twine upload dist/* --skip-existing --username __token__ --password ${{ secrets.pypi_password }}
\ No newline at end of file
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
deleted file mode 100644
index 0c58bf0..0000000
--- a/.github/workflows/test.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-name: Test
-
-on: [push, pull_request]
-
-jobs:
- build:
-
- runs-on: ubuntu-latest
- strategy:
- matrix:
- python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
-
- steps:
- - uses: actions/checkout@v4
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v5
- with:
- python-version: ${{ matrix.python-version }}
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- python -m pip install .[testing]
- - name: Test with pytest
- run: |
- pytest --doctest-modules --cov=httpx_auth --cov-fail-under=100 --cov-report=term-missing
- - name: Create packages
- run: |
- python -m pip install build
- python -m build .
- rm -Rf httpx_auth
- - name: Install wheel
- run: |
- python -m pip install dist/httpx_auth-0.23.1-py3-none-any.whl --force-reinstall
- python -c 'import httpx_auth'
- - name: Install source distribution
- run: |
- python -m pip install dist/httpx_auth-0.23.1.tar.gz --force-reinstall
- python -c 'import httpx_auth'
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 8856f1b..e96c32e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,5 +1,83 @@
+# pre-commit.ci
+ci:
+ autofix_commit_msg: |
+ [pre-commit.ci] auto fixes from pre-commit hooks
+
+ For more information, see https://pre-commit.ci
+ autofix_prs: false
+ autoupdate_branch: 'main'
+ autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate'
+ autoupdate_schedule: 'weekly'
+ skip: []
+ submodules: false
+
+# hooks
repos:
- - repo: https://github.com/psf/black
- rev: 24.8.0
+ # General-purpose hygiene hooks.
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v6.0.0
hooks:
- - id: black
\ No newline at end of file
+ - id: check-json
+ - id: check-toml
+ - id: check-yaml
+ - id: debug-statements
+ - id: end-of-file-fixer
+ - id: mixed-line-ending
+ - id: name-tests-test
+ args: ["--pytest-test-first"]
+ - id: trailing-whitespace
+ args: ["--markdown-linebreak-ext=md"]
+ - id: check-added-large-files
+ - id: check-merge-conflict
+
+ # pyupgrade — modernize syntax to the project's minimum Python.
+ - repo: https://github.com/asottile/pyupgrade
+ rev: v3.21.2
+ hooks:
+ - id: pyupgrade
+ args: ["--py310-plus", "--keep-runtime-typing"]
+
+ # black — code formatter (config in [tool.black]).
+ - repo: https://github.com/psf/black-pre-commit-mirror
+ rev: 26.5.1
+ hooks:
+ - id: black
+
+ # blacken-docs — format Python code blocks inside docs.
+ - repo: https://github.com/adamchainz/blacken-docs
+ rev: 1.20.0
+ hooks:
+ - id: blacken-docs
+ additional_dependencies: [black]
+
+ # ruff — linter only (config in [tool.ruff]; black does the formatting).
+ - repo: https://github.com/astral-sh/ruff-pre-commit
+ rev: v0.15.15
+ hooks:
+ - id: ruff-check
+ name: ruff
+ args:
+ - "--fix"
+ - "--exit-non-zero-on-fix"
+ - "--show-fixes"
+ - "--no-unsafe-fixes"
+
+ # bandit — security linter (config in [tool.bandit]; needs the toml extra).
+ - repo: https://github.com/PyCQA/bandit
+ rev: 1.9.4
+ hooks:
+ - id: bandit
+ args: ["-c", "pyproject.toml", "-r"]
+ # testing.py is a test-support module (browser/HTTP-server mocks); exempt like tests/.
+ exclude: ^(tests/.*|httpx2_auth/testing\.py)$
+ additional_dependencies: ["bandit[toml]"]
+
+ # mypy — static type checking (strict; config in [tool.mypy]). Runs in its own
+ # isolated env with the project's runtime deps pinned for type resolution.
+ - repo: https://github.com/pre-commit/mirrors-mypy
+ rev: v2.1.0
+ hooks:
+ - id: mypy
+ exclude: ^tests/.*$
+ additional_dependencies:
+ - "httpx2~=2.3"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 01ab6ec..0d91436 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -22,9 +22,9 @@ Before creating an issue please make sure that it was not already reported.
1) Go to the *Issues* tab and click on the *New issue* button.
2) Title should be a small sentence describing the request.
3) The comment should contain as much information as possible
- * Actual behavior (including the version you used)
- * Expected behavior
- * Steps to reproduce
+ - Actual behavior (including the version you used)
+ - Expected behavior
+ - Steps to reproduce
## Submitting a pull request
@@ -40,16 +40,16 @@ Before creating an issue please make sure that it was not already reported.
1) Create a new branch based on `develop` branch.
2) Fetch all dev dependencies.
- * Install required python modules using `pip`: **python -m pip install .[testing]**
+ - Install required python modules using `pip`: **python -m pip install .[testing]**
3) Ensure tests are ok by running them using [`pytest`](http://doc.pytest.org/en/latest/index.html).
4) Add your changes.
5) Follow [Black](https://black.readthedocs.io/en/stable/) code formatting.
- * Install [pre-commit](https://pre-commit.com) python module using `pip`: **python -m pip install pre-commit**
- * To add the [pre-commit](https://pre-commit.com) hook, after the installation run: **pre-commit install**
+ - Install [pre-commit](https://pre-commit.com) python module using `pip`: **python -m pip install pre-commit**
+ - To add the [pre-commit](https://pre-commit.com) hook, after the installation run: **pre-commit install**
6) Add at least one [`pytest`](http://doc.pytest.org/en/latest/index.html) test case.
- * Unless it is an internal refactoring request or a documentation update.
+ - Unless it is an internal refactoring request or a documentation update.
7) Add related [changelog entry](https://keepachangelog.com/en/1.0.0/) in the `Unreleased` section.
- * Unless it is a documentation update.
+ - Unless it is a documentation update.
#### Enter pull request
@@ -57,6 +57,6 @@ Before creating an issue please make sure that it was not already reported.
2) *base* should always be set to `develop` and it should be compared to your branch.
3) Title should be a small sentence describing the request.
4) The comment should contain as much information as possible
- * Actual behavior (before the new code)
- * Expected behavior (with the new code)
- * Steps to reproduce (with and without the new code to see the difference)
+ - Actual behavior (before the new code)
+ - Expected behavior (with the new code)
+ - Steps to reproduce (with and without the new code to see the difference)
diff --git a/LICENSE b/LICENSE
index cab2d30..00ff532 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2024 Colin Bounouar
+Copyright (c) 2024-2026 Colin Bounouar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 06c1ddd..ce610d7 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,21 @@
-
Authentication for HTTPX
+Authentication for HTTPX2
+
+> [!NOTE]
+> This is a fork of [Colin-b/httpx_auth](https://github.com/Colin-b/httpx_auth) with support for [HTTPX2](https://httpx2.pydantic.dev).
+>
+> There is not associated docuemntation, and everything below this NOTE is the same as in the original repository.
+> With the exception of code snippets and links to create new issues.
+> These have been directed to this repository instead of the original one.
+>
+> For more information about this project, please refer to the original repository.
-
-
-
+
+
+
-
-
+
+
> [!NOTE]
@@ -53,25 +62,31 @@ Provides authentication classes to be used with [`httpx`][1] [authentication par
Most of [OAuth2](https://oauth.net/2/) flows are supported.
-If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/Colin-b/httpx_auth/issues/new).
+If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/SemanticMatter/httpx2_auth/issues/new).
### Authorization Code flow
Authorization Code Grant is implemented following [rfc6749](https://tools.ietf.org/html/rfc6749#section-4.1).
-Use `httpx_auth.OAuth2AuthorizationCode` to configure this kind of authentication.
+Use `httpx2_auth.OAuth2AuthorizationCode` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import OAuth2AuthorizationCode
-
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=OAuth2AuthorizationCode('https://www.authorization.url', 'https://www.token.url'))
+import httpx2
+from httpx2_auth import OAuth2AuthorizationCode
+
+with httpx2.Client() as client:
+ client.get(
+ "https://www.example.com",
+ auth=OAuth2AuthorizationCode(
+ "https://www.authorization.url", "https://www.token.url"
+ ),
+ )
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
-* You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
+- You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
#### Parameters
@@ -91,12 +106,12 @@ Note:
| `code_field_name` | Field name containing the code. | Optional | code |
| `username` | User name in case basic authentication should be used to retrieve token. | Optional | |
| `password` | User password in case basic authentication should be used to retrieve token. | Optional | |
-| `client` | `httpx.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
+| `client` | `httpx2.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
-Any other parameter will be put as query parameter in the authorization URL and as body parameters in the token URL.
+Any other parameter will be put as query parameter in the authorization URL and as body parameters in the token URL.
Usual extra parameters are:
-
+
| Name | Description |
|:----------------|:---------------------------------------------------------------------|
| `client_id` | Corresponding to your Application ID (in Microsoft Azure app portal) |
@@ -107,27 +122,30 @@ Usual extra parameters are:
Most of [OAuth2](https://oauth.net/2/) Authorization Code Grant providers are supported.
-If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/Colin-b/httpx_auth/issues/new).
+If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/SemanticMatter/httpx2_auth/issues/new).
##### Okta (OAuth2 Authorization Code)
[Okta Authorization Code Grant](https://developer.okta.com/docs/guides/implement-auth-code/overview/) providing access tokens is supported.
-Use `httpx_auth.OktaAuthorizationCode` to configure this kind of authentication.
+Use `httpx2_auth.OktaAuthorizationCode` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import OktaAuthorizationCode
-
-
-okta = OktaAuthorizationCode(instance='testserver.okta-emea.com', client_id='54239d18-c68c-4c47-8bdd-ce71ea1d50cd')
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=okta)
+import httpx2
+from httpx2_auth import OktaAuthorizationCode
+
+okta = OktaAuthorizationCode(
+ instance="testserver.okta-emea.com",
+ client_id="54239d18-c68c-4c47-8bdd-ce71ea1d50cd",
+)
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=okta)
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
-* You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
+- You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
###### Parameters
@@ -147,12 +165,12 @@ Note:
| `timeout` | Maximum amount of seconds to wait for a token to be received once requested. | Optional | 60 |
| `header_name` | Name of the header field used to send token. | Optional | Authorization |
| `header_value` | Format used to send the token value. "{token}" must be present as it will be replaced by the actual token. | Optional | Bearer {token} |
-| `client` | `httpx.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
+| `client` | `httpx2.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
-Any other parameter will be put as query parameter in the authorization URL.
+Any other parameter will be put as query parameter in the authorization URL.
Usual extra parameters are:
-
+
| Name | Description |
|:----------------|:---------------------------------------------------------------------|
| `prompt` | none to avoid prompting the user if a session is already opened. |
@@ -161,21 +179,23 @@ Usual extra parameters are:
[WakaTime Authorization Code Grant](https://wakatime.com/developers#authentication) providing access tokens is supported.
-Use `httpx_auth.WakaTimeAuthorizationCode` to configure this kind of authentication.
+Use `httpx2_auth.WakaTimeAuthorizationCode` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import WakaTimeAuthorizationCode
-
-
-waka_time = WakaTimeAuthorizationCode(client_id="aPJQV0op6Pu3b66MWDi9b1wB", client_secret="waka_sec_0c5MB", scope="email")
-with httpx.Client() as client:
- client.get('https://wakatime.com/api/v1/users/current', auth=waka_time)
+import httpx2
+from httpx2_auth import WakaTimeAuthorizationCode
+
+waka_time = WakaTimeAuthorizationCode(
+ client_id="aPJQV0op6Pu3b66MWDi9b1wB", client_secret="waka_sec_0c5MB", scope="email"
+)
+with httpx2.Client() as client:
+ client.get("https://wakatime.com/api/v1/users/current", auth=waka_time)
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
-* You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
+- You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
###### Parameters
@@ -194,7 +214,7 @@ Note:
| `timeout` | Maximum amount of seconds to wait for a token to be received once requested. | Optional | 60 |
| `header_name` | Name of the header field used to send token. | Optional | Authorization |
| `header_value` | Format used to send the token value. "{token}" must be present as it will be replaced by the actual token. | Optional | Bearer {token} |
-| `client` | `httpx.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
+| `client` | `httpx2.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
Any other parameter will be put as query parameter in the authorization URL.
@@ -202,21 +222,27 @@ Any other parameter will be put as query parameter in the authorization URL.
Proof Key for Code Exchange is implemented following [rfc7636](https://tools.ietf.org/html/rfc7636).
-Use `httpx_auth.OAuth2AuthorizationCodePKCE` to configure this kind of authentication.
+Use `httpx2_auth.OAuth2AuthorizationCodePKCE` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import OAuth2AuthorizationCodePKCE
-
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=OAuth2AuthorizationCodePKCE('https://www.authorization.url', 'https://www.token.url'))
+import httpx2
+from httpx2_auth import OAuth2AuthorizationCodePKCE
+
+with httpx2.Client() as client:
+ client.get(
+ "https://www.example.com",
+ auth=OAuth2AuthorizationCodePKCE(
+ "https://www.authorization.url", "https://www.token.url"
+ ),
+ )
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
-* You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
-#### Parameters
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
+- You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
+
+#### Parameters
| Name | Description | Mandatory | Default value |
|:------------------------|:---------------------------|:----------|:--------------|
@@ -232,12 +258,12 @@ Note:
| `token_field_name` | Field name containing the token. | Optional | access_token |
| `early_expiry` | Number of seconds before actual token expiry where token will be considered as expired. Used to ensure token will not expire between the time of retrieval and the time the request reaches the actual server. Set it to 0 to deactivate this feature and use the same token until actual expiry. | Optional | 30.0 |
| `code_field_name` | Field name containing the code. | Optional | code |
-| `client` | `httpx.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
+| `client` | `httpx2.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
-Any other parameter will be put as query parameter in the authorization URL and as body parameters in the token URL.
+Any other parameter will be put as query parameter in the authorization URL and as body parameters in the token URL.
Usual extra parameters are:
-
+
| Name | Description |
|:----------------|:---------------------------------------------------------------------|
| `client_id` | Corresponding to your Application ID (in Microsoft Azure app portal) |
@@ -248,27 +274,30 @@ Usual extra parameters are:
Most of [OAuth2](https://oauth.net/2/) Proof Key for Code Exchange providers are supported.
-If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/Colin-b/httpx_auth/issues/new).
+If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/SemanticMatter/httpx2_auth/issues/new).
##### Okta (OAuth2 Proof Key for Code Exchange)
[Okta Proof Key for Code Exchange](https://developer.okta.com/docs/guides/implement-auth-code-pkce/overview/) providing access tokens is supported.
-Use `httpx_auth.OktaAuthorizationCodePKCE` to configure this kind of authentication.
+Use `httpx2_auth.OktaAuthorizationCodePKCE` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import OktaAuthorizationCodePKCE
-
-
-okta = OktaAuthorizationCodePKCE(instance='testserver.okta-emea.com', client_id='54239d18-c68c-4c47-8bdd-ce71ea1d50cd')
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=okta)
+import httpx2
+from httpx2_auth import OktaAuthorizationCodePKCE
+
+okta = OktaAuthorizationCodePKCE(
+ instance="testserver.okta-emea.com",
+ client_id="54239d18-c68c-4c47-8bdd-ce71ea1d50cd",
+)
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=okta)
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
-* You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
+- You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
###### Parameters
@@ -289,12 +318,12 @@ Note:
| `timeout` | Maximum amount of seconds to wait for a token to be received once requested. | Optional | 60 |
| `header_name` | Name of the header field used to send token. | Optional | Authorization |
| `header_value` | Format used to send the token value. "{token}" must be present as it will be replaced by the actual token. | Optional | Bearer {token} |
-| `client` | `httpx.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
+| `client` | `httpx2.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
-Any other parameter will be put as query parameter in the authorization URL and as body parameters in the token URL.
+Any other parameter will be put as query parameter in the authorization URL and as body parameters in the token URL.
Usual extra parameters are:
-
+
| Name | Description |
|:----------------|:---------------------------------------------------------------------|
| `client_secret` | If client is not authenticated with the authorization server |
@@ -304,18 +333,24 @@ Usual extra parameters are:
Resource Owner Password Credentials Grant is implemented following [rfc6749](https://tools.ietf.org/html/rfc6749#section-4.3).
-Use `httpx_auth.OAuth2ResourceOwnerPasswordCredentials` to configure this kind of authentication.
+Use `httpx2_auth.OAuth2ResourceOwnerPasswordCredentials` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import OAuth2ResourceOwnerPasswordCredentials
-
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=OAuth2ResourceOwnerPasswordCredentials('https://www.token.url', 'user name', 'user password'))
+import httpx2
+from httpx2_auth import OAuth2ResourceOwnerPasswordCredentials
+
+with httpx2.Client() as client:
+ client.get(
+ "https://www.example.com",
+ auth=OAuth2ResourceOwnerPasswordCredentials(
+ "https://www.token.url", "user name", "user password"
+ ),
+ )
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
#### Parameters
@@ -324,14 +359,14 @@ Note:
| `token_url` | OAuth 2 token URL. | Mandatory | |
| `username` | Resource owner user name. | Mandatory | |
| `password` | Resource owner password. | Mandatory | |
-| `client_auth` | Client authentication if the client type is confidential or the client was issued client credentials (or assigned other authentication requirements). Can be a tuple or any httpx authentication class instance. | Optional | |
+| `client_auth` | Client authentication if the client type is confidential or the client was issued client credentials (or assigned other authentication requirements). Can be a tuple or any httpx2 authentication class instance. | Optional | |
| `timeout` | Maximum amount of seconds to wait for a token to be received once requested. | Optional | 60 |
| `header_name` | Name of the header field used to send token. | Optional | Authorization |
| `header_value` | Format used to send the token value. "{token}" must be present as it will be replaced by the actual token. | Optional | Bearer {token} |
| `scope` | Scope parameter sent to token URL as body. Can also be a list of scopes. | Optional | |
| `token_field_name` | Field name containing the token. | Optional | access_token |
| `early_expiry` | Number of seconds before actual token expiry where token will be considered as expired. Used to ensure token will not expire between the time of retrieval and the time the request reaches the actual server. Set it to 0 to deactivate this feature and use the same token until actual expiry. | Optional | 30.0 |
-| `client` | `httpx.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
+| `client` | `httpx2.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
Any other parameter will be put as body parameter in the token URL.
@@ -339,26 +374,32 @@ Any other parameter will be put as body parameter in the token URL.
Most of [OAuth2](https://oauth.net/2/) Resource Owner Password Credentials providers are supported.
-If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/Colin-b/httpx_auth/issues/new).
+If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/SemanticMatter/httpx2_auth/issues/new).
##### Okta (OAuth2 Resource Owner Password Credentials)
[Okta Resource Owner Password Credentials](https://developer.okta.com/docs/guides/implement-grant-type/ropassword/main/) providing access tokens is supported.
-Use `httpx_auth.OktaResourceOwnerPasswordCredentials` to configure this kind of authentication.
+Use `httpx2_auth.OktaResourceOwnerPasswordCredentials` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import OktaResourceOwnerPasswordCredentials
-
-
-okta = OktaResourceOwnerPasswordCredentials(instance='testserver.okta-emea.com', username='user name', password='user password', client_id='54239d18-c68c-4c47-8bdd-ce71ea1d50cd', client_secret="0c5MB")
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=okta)
+import httpx2
+from httpx2_auth import OktaResourceOwnerPasswordCredentials
+
+okta = OktaResourceOwnerPasswordCredentials(
+ instance="testserver.okta-emea.com",
+ username="user name",
+ password="user password",
+ client_id="54239d18-c68c-4c47-8bdd-ce71ea1d50cd",
+ client_secret="0c5MB",
+)
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=okta)
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
###### Parameters
@@ -375,27 +416,32 @@ Note:
| `scope` | Scope parameter sent in query. Can also be a list of scopes. | Optional | openid |
| `token_field_name` | Field name containing the token. | Optional | access_token |
| `early_expiry` | Number of seconds before actual token expiry where token will be considered as expired. Used to ensure token will not expire between the time of retrieval and the time the request reaches the actual server. Set it to 0 to deactivate this feature and use the same token until actual expiry. | Optional | 30.0 |
-| `client` | `httpx.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
+| `client` | `httpx2.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
Any other parameter will be put as body parameters in the token URL.
-
### Client Credentials flow
Client Credentials Grant is implemented following [rfc6749](https://tools.ietf.org/html/rfc6749#section-4.4).
-Use `httpx_auth.OAuth2ClientCredentials` to configure this kind of authentication.
+Use `httpx2_auth.OAuth2ClientCredentials` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import OAuth2ClientCredentials
-
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=OAuth2ClientCredentials('https://www.token.url', client_id='id', client_secret='secret'))
+import httpx2
+from httpx2_auth import OAuth2ClientCredentials
+
+with httpx2.Client() as client:
+ client.get(
+ "https://www.example.com",
+ auth=OAuth2ClientCredentials(
+ "https://www.token.url", client_id="id", client_secret="secret"
+ ),
+ )
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
#### Parameters
@@ -410,7 +456,7 @@ Note:
| `scope` | Scope parameter sent to token URL as body. Can also be a list of scopes. | Optional | |
| `token_field_name` | Field name containing the token. | Optional | access_token |
| `early_expiry` | Number of seconds before actual token expiry where token will be considered as expired. Used to ensure token will not expire between the time of retrieval and the time the request reaches the actual server. Set it to 0 to deactivate this feature and use the same token until actual expiry. | Optional | 30.0 |
-| `client` | `httpx.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
+| `client` | `httpx2.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
Any other parameter will be put as body parameter in the token URL.
@@ -418,26 +464,31 @@ Any other parameter will be put as body parameter in the token URL.
Most of [OAuth2](https://oauth.net/2/) Client Credentials Grant providers are supported.
-If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/Colin-b/httpx_auth/issues/new).
+If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/SemanticMatter/httpx2_auth/issues/new).
##### Okta (OAuth2 Client Credentials)
[Okta Client Credentials Grant](https://developer.okta.com/docs/guides/implement-grant-type/clientcreds/main/) providing access tokens is supported.
-Use `httpx_auth.OktaClientCredentials` to configure this kind of authentication.
+Use `httpx2_auth.OktaClientCredentials` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import OktaClientCredentials
-
-
-okta = OktaClientCredentials(instance='testserver.okta-emea.com', client_id='54239d18-c68c-4c47-8bdd-ce71ea1d50cd', client_secret="secret", scope=["scope1", "scope2"])
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=okta)
+import httpx2
+from httpx2_auth import OktaClientCredentials
+
+okta = OktaClientCredentials(
+ instance="testserver.okta-emea.com",
+ client_id="54239d18-c68c-4c47-8bdd-ce71ea1d50cd",
+ client_secret="secret",
+ scope=["scope1", "scope2"],
+)
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=okta)
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
###### Parameters
@@ -453,27 +504,30 @@ Note:
| `header_value` | Format used to send the token value. "{token}" must be present as it will be replaced by the actual token. | Optional | Bearer {token} |
| `token_field_name` | Field name containing the token. | Optional | access_token |
| `early_expiry` | Number of seconds before actual token expiry where token will be considered as expired. Used to ensure token will not expire between the time of retrieval and the time the request reaches the actual server. Set it to 0 to deactivate this feature and use the same token until actual expiry. | Optional | 30.0 |
-| `client` | `httpx.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
+| `client` | `httpx2.Client` instance that will be used to request the token. Use it to provide a custom proxying rule for instance. | Optional | |
-Any other parameter will be put as query parameter in the token URL.
+Any other parameter will be put as query parameter in the token URL.
### Implicit flow
Implicit Grant is implemented following [rfc6749](https://tools.ietf.org/html/rfc6749#section-4.2).
-Use `httpx_auth.OAuth2Implicit` to configure this kind of authentication.
+Use `httpx2_auth.OAuth2Implicit` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import OAuth2Implicit
+import httpx2
+from httpx2_auth import OAuth2Implicit
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=OAuth2Implicit('https://www.authorization.url'))
+with httpx2.Client() as client:
+ client.get(
+ "https://www.example.com", auth=OAuth2Implicit("https://www.authorization.url")
+ )
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
-* You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
+- You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
#### Parameters
@@ -490,10 +544,10 @@ Note:
| `header_name` | Name of the header field used to send token. | Optional | Authorization |
| `header_value` | Format used to send the token value. "{token}" must be present as it will be replaced by the actual token. | Optional | Bearer {token} |
-Any other parameter will be put as query parameter in the authorization URL.
+Any other parameter will be put as query parameter in the authorization URL.
Usual extra parameters are:
-
+
| Name | Description |
|:----------------|:---------------------------------------------------------------------|
| `client_id` | Corresponding to your Application ID (in Microsoft Azure app portal) |
@@ -504,27 +558,30 @@ Usual extra parameters are:
Most of [OAuth2](https://oauth.net/2/) Implicit Grant providers are supported.
-If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/Colin-b/httpx_auth/issues/new).
+If the one you are looking for is not yet supported, feel free to [ask for its implementation](https://github.com/SemanticMatter/httpx2_auth/issues/new).
##### Microsoft - Azure Active Directory (OAuth2 Access Token)
[Microsoft identity platform access tokens](https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens) are supported.
-Use `httpx_auth.AzureActiveDirectoryImplicit` to configure this kind of authentication.
+Use `httpx2_auth.AzureActiveDirectoryImplicit` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import AzureActiveDirectoryImplicit
-
-
-aad = AzureActiveDirectoryImplicit(tenant_id='45239d18-c68c-4c47-8bdd-ce71ea1d50cd', client_id='54239d18-c68c-4c47-8bdd-ce71ea1d50cd')
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=aad)
+import httpx2
+from httpx2_auth import AzureActiveDirectoryImplicit
+
+aad = AzureActiveDirectoryImplicit(
+ tenant_id="45239d18-c68c-4c47-8bdd-ce71ea1d50cd",
+ client_id="54239d18-c68c-4c47-8bdd-ce71ea1d50cd",
+)
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=aad)
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
-* You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
+- You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
You can retrieve Microsoft Azure Active Directory application information thanks to the [application list on Azure portal](https://portal.azure.com/#blade/Microsoft_AAD_IAM/StartboardApplicationsMenuBlade/AllApps/menuId/).
@@ -545,10 +602,10 @@ You can retrieve Microsoft Azure Active Directory application information thanks
| `header_name` | Name of the header field used to send token. | Optional | Authorization |
| `header_value` | Format used to send the token value. "{token}" must be present as it will be replaced by the actual token. | Optional | Bearer {token} |
-Any other parameter will be put as query parameter in the authorization URL.
+Any other parameter will be put as query parameter in the authorization URL.
Usual extra parameters are:
-
+
| Name | Description |
|:----------------|:---------------------------------------------------------------------|
| `prompt` | none to avoid prompting the user if a session is already opened. |
@@ -557,21 +614,24 @@ Usual extra parameters are:
[Microsoft identity platform ID tokens](https://docs.microsoft.com/en-us/azure/active-directory/develop/id-tokens) are supported.
-Use `httpx_auth.AzureActiveDirectoryImplicitIdToken` to configure this kind of authentication.
+Use `httpx2_auth.AzureActiveDirectoryImplicitIdToken` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import AzureActiveDirectoryImplicitIdToken
-
-
-aad = AzureActiveDirectoryImplicitIdToken(tenant_id='45239d18-c68c-4c47-8bdd-ce71ea1d50cd', client_id='54239d18-c68c-4c47-8bdd-ce71ea1d50cd')
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=aad)
+import httpx2
+from httpx2_auth import AzureActiveDirectoryImplicitIdToken
+
+aad = AzureActiveDirectoryImplicitIdToken(
+ tenant_id="45239d18-c68c-4c47-8bdd-ce71ea1d50cd",
+ client_id="54239d18-c68c-4c47-8bdd-ce71ea1d50cd",
+)
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=aad)
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
-* You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
+- You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
You can retrieve Microsoft Azure Active Directory application information thanks to the [application list on Azure portal](https://portal.azure.com/#blade/Microsoft_AAD_IAM/StartboardApplicationsMenuBlade/AllApps/menuId/).
@@ -592,10 +652,10 @@ You can retrieve Microsoft Azure Active Directory application information thanks
| `header_name` | Name of the header field used to send token. | Optional | Authorization |
| `header_value` | Format used to send the token value. "{token}" must be present as it will be replaced by the actual token. | Optional | Bearer {token} |
-Any other parameter will be put as query parameter in the authorization URL.
+Any other parameter will be put as query parameter in the authorization URL.
Usual extra parameters are:
-
+
| Name | Description |
|:----------------|:---------------------------------------------------------------------|
| `prompt` | none to avoid prompting the user if a session is already opened. |
@@ -604,21 +664,24 @@ Usual extra parameters are:
[Okta Implicit Grant](https://developer.okta.com/docs/guides/implement-implicit/overview/) providing access tokens is supported.
-Use `httpx_auth.OktaImplicit` to configure this kind of authentication.
+Use `httpx2_auth.OktaImplicit` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import OktaImplicit
-
-
-okta = OktaImplicit(instance='testserver.okta-emea.com', client_id='54239d18-c68c-4c47-8bdd-ce71ea1d50cd')
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=okta)
+import httpx2
+from httpx2_auth import OktaImplicit
+
+okta = OktaImplicit(
+ instance="testserver.okta-emea.com",
+ client_id="54239d18-c68c-4c47-8bdd-ce71ea1d50cd",
+)
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=okta)
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
-* You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
+- You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
###### Parameters
@@ -639,10 +702,10 @@ Note:
| `header_name` | Name of the header field used to send token. | Optional | Authorization |
| `header_value` | Format used to send the token value. "{token}" must be present as it will be replaced by the actual token. | Optional | Bearer {token} |
-Any other parameter will be put as query parameter in the authorization URL.
+Any other parameter will be put as query parameter in the authorization URL.
Usual extra parameters are:
-
+
| Name | Description |
|:----------------|:---------------------------------------------------------------------|
| `prompt` | none to avoid prompting the user if a session is already opened. |
@@ -651,21 +714,24 @@ Usual extra parameters are:
[Okta Implicit Grant](https://developer.okta.com/docs/guides/implement-implicit/overview/) providing ID tokens is supported.
-Use `httpx_auth.OktaImplicitIdToken` to configure this kind of authentication.
+Use `httpx2_auth.OktaImplicitIdToken` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import OktaImplicitIdToken
-
-
-okta = OktaImplicitIdToken(instance='testserver.okta-emea.com', client_id='54239d18-c68c-4c47-8bdd-ce71ea1d50cd')
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=okta)
+import httpx2
+from httpx2_auth import OktaImplicitIdToken
+
+okta = OktaImplicitIdToken(
+ instance="testserver.okta-emea.com",
+ client_id="54239d18-c68c-4c47-8bdd-ce71ea1d50cd",
+)
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=okta)
```
Note:
-* You can persist tokens thanks to [the token cache](#managing-token-cache).
-* You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
+
+- You can persist tokens thanks to [the token cache](#managing-token-cache).
+- You can tweak web browser interaction thanks to [the display settings](#managing-the-web-browser).
###### Parameters
@@ -686,10 +752,10 @@ Note:
| `header_name` | Name of the header field used to send token. | Optional | Authorization |
| `header_value` | Format used to send the token value. "{token}" must be present as it will be replaced by the actual token. | Optional | Bearer {token} |
-Any other parameter will be put as query parameter in the authorization URL.
+Any other parameter will be put as query parameter in the authorization URL.
Usual extra parameters are:
-
+
| Name | Description |
|:----------------|:---------------------------------------------------------------------|
| `prompt` | none to avoid prompting the user if a session is already opened. |
@@ -705,18 +771,19 @@ You need to provide the location of your token cache file. It can be a full or r
If the file already exists it will be used, if the file do not exist it will be created.
```python
-from httpx_auth import OAuth2, JsonTokenFileCache
+from httpx2_auth import OAuth2, JsonTokenFileCache
-OAuth2.token_cache = JsonTokenFileCache('path/to/my_token_cache.json')
+OAuth2.token_cache = JsonTokenFileCache("path/to/my_token_cache.json")
```
### Managing the web browser
#### Authentication response pages
-You can configure the browser display settings thanks to `httpx_auth.OAuth2.display` as in the following:
+You can configure the browser display settings thanks to `httpx2_auth.OAuth2.display` as in the following:
+
```python
-from httpx_auth import OAuth2, DisplaySettings
+from httpx2_auth import OAuth2, DisplaySettings
OAuth2.display = DisplaySettings()
```
@@ -732,7 +799,7 @@ The following parameters can be provided to `DisplaySettings`:
#### Text-mode web browser
-This project uses [`webbrowser.open()`][4] to open a web browser to support authentication flows like OAuth's Authorization Code grant. When running graphically, `webbrowser.open()` does not block. But when run in text mode, `webbrowser.open()` blocks until the opened browser is closed, which leads to a deadlock when httpx-auth cannot serve the auth response pages to the webbrowser. To work around this, you can specify a `BROWSER` environment variable that contains a `%s` and ends with a `&`, and the `webbrowser` module will open the text-mode browser in a subprocess and allow httpx-auth to serve the auth response pages to the browser without deadlocking.
+This project uses [`webbrowser.open()`][4] to open a web browser to support authentication flows like OAuth's Authorization Code grant. When running graphically, `webbrowser.open()` does not block. But when run in text mode, `webbrowser.open()` blocks until the opened browser is closed, which leads to a deadlock when httpx2-auth cannot serve the auth response pages to the webbrowser. To work around this, you can specify a `BROWSER` environment variable that contains a `%s` and ends with a `&`, and the `webbrowser` module will open the text-mode browser in a subprocess and allow httpx2-auth to serve the auth response pages to the browser without deadlocking.
```bash
BROWSER="/usr/bin/links %s &"
@@ -744,31 +811,37 @@ For more information, please see the implementation of [`webbrowser.get()`][5].
Amazon Web Service Signature version 4 is implemented following [Amazon S3 documentation](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html) and [request-aws4auth 1.2.3](https://github.com/sam-washington/requests-aws4auth) (with some changes, see below).
-Use `httpx_auth.AWS4Auth` to configure this kind of authentication.
+Use `httpx2_auth.AWS4Auth` to configure this kind of authentication.
```python
-import httpx
-from httpx_auth import AWS4Auth
-
-aws = AWS4Auth(access_id="my-access-id", secret_key="my-secret-key", region="eu-west-1", service="s3")
-with httpx.Client() as client:
- client.get('http://s3-eu-west-1.amazonaws.com', auth=aws)
+import httpx2
+from httpx2_auth import AWS4Auth
+
+aws = AWS4Auth(
+ access_id="my-access-id",
+ secret_key="my-secret-key",
+ region="eu-west-1",
+ service="s3",
+)
+with httpx2.Client() as client:
+ client.get("http://s3-eu-west-1.amazonaws.com", auth=aws)
```
Note that the following changes were made compared to `requests-aws4auth`:
- - Each request now has its own signing key and `x-amz-date`. Meaning **you can use the same auth instance for more than one request**.
- - `session_token` was renamed into `security_token` for consistency with the underlying name at Amazon.
- - `include_hdrs` parameter was renamed into `include_headers`. When using this parameter:
- - Provided values will not be stripped, [WYSIWYG](https://en.wikipedia.org/wiki/WYSIWYG).
- - If multiple values are provided for a same header, the computation will be based on the value order you provided and value separated by `, `. Instead of ordered values separated by comma for `requests-aws4auth`.
- - `amz_date` attribute has been removed.
- - It is not possible to provide a `date`. It will default to now.
- - It is not possible to provide an `AWSSigningKey` instance, use explicit parameters instead.
- - It is not possible to provide `raise_invalid_date` parameter anymore as the date will always be valid.
- - `host` is not considered as a specific Amazon service anymore (no test specific code).
- - Canonical query string computation is entirely based on AWS documentation (and consider undocumented fragment (`#` and following characters) as part of the query string).
- - Canonical uri computation is entirely based on AWS documentation.
- - Canonical headers computation is entirely based on AWS documentation.
+
+- Each request now has its own signing key and `x-amz-date`. Meaning **you can use the same auth instance for more than one request**.
+- `session_token` was renamed into `security_token` for consistency with the underlying name at Amazon.
+- `include_hdrs` parameter was renamed into `include_headers`. When using this parameter:
+ - Provided values will not be stripped, [WYSIWYG](https://en.wikipedia.org/wiki/WYSIWYG).
+ - If multiple values are provided for a same header, the computation will be based on the value order you provided and value separated by `,`. Instead of ordered values separated by comma for `requests-aws4auth`.
+- `amz_date` attribute has been removed.
+- It is not possible to provide a `date`. It will default to now.
+- It is not possible to provide an `AWSSigningKey` instance, use explicit parameters instead.
+- It is not possible to provide `raise_invalid_date` parameter anymore as the date will always be valid.
+- `host` is not considered as a specific Amazon service anymore (no test specific code).
+- Canonical query string computation is entirely based on AWS documentation (and consider undocumented fragment (`#` and following characters) as part of the query string).
+- Canonical uri computation is entirely based on AWS documentation.
+- Canonical headers computation is entirely based on AWS documentation.
### Parameters
@@ -783,17 +856,25 @@ Note that the following changes were made compared to `requests-aws4auth`:
### Dynamically retrieving credentials using boto3
-While `httpx-auth` does not want to include support for `botocore`, the following authentication class should allow you to automatically retrieve up-to-date credentials.
+While `httpx2-auth` does not want to include support for `botocore`, the following authentication class should allow you to automatically retrieve up-to-date credentials.
```python
-import httpx
+import httpx2
from botocore.session import Session
-from httpx_auth import AWS4Auth
+from httpx2_auth import AWS4Auth
+
class AWS4BotoAuth(AWS4Auth):
def __init__(self, region: str, service: str = "s3", **kwargs):
self.refreshable_credentials = Session().get_credentials()
- AWS4Auth.__init__(self, access_id=kwargs.pop("access_id", "_"), secret_key=kwargs.pop("secret_key", "_"), region=region, service=service, **kwargs)
+ AWS4Auth.__init__(
+ self,
+ access_id=kwargs.pop("access_id", "_"),
+ secret_key=kwargs.pop("secret_key", "_"),
+ region=region,
+ service=service,
+ **kwargs
+ )
def auth_flow(self, request):
self.refresh_credentials()
@@ -807,20 +888,20 @@ class AWS4BotoAuth(AWS4Auth):
aws = AWS4BotoAuth(region="eu-west-1")
-with httpx.Client() as client:
- client.get('http://s3-eu-west-1.amazonaws.com', auth=aws)
+with httpx2.Client() as client:
+ client.get("http://s3-eu-west-1.amazonaws.com", auth=aws)
```
## API key in header
-You can send an API key inside the header of your request using `httpx_auth.HeaderApiKey`.
+You can send an API key inside the header of your request using `httpx2_auth.HeaderApiKey`.
```python
-import httpx
-from httpx_auth import HeaderApiKey
+import httpx2
+from httpx2_auth import HeaderApiKey
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=HeaderApiKey('my_api_key'))
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=HeaderApiKey("my_api_key"))
```
### Parameters
@@ -832,14 +913,14 @@ with httpx.Client() as client:
## API key in query
-You can send an API key inside the query parameters of your request using `httpx_auth.QueryApiKey`.
+You can send an API key inside the query parameters of your request using `httpx2_auth.QueryApiKey`.
```python
-import httpx
-from httpx_auth import QueryApiKey
+import httpx2
+from httpx2_auth import QueryApiKey
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=QueryApiKey('my_api_key'))
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=QueryApiKey("my_api_key"))
```
### Parameters
@@ -851,16 +932,16 @@ with httpx.Client() as client:
## Basic
-You can use basic authentication using `httpx_auth.Basic`.
+You can use basic authentication using `httpx2_auth.Basic`.
-The only advantage of using this class instead of `httpx` native support of basic authentication, is to be able to use it in [multiple authentication](#multiple-authentication-at-once).
+The only advantage of using this class instead of `httpx2` native support of basic authentication, is to be able to use it in [multiple authentication](#multiple-authentication-at-once).
```python
-import httpx
-from httpx_auth import Basic
+import httpx2
+from httpx2_auth import Basic
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=Basic('username', 'password'))
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=Basic("username", "password"))
```
### Parameters
@@ -875,35 +956,37 @@ with httpx.Client() as client:
You can also use a combination of authentication using `+`or `&` as in the following sample:
```python
-import httpx
-from httpx_auth import HeaderApiKey, OAuth2Implicit
+import httpx2
+from httpx2_auth import HeaderApiKey, OAuth2Implicit
-api_key = HeaderApiKey('my_api_key')
-oauth2 = OAuth2Implicit('https://www.example.com')
-with httpx.Client() as client:
- client.get('https://www.example.com', auth=api_key + oauth2)
+api_key = HeaderApiKey("my_api_key")
+oauth2 = OAuth2Implicit("https://www.example.com")
+with httpx2.Client() as client:
+ client.get("https://www.example.com", auth=api_key + oauth2)
```
-This is supported on every authentication class exposed by `httpx_auth`, but you can also enable it on your own authentication classes by using `httpx_auth.SupportMultiAuth` as in the following sample:
+This is supported on every authentication class exposed by `httpx2_auth`, but you can also enable it on your own authentication classes by using `httpx2_auth.SupportMultiAuth` as in the following sample:
```python
-from httpx_auth import SupportMultiAuth
+from httpx2_auth import SupportMultiAuth
+
# TODO Import your own auth here
from my_package import MyAuth
+
class MyMultiAuth(MyAuth, SupportMultiAuth):
pass
```
-
## Available pytest fixtures
-Testing the code using `httpx_auth` authentication classes can be achieved using provided [`pytest`][6] fixtures.
+Testing the code using `httpx2_auth` authentication classes can be achieved using provided [`pytest`][6] fixtures.
### token_cache_mock
```python
-from httpx_auth.testing import token_cache_mock, token_mock
+from httpx2_auth.testing import token_cache_mock, token_mock
+
def test_something(token_cache_mock):
# perform code using authentication
@@ -911,20 +994,21 @@ def test_something(token_cache_mock):
```
Use this fixture to mock authentication success for any of the following classes:
- * `OAuth2AuthorizationCodePKCE`
- * `OktaAuthorizationCodePKCE`
- * `OAuth2Implicit`
- * `OktaImplicit`
- * `OktaImplicitIdToken`
- * `AzureActiveDirectoryImplicit`
- * `AzureActiveDirectoryImplicitIdToken`
- * `OAuth2AuthorizationCode`
- * `OktaAuthorizationCode`
- * `WakaTimeAuthorizationCode`
- * `OAuth2ClientCredentials`
- * `OktaClientCredentials`
- * `OAuth2ResourceOwnerPasswordCredentials`
- * `OktaResourceOwnerPasswordCredentials`
+
+- `OAuth2AuthorizationCodePKCE`
+- `OktaAuthorizationCodePKCE`
+- `OAuth2Implicit`
+- `OktaImplicit`
+- `OktaImplicitIdToken`
+- `AzureActiveDirectoryImplicit`
+- `AzureActiveDirectoryImplicitIdToken`
+- `OAuth2AuthorizationCode`
+- `OktaAuthorizationCode`
+- `WakaTimeAuthorizationCode`
+- `OAuth2ClientCredentials`
+- `OktaClientCredentials`
+- `OAuth2ResourceOwnerPasswordCredentials`
+- `OktaResourceOwnerPasswordCredentials`
By default, an access token with value `2YotnFZFEjr1zCsicMWpAA` is generated.
@@ -933,7 +1017,7 @@ You can however return your custom token by providing your own `token_mock` fixt
```python
import pytest
-from httpx_auth.testing import token_cache_mock
+from httpx2_auth.testing import token_cache_mock
@pytest.fixture
@@ -952,7 +1036,7 @@ Note that [`pyjwt`](https://pypi.org/project/PyJWT/) is a required dependency in
```python
import pytest
-from httpx_auth.testing import token_cache_mock, create_token
+from httpx2_auth.testing import token_cache_mock, create_token
@pytest.fixture
@@ -973,7 +1057,8 @@ def test_something(token_cache_mock):
This [`pytest`][6] fixture will return the token cache and ensure it is reset at the end of the test case.
```python
-from httpx_auth.testing import token_cache
+from httpx2_auth.testing import token_cache
+
def test_something(token_cache):
# perform code using authentication
@@ -991,10 +1076,13 @@ With this [`pytest`][6] fixture you will be allowed to fine tune your authentica
```python
import datetime
-from httpx_auth.testing import browser_mock, BrowserMock, create_token
+from httpx2_auth.testing import browser_mock, BrowserMock, create_token
+
def test_something(browser_mock: BrowserMock):
- token_expiry = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=1)
+ token_expiry = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
+ hours=1
+ )
token = create_token(token_expiry)
tab = browser_mock.add_response(
opened_url="http://url_opened_by_browser?state=1234",
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index c419263..0000000
--- a/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-theme: jekyll-theme-cayman
\ No newline at end of file
diff --git a/httpx_auth/__init__.py b/httpx2_auth/__init__.py
similarity index 59%
rename from httpx_auth/__init__.py
rename to httpx2_auth/__init__.py
index b948ba1..8af5b38 100644
--- a/httpx_auth/__init__.py
+++ b/httpx2_auth/__init__.py
@@ -1,80 +1,88 @@
-from httpx_auth._authentication import (
+from httpx2_auth._authentication import (
Basic,
HeaderApiKey,
QueryApiKey,
SupportMultiAuth,
)
-from httpx_auth._oauth2.browser import DisplaySettings
-from httpx_auth._oauth2.common import OAuth2
-from httpx_auth._oauth2.authorization_code import (
+from httpx2_auth._aws import AWS4Auth
+from httpx2_auth._errors import (
+ AuthenticationFailed,
+ GrantNotProvided,
+ HttpxAuthException,
+ InvalidGrantRequest,
+ InvalidToken,
+ StateNotProvided,
+ TimeoutOccurred,
+ TokenExpiryNotProvided,
+)
+from httpx2_auth._oauth2.authorization_code import (
OAuth2AuthorizationCode,
OktaAuthorizationCode,
WakaTimeAuthorizationCode,
)
-from httpx_auth._oauth2.authorization_code_pkce import (
+from httpx2_auth._oauth2.authorization_code_pkce import (
OAuth2AuthorizationCodePKCE,
OktaAuthorizationCodePKCE,
)
-from httpx_auth._oauth2.client_credentials import (
+from httpx2_auth._oauth2.browser import DisplaySettings
+from httpx2_auth._oauth2.client_credentials import (
OAuth2ClientCredentials,
OktaClientCredentials,
)
-from httpx_auth._oauth2.implicit import (
+from httpx2_auth._oauth2.common import OAuth2
+from httpx2_auth._oauth2.implicit import (
+ AzureActiveDirectoryImplicit,
+ AzureActiveDirectoryImplicitIdToken,
OAuth2Implicit,
OktaImplicit,
OktaImplicitIdToken,
- AzureActiveDirectoryImplicit,
- AzureActiveDirectoryImplicitIdToken,
)
-from httpx_auth._oauth2.resource_owner_password import (
+from httpx2_auth._oauth2.resource_owner_password import (
OAuth2ResourceOwnerPasswordCredentials,
OktaResourceOwnerPasswordCredentials,
)
-from httpx_auth._oauth2.tokens import JsonTokenFileCache, TokenMemoryCache
-from httpx_auth._aws import AWS4Auth
-from httpx_auth._errors import (
- GrantNotProvided,
- TimeoutOccurred,
- AuthenticationFailed,
- StateNotProvided,
- InvalidToken,
- TokenExpiryNotProvided,
- InvalidGrantRequest,
- HttpxAuthException,
-)
-from httpx_auth.version import __version__
+from httpx2_auth._oauth2.tokens import JsonTokenFileCache, TokenMemoryCache
__all__ = [
+ "AWS4Auth",
+ "AuthenticationFailed",
+ "AzureActiveDirectoryImplicit",
+ "AzureActiveDirectoryImplicitIdToken",
"Basic",
+ "DisplaySettings",
+ "GrantNotProvided",
"HeaderApiKey",
- "QueryApiKey",
+ "HttpxAuthException",
+ "InvalidGrantRequest",
+ "InvalidToken",
+ "JsonTokenFileCache",
"OAuth2",
- "DisplaySettings",
+ "OAuth2AuthorizationCode",
"OAuth2AuthorizationCodePKCE",
- "OktaAuthorizationCodePKCE",
+ "OAuth2ClientCredentials",
"OAuth2Implicit",
- "OktaImplicit",
- "OktaImplicitIdToken",
- "AzureActiveDirectoryImplicit",
- "AzureActiveDirectoryImplicitIdToken",
- "OAuth2AuthorizationCode",
+ "OAuth2ResourceOwnerPasswordCredentials",
"OktaAuthorizationCode",
- "OAuth2ClientCredentials",
+ "OktaAuthorizationCodePKCE",
"OktaClientCredentials",
- "OAuth2ResourceOwnerPasswordCredentials",
+ "OktaImplicit",
+ "OktaImplicitIdToken",
"OktaResourceOwnerPasswordCredentials",
- "WakaTimeAuthorizationCode",
+ "QueryApiKey",
+ "StateNotProvided",
"SupportMultiAuth",
- "JsonTokenFileCache",
- "TokenMemoryCache",
- "AWS4Auth",
- "HttpxAuthException",
- "GrantNotProvided",
"TimeoutOccurred",
- "AuthenticationFailed",
- "StateNotProvided",
- "InvalidToken",
"TokenExpiryNotProvided",
- "InvalidGrantRequest",
+ "TokenMemoryCache",
+ "WakaTimeAuthorizationCode",
"__version__",
]
+
+# Version number as Major.Minor.Patch
+# The version modification must respect the following rules:
+# Major should be incremented in case there is a breaking change. (eg: 2.5.8 -> 3.0.0)
+# Minor should be incremented in case there is an enhancement. (eg: 2.5.8 -> 2.6.0)
+# Patch should be incremented in case there is a bug fix. (eg: 2.5.8 -> 2.5.9)
+
+# Started epoch 2 to be able to use distinct version numbers for the first release of httpx2_auth, which is compatible with httpx2.
+__version__ = "2.0.1.0"
diff --git a/httpx_auth/_authentication.py b/httpx2_auth/_authentication.py
similarity index 72%
rename from httpx_auth/_authentication.py
rename to httpx2_auth/_authentication.py
index f251bff..7b0c15a 100644
--- a/httpx_auth/_authentication.py
+++ b/httpx2_auth/_authentication.py
@@ -1,17 +1,17 @@
-from typing import Generator
+from collections.abc import Generator
-import httpx
+import httpx2
-class _MultiAuth(httpx.Auth):
+class _MultiAuth(httpx2.Auth):
"""Authentication using multiple authentication methods."""
def __init__(self, *authentication_modes):
self.authentication_modes = authentication_modes
def auth_flow(
- self, request: httpx.Request
- ) -> Generator[httpx.Request, httpx.Response, None]:
+ self, request: httpx2.Request
+ ) -> Generator[httpx2.Request, httpx2.Response, None]:
for authentication_mode in self.authentication_modes:
next(authentication_mode.auth_flow(request))
yield request
@@ -28,7 +28,7 @@ def __and__(self, other) -> "_MultiAuth":
class SupportMultiAuth:
- """Inherit from this class to be able to use your class with httpx_auth provided authentication classes."""
+ """Inherit from this class to be able to use your class with httpx2_auth provided authentication classes."""
def __add__(self, other) -> _MultiAuth:
if isinstance(other, _MultiAuth):
@@ -41,10 +41,10 @@ def __and__(self, other) -> _MultiAuth:
return _MultiAuth(self, other)
-class HeaderApiKey(httpx.Auth, SupportMultiAuth):
+class HeaderApiKey(httpx2.Auth, SupportMultiAuth):
"""Describes an API Key requests authentication."""
- def __init__(self, api_key: str, header_name: str = None):
+ def __init__(self, api_key: str, header_name: str | None = None):
"""
:param api_key: The API key that will be sent.
:param header_name: Name of the header field. "X-API-Key" by default.
@@ -55,16 +55,16 @@ def __init__(self, api_key: str, header_name: str = None):
self.header_name = header_name or "X-API-Key"
def auth_flow(
- self, request: httpx.Request
- ) -> Generator[httpx.Request, httpx.Response, None]:
+ self, request: httpx2.Request
+ ) -> Generator[httpx2.Request, httpx2.Response, None]:
request.headers[self.header_name] = self.api_key
yield request
-class QueryApiKey(httpx.Auth, SupportMultiAuth):
+class QueryApiKey(httpx2.Auth, SupportMultiAuth):
"""Describes an API Key requests authentication."""
- def __init__(self, api_key: str, query_parameter_name: str = None):
+ def __init__(self, api_key: str, query_parameter_name: str | None = None):
"""
:param api_key: The API key that will be sent.
:param query_parameter_name: Name of the query parameter. "api_key" by default.
@@ -75,16 +75,14 @@ def __init__(self, api_key: str, query_parameter_name: str = None):
self.query_parameter_name = query_parameter_name or "api_key"
def auth_flow(
- self, request: httpx.Request
- ) -> Generator[httpx.Request, httpx.Response, None]:
- request.url = request.url.copy_merge_params(
- {self.query_parameter_name: self.api_key}
- )
+ self, request: httpx2.Request
+ ) -> Generator[httpx2.Request, httpx2.Response, None]:
+ request.url = request.url.copy_merge_params({self.query_parameter_name: self.api_key})
yield request
-class Basic(httpx.BasicAuth, SupportMultiAuth):
+class Basic(httpx2.BasicAuth, SupportMultiAuth):
"""Describes a basic requests authentication."""
def __init__(self, username: str, password: str):
- httpx.BasicAuth.__init__(self, username, password)
+ httpx2.BasicAuth.__init__(self, username, password)
diff --git a/httpx_auth/_aws.py b/httpx2_auth/_aws.py
similarity index 83%
rename from httpx_auth/_aws.py
rename to httpx2_auth/_aws.py
index 826937a..5d2e669 100644
--- a/httpx_auth/_aws.py
+++ b/httpx2_auth/_aws.py
@@ -1,29 +1,29 @@
"""
Provides code for AWSAuth initially ported to httpx from Sam Washington's requests-aws4auth
https://github.com/sam-washington/requests-aws4auth
+
+Updated to use httpx2 and to be compatible with httpx2_auth multi-authentication system.
"""
import datetime
import hashlib
import hmac
from collections import defaultdict
+from collections.abc import Generator
from posixpath import normpath
-from typing import Generator
from urllib.parse import quote
-import httpx
+import httpx2
-class AWS4Auth(httpx.Auth):
+class AWS4Auth(httpx2.Auth):
"""
https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html
"""
requires_request_body = True
- def __init__(
- self, access_id: str, secret_key: str, region: str, service: str, **kwargs
- ):
+ def __init__(self, access_id: str, secret_key: str, region: str, service: str, **kwargs):
"""
:param access_id: AWS access ID
@@ -51,13 +51,11 @@ def __init__(
self.security_token = kwargs.get("security_token")
- self.include_headers = {
- header.lower() for header in kwargs.get("include_headers", [])
- }
+ self.include_headers = {header.lower() for header in kwargs.get("include_headers", [])}
def auth_flow(
- self, request: httpx.Request
- ) -> Generator[httpx.Request, httpx.Response, None]:
+ self, request: httpx2.Request
+ ) -> Generator[httpx2.Request, httpx2.Response, None]:
date = datetime.datetime.now(datetime.timezone.utc)
# https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html
@@ -69,9 +67,7 @@ def auth_flow(
# The x-amz-content-sha256 header is required for all AWS Signature Version 4 requests.
# It provides a hash of the request payload.
# If there is no payload, you must provide the hash of an empty string.
- request.headers["x-amz-content-sha256"] = hashlib.sha256(
- request.read()
- ).hexdigest()
+ request.headers["x-amz-content-sha256"] = hashlib.sha256(request.read()).hexdigest()
# https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
# if you are using temporary security credentials, you need to include x-amz-security-token in your request.
@@ -82,9 +78,7 @@ def auth_flow(
canonical_headers, signed_headers = canonical_and_signed_headers(
request.headers, self.include_headers
)
- canonical_request = self._canonical_request(
- request, canonical_headers, signed_headers
- )
+ canonical_request = self._canonical_request(request, canonical_headers, signed_headers)
scope = f"{date.strftime('%Y%m%d')}/{self.region}/{self.service}/aws4_request"
string_to_sign = _string_to_sign(request, canonical_request, scope)
signing_key = _signing_key(
@@ -102,7 +96,7 @@ def auth_flow(
yield request
def _canonical_request(
- self, request: httpx.Request, canonical_headers: str, signed_headers: str
+ self, request: httpx2.Request, canonical_headers: str, signed_headers: str
) -> str:
return "\n".join(
[
@@ -118,7 +112,7 @@ def _canonical_request(
def canonical_and_signed_headers(
- headers: httpx.Headers, include_headers: set[str]
+ headers: httpx2.Headers, include_headers: set[str]
) -> tuple[str, str]:
r"""
See https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html for more details.
@@ -133,7 +127,7 @@ def canonical_and_signed_headers(
...
Lowercase()+":"+Trim()+"\n"
- >>> canonical_and_signed_headers(httpx.Headers({"X-AMZ-Whatever": " value with spaces "}), include_headers=set())
+ >>> canonical_and_signed_headers(httpx2.Headers({"X-AMZ-Whatever": " value with spaces "}), include_headers=set())
('x-amz-whatever:value with spaces\n', 'x-amz-whatever')
The Lowercase() and Trim() functions used in this example are described in the preceding section.
@@ -167,7 +161,7 @@ def canonical_and_signed_headers(
For example, for the previous example, the value of SignedHeaders would be as follows:
host;x-amz-content-sha256;x-amz-date
- >>> canonical_and_signed_headers(httpx.Headers({"Host": "s3.amazonaws.com", "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "x-amz-date": "20130708T220855Z"}), include_headers={"host"})
+ >>> canonical_and_signed_headers(httpx2.Headers({"Host": "s3.amazonaws.com", "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "x-amz-date": "20130708T220855Z"}), include_headers={"host"})
('host:s3.amazonaws.com\nx-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\nx-amz-date:20130708T220855Z\n', 'host;x-amz-content-sha256;x-amz-date')
"""
include_headers.add("host")
@@ -177,7 +171,7 @@ def canonical_and_signed_headers(
if (header or "*") in include_headers or (
header.startswith("x-amz-")
# x-amz-client-context break mobile analytics auth if included
- and not header == "x-amz-client-context"
+ and header != "x-amz-client-context"
):
included_headers[header] = header_value.strip()
@@ -190,14 +184,12 @@ def canonical_and_signed_headers(
return canonical_headers, ";".join(signed_headers)
-def _string_to_sign(request: httpx.Request, canonical_request: str, scope: str) -> str:
+def _string_to_sign(request: httpx2.Request, canonical_request: str, scope: str) -> str:
hsh = hashlib.sha256(canonical_request.encode())
- return "\n".join(
- ["AWS4-HMAC-SHA256", request.headers["x-amz-date"], scope, hsh.hexdigest()]
- )
+ return "\n".join(["AWS4-HMAC-SHA256", request.headers["x-amz-date"], scope, hsh.hexdigest()])
-def canonical_uri(url: httpx.URL, is_s3: bool) -> str:
+def canonical_uri(url: httpx2.URL, is_s3: bool) -> str:
"""
See https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html for more details.
@@ -209,7 +201,7 @@ def canonical_uri(url: httpx.URL, is_s3: bool) -> str:
The URI in the following example, /examplebucket/myphoto.jpg, is the absolute path, and you don't encode the "/" in the absolute path:
http://s3.amazonaws.com/examplebucket/myphoto.jpg
- >>> canonical_uri(httpx.URL("http://s3.amazonaws.com/examplebucket/myphoto.jpg"), is_s3=False)
+ >>> canonical_uri(httpx2.URL("http://s3.amazonaws.com/examplebucket/myphoto.jpg"), is_s3=False)
'/examplebucket/myphoto.jpg'
Note
@@ -217,18 +209,18 @@ def canonical_uri(url: httpx.URL, is_s3: bool) -> str:
For example, you may have a bucket with an object named "my-object//example//photo.user".
Normalizing the path changes the object name in the request to "my-object/example/photo.user".
This is an incorrect path for that object.
- >>> canonical_uri(httpx.URL("http://s3.amazonaws.com/my-object//example//photo.user"), is_s3=False)
+ >>> canonical_uri(httpx2.URL("http://s3.amazonaws.com/my-object//example//photo.user"), is_s3=False)
'/my-object/example/photo.user'
- >>> canonical_uri(httpx.URL("http://s3.amazonaws.com/my-object//example//photo.user"), is_s3=True)
+ >>> canonical_uri(httpx2.URL("http://s3.amazonaws.com/my-object//example//photo.user"), is_s3=True)
'/my-object//example//photo.user'
Some limitation that should be covered but not documented by AWS:
- Trailing / should be kept
- >>> canonical_uri(httpx.URL("http://s3.amazonaws.com/resource/"), is_s3=False)
+ >>> canonical_uri(httpx2.URL("http://s3.amazonaws.com/resource/"), is_s3=False)
'/resource/'
- Starting with // should be normalized
- >>> canonical_uri(httpx.URL("http://s3.amazonaws.com//resource/"), is_s3=False)
+ >>> canonical_uri(httpx2.URL("http://s3.amazonaws.com//resource/"), is_s3=False)
'/resource/'
"""
resource = url.path
@@ -244,7 +236,7 @@ def canonical_uri(url: httpx.URL, is_s3: bool) -> str:
return uri_encode(resource, is_key=True)
-def canonical_query_string(url: httpx.URL) -> str:
+def canonical_query_string(url: httpx2.URL) -> str:
"""
See https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html for more details.
@@ -261,7 +253,7 @@ def canonical_query_string(url: httpx.URL) -> str:
UriEncode("marker")+"="+UriEncode("someMarker")+"&"+
UriEncode("max-keys")+"="+UriEncode("20") + "&" +
UriEncode("prefix")+"="+UriEncode("somePrefix")
- >>> canonical_query_string(httpx.URL("http://s3.amazonaws.com/examplebucket?prefix=somePrefix&marker=someMarker&max-keys=20"))
+ >>> canonical_query_string(httpx2.URL("http://s3.amazonaws.com/examplebucket?prefix=somePrefix&marker=someMarker&max-keys=20"))
'marker=someMarker&max-keys=20&prefix=somePrefix'
When a request targets a subresource, the corresponding query parameter value will be an empty string ("").
@@ -272,11 +264,11 @@ def canonical_query_string(url: httpx.URL) -> str:
The CanonicalQueryString in this case is as follows:
UriEncode("acl") + "=" + ""
- >>> canonical_query_string(httpx.URL("http://s3.amazonaws.com/examplebucket?acl"))
+ >>> canonical_query_string(httpx2.URL("http://s3.amazonaws.com/examplebucket?acl"))
'acl='
If the URI does not include a '?', there is no query string in the request, and you set the canonical query string to an empty string ("").
- >>> canonical_query_string(httpx.URL("http://s3.amazonaws.com/examplebucket"))
+ >>> canonical_query_string(httpx2.URL("http://s3.amazonaws.com/examplebucket"))
''
You will still need to include the "\n".
@@ -284,18 +276,18 @@ def canonical_query_string(url: httpx.URL) -> str:
Undocumented:
As URL fragment are not mentionned in AWS documentation, it is assumed they don't treat it as what it is and part of the query string instead
- >>> canonical_query_string(httpx.URL("http://s3.amazonaws.com/examplebucket?#this_will_be_a_parameter=and_its_value"))
+ >>> canonical_query_string(httpx2.URL("http://s3.amazonaws.com/examplebucket?#this_will_be_a_parameter=and_its_value"))
'%23this_will_be_a_parameter=and_its_value'
- >>> canonical_query_string(httpx.URL("http://s3.amazonaws.com/examplebucket?#first=1#invalue"))
+ >>> canonical_query_string(httpx2.URL("http://s3.amazonaws.com/examplebucket?#first=1#invalue"))
'%23first=1%23invalue'
- >>> canonical_query_string(httpx.URL("http://s3.amazonaws.com/examplebucket?first#=1second=invalue"))
+ >>> canonical_query_string(httpx2.URL("http://s3.amazonaws.com/examplebucket?first#=1second=invalue"))
'%23second=invalue&first%23=1'
"""
if fragment := url.fragment:
url_without_fragment = url.copy_with(fragment=None)
- return canonical_query_string(httpx.URL(f"{url_without_fragment}%23{fragment}"))
+ return canonical_query_string(httpx2.URL(f"{url_without_fragment}%23{fragment}"))
encoded_params = defaultdict(list)
for name, value in url.params.multi_items():
@@ -310,7 +302,7 @@ def canonical_query_string(url: httpx.URL) -> str:
def _signing_key(secret_key: str, region: str, service: str, date: str) -> bytes:
- init_key = f"AWS4{secret_key}".encode("utf-8")
+ init_key = f"AWS4{secret_key}".encode()
date_key = sign_sha256(init_key, date)
region_key = sign_sha256(date_key, region)
service_key = sign_sha256(region_key, service)
@@ -322,7 +314,7 @@ def sign_sha256(signing_key: bytes, message: str) -> bytes:
def uri_encode(value: str, is_key: bool = False) -> str:
- """
+ r"""
See https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html for more details.
URI encode every byte. UriEncode() must enforce the following rules:
diff --git a/httpx_auth/_errors.py b/httpx2_auth/_errors.py
similarity index 92%
rename from httpx_auth/_errors.py
rename to httpx2_auth/_errors.py
index 078fe41..724ec60 100644
--- a/httpx_auth/_errors.py
+++ b/httpx2_auth/_errors.py
@@ -1,10 +1,10 @@
from json import JSONDecodeError
-from typing import Union
+from typing import ClassVar
-import httpx
+import httpx2
-class HttpxAuthException(httpx.HTTPError): ...
+class HttpxAuthException(httpx2.HTTPError): ...
class AuthenticationFailed(HttpxAuthException):
@@ -45,7 +45,7 @@ class InvalidGrantRequest(HttpxAuthException):
"""
# https://tools.ietf.org/html/rfc6749#section-5.2
- request_errors = {
+ request_errors: ClassVar[dict[str, str]] = {
"invalid_request": "The request is missing a required parameter, includes an unsupported parameter value (other than grant type), repeats a parameter, includes multiple credentials, utilizes more than one mechanism for authenticating the client, or is otherwise malformed.",
"invalid_client": 'Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported. If the client attempted to authenticate via the "Authorization" request header field, the authorization server MUST respond with an HTTP 401 (Unauthorized) status code and include the "WWW-Authenticate" response header field matching the authentication scheme used by the client.',
"invalid_grant": "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.",
@@ -56,7 +56,7 @@ class InvalidGrantRequest(HttpxAuthException):
# https://tools.ietf.org/html/rfc6749#section-4.2.2.1
# https://tools.ietf.org/html/rfc6749#section-4.1.2.1
- browser_errors = {
+ browser_errors: ClassVar[dict[str, str]] = {
"invalid_request": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.",
"unauthorized_client": "The client is not authorized to request an authorization code or an access token using this method.",
"access_denied": "The resource owner or authorization server denied the request.",
@@ -66,11 +66,11 @@ class InvalidGrantRequest(HttpxAuthException):
"temporarily_unavailable": "The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server. (This error code is needed because a 503 Service Unavailable HTTP status code cannot be returned to the client via an HTTP redirect.)",
}
- def __init__(self, response: Union[httpx.Response, dict]):
+ def __init__(self, response: httpx2.Response | dict):
HttpxAuthException.__init__(self, InvalidGrantRequest.to_message(response))
@staticmethod
- def to_message(response: Union[httpx.Response, dict]) -> str:
+ def to_message(response: httpx2.Response | dict) -> str:
"""
Handle response as described in:
* https://tools.ietf.org/html/rfc6749#section-5.2
@@ -121,15 +121,11 @@ class StateNotProvided(HttpxAuthException):
"""State was not provided."""
def __init__(self, dictionary_without_state: dict):
- HttpxAuthException.__init__(
- self, f"state not provided within {dictionary_without_state}."
- )
+ HttpxAuthException.__init__(self, f"state not provided within {dictionary_without_state}.")
class TokenExpiryNotProvided(HttpxAuthException):
"""Token expiry was not provided."""
def __init__(self, token_body: dict):
- HttpxAuthException.__init__(
- self, f"Expiry (exp) is not provided in {token_body}."
- )
+ HttpxAuthException.__init__(self, f"Expiry (exp) is not provided in {token_body}.")
diff --git a/httpx_auth/_oauth2/__init__.py b/httpx2_auth/_oauth2/__init__.py
similarity index 100%
rename from httpx_auth/_oauth2/__init__.py
rename to httpx2_auth/_oauth2/__init__.py
diff --git a/httpx_auth/_oauth2/authentication_responses_server.py b/httpx2_auth/_oauth2/authentication_responses_server.py
similarity index 82%
rename from httpx_auth/_oauth2/authentication_responses_server.py
rename to httpx2_auth/_oauth2/authentication_responses_server.py
index fef2c80..2e87fa9 100644
--- a/httpx_auth/_oauth2/authentication_responses_server.py
+++ b/httpx2_auth/_oauth2/authentication_responses_server.py
@@ -1,37 +1,35 @@
-import webbrowser
import logging
-from http.server import HTTPServer, BaseHTTPRequestHandler
+import webbrowser
+from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qs, urlparse
-from socket import socket
-import httpx
+import httpx2
-from httpx_auth._errors import (
- InvalidGrantRequest,
+from httpx2_auth._errors import (
GrantNotProvided,
+ InvalidGrantRequest,
StateNotProvided,
TimeoutOccurred,
)
-from httpx_auth._oauth2.common import OAuth2
+from httpx2_auth._oauth2.common import OAuth2
logger = logging.getLogger(__name__)
class OAuth2ResponseHandler(BaseHTTPRequestHandler):
+ # The server is always a FixedHttpServer (set when the server instantiates the handler).
+ server: "FixedHttpServer"
+
def do_GET(self) -> None:
# Do not consider a favicon request as an error
if self.path == "/favicon.ico":
- logger.debug(
- "Favicon request received on OAuth2 authentication response server."
- )
+ logger.debug("Favicon request received on OAuth2 authentication response server.")
return self.send_html("Favicon is not provided.")
- logger.debug(f"GET received on {self.path}")
+ logger.debug("GET received on %s", self.path)
try:
args = self._get_params()
- if self.server.grant_details.name in args or args.pop(
- "httpx_auth_redirect", None
- ):
+ if self.server.grant_details.name in args or args.pop("httpx2_auth_redirect", None):
self._parse_grant(args)
else:
logger.debug("Send anchor grant as query parameter.")
@@ -47,7 +45,7 @@ def do_GET(self) -> None:
)
def do_POST(self) -> None:
- logger.debug(f"POST received on {self.path}")
+ logger.debug("POST received on %s", self.path)
try:
form_dict = self._get_form()
self._parse_grant(form_dict)
@@ -72,19 +70,17 @@ def _parse_grant(self, arguments: dict) -> None:
if "error" in arguments:
raise InvalidGrantRequest(arguments)
raise GrantNotProvided(self.server.grant_details.name, arguments)
- logger.debug(f"Received grants: {grants}")
+ logger.debug("Received grants: %s", grants)
grant = grants[0]
states = arguments.get("state")
if not states or len(states) > 1:
raise StateNotProvided(arguments)
- logger.debug(f"Received states: {states}")
+ logger.debug("Received states: %s", states)
state = states[0]
self.server.grant = state, grant
self.send_html(
- OAuth2.display.success_html.format(
- display_time=OAuth2.display.success_display_time
- )
+ OAuth2.display.success_html.format(display_time=OAuth2.display.success_display_time)
)
def _get_form(self) -> dict:
@@ -114,9 +110,9 @@ def fragment_redirect_page(self) -> str:
return """"""
@@ -142,16 +138,14 @@ def __init__(
class FixedHttpServer(HTTPServer):
def __init__(self, grant_details: GrantDetails):
- HTTPServer.__init__(
- self, ("", grant_details.redirect_uri_port), OAuth2ResponseHandler
- )
+ HTTPServer.__init__(self, ("", grant_details.redirect_uri_port), OAuth2ResponseHandler)
self.timeout = grant_details.reception_timeout
- logger.debug(f"Timeout is set to {self.timeout} seconds.")
+ logger.debug("Timeout is set to %s seconds.", self.timeout)
self.grant_details = grant_details
- self.request_error = None
- self.grant = False
+ self.request_error: Exception | None = None
+ self.grant: tuple[str, str] | None = None
- def finish_request(self, request: socket, client_address) -> None:
+ def finish_request(self, request, client_address) -> None:
"""Make sure that timeout is used by the request (seems like a bug in HTTPServer)."""
request.settimeout(self.timeout)
HTTPServer.finish_request(self, request, client_address)
@@ -163,7 +157,7 @@ def ensure_no_error_occurred(self) -> bool:
return not self.grant
def handle_timeout(self) -> None:
- raise TimeoutOccurred(self.timeout)
+ raise TimeoutOccurred(self.grant_details.reception_timeout)
def request_new_grant(grant_details: GrantDetails) -> tuple[str, str]:
@@ -175,7 +169,7 @@ def request_new_grant(grant_details: GrantDetails) -> tuple[str, str]:
:raises GrantNotProvided: If grant is not provided in response (but no error occurred).
:raises StateNotProvided: If state is not provided in addition to the grant.
"""
- logger.debug(f"Requesting new {grant_details.name}...")
+ logger.debug("Requesting new %s...", grant_details.name)
with FixedHttpServer(grant_details) as server:
_open_url(grant_details.url)
@@ -192,16 +186,16 @@ def _open_url(url: str) -> None:
if hasattr(webbrowser, "iexplore")
else webbrowser.get()
)
- logger.debug(f"Opening browser on {url}")
+ logger.debug("Opening browser on %s", url)
if not browser.open(url, new=1):
logger.warning("Unable to open URL, try with a GET request.")
- httpx.get(url)
+ httpx2.get(url)
except webbrowser.Error:
logger.exception("Unable to open URL, try with a GET request.")
- httpx.get(url)
+ httpx2.get(url)
-def _wait_for_grant(server: FixedHttpServer) -> (str, str):
+def _wait_for_grant(server: FixedHttpServer) -> tuple[str, str]:
"""
:return: A tuple (state, grant)
:raises InvalidGrantRequest: If the request was invalid.
@@ -210,7 +204,7 @@ def _wait_for_grant(server: FixedHttpServer) -> (str, str):
:raises StateNotProvided: If state is not provided in addition to the grant.
"""
logger.debug("Waiting for user authentication...")
- while not server.grant:
+ while server.grant is None:
server.handle_request()
server.ensure_no_error_occurred()
return server.grant
diff --git a/httpx_auth/_oauth2/authorization_code.py b/httpx2_auth/_oauth2/authorization_code.py
similarity index 89%
rename from httpx_auth/_oauth2/authorization_code.py
rename to httpx2_auth/_oauth2/authorization_code.py
index 334a44c..831d48e 100644
--- a/httpx_auth/_oauth2/authorization_code.py
+++ b/httpx2_auth/_oauth2/authorization_code.py
@@ -1,17 +1,17 @@
+from collections.abc import Iterable
from hashlib import sha512
-from typing import Iterable, Union
-import httpx
+import httpx2
-from httpx_auth._authentication import SupportMultiAuth
-from httpx_auth._oauth2 import authentication_responses_server
-from httpx_auth._oauth2.browser import BrowserAuth
-from httpx_auth._oauth2.common import (
- request_new_grant_with_post,
+from httpx2_auth._authentication import SupportMultiAuth
+from httpx2_auth._oauth2 import authentication_responses_server
+from httpx2_auth._oauth2.browser import BrowserAuth
+from httpx2_auth._oauth2.common import (
OAuth2BaseAuth,
_add_parameters,
- _pop_parameter,
_get_query_parameter,
+ _pop_parameter,
+ request_new_grant_with_post,
)
@@ -52,7 +52,7 @@ def __init__(self, authorization_url: str, token_url: str, **kwargs):
:param code_field_name: Field name containing the code. code by default.
:param username: Username in case basic authentication should be used to retrieve token.
:param password: User password in case basic authentication should be used to retrieve token.
- :param client: httpx.Client instance that will be used to request the token.
+ :param client: httpx2.Client instance that will be used to request the token.
Use it to provide a custom proxying rule for instance.
:param kwargs: all additional authorization parameters that should be put as query parameter
in the authorization URL and as body parameters in the token URL.
@@ -91,24 +91,18 @@ def __init__(self, authorization_url: str, token_url: str, **kwargs):
# As described in https://tools.ietf.org/html/rfc6749#section-4.1.1
kwargs.setdefault("response_type", "code")
- authorization_url_without_nonce = _add_parameters(
- self.authorization_url, kwargs
- )
+ authorization_url_without_nonce = _add_parameters(self.authorization_url, kwargs)
authorization_url_without_nonce, nonce = _pop_parameter(
authorization_url_without_nonce, "nonce"
)
- state = sha512(
- authorization_url_without_nonce.encode("unicode_escape")
- ).hexdigest()
- custom_code_parameters = {
+ state = sha512(authorization_url_without_nonce.encode("unicode_escape")).hexdigest()
+ custom_code_parameters: dict[str, str | list[str]] = {
"state": state,
"redirect_uri": self.redirect_uri,
}
if nonce:
custom_code_parameters["nonce"] = nonce
- code_grant_url = _add_parameters(
- authorization_url_without_nonce, custom_code_parameters
- )
+ code_grant_url = _add_parameters(authorization_url_without_nonce, custom_code_parameters)
self.code_grant_details = authentication_responses_server.GrantDetails(
code_grant_url,
code_field_name,
@@ -138,14 +132,12 @@ def __init__(self, authorization_url: str, token_url: str, **kwargs):
def request_new_token(self) -> tuple:
# Request code
- state, code = authentication_responses_server.request_new_grant(
- self.code_grant_details
- )
+ _state, code = authentication_responses_server.request_new_grant(self.code_grant_details)
# As described in https://tools.ietf.org/html/rfc6749#section-4.1.3
self.token_data["code"] = code
- client = self.client or httpx.Client()
+ client = self.client or httpx2.Client()
self._configure_client(client)
try:
# As described in https://tools.ietf.org/html/rfc6749#section-4.1.4
@@ -157,14 +149,10 @@ def request_new_token(self) -> tuple:
if self.client is None:
client.close()
# Handle both Access and Bearer tokens
- return (
- (self.state, token, expires_in, refresh_token)
- if expires_in
- else (self.state, token)
- )
+ return (self.state, token, expires_in, refresh_token) if expires_in else (self.state, token)
def refresh_token(self, refresh_token: str) -> tuple:
- client = self.client or httpx.Client()
+ client = self.client or httpx2.Client()
self._configure_client(client)
try:
# As described in https://tools.ietf.org/html/rfc6749#section-6
@@ -181,8 +169,10 @@ def refresh_token(self, refresh_token: str) -> tuple:
client.close()
return self.state, token, expires_in, refresh_token
- def _configure_client(self, client: httpx.Client):
- client.auth = self.auth
+ def _configure_client(self, client: httpx2.Client) -> None:
+ # self.auth may be None (no client credentials); httpx2 accepts None at
+ # runtime even though its setter is typed as AuthTypes only.
+ client.auth = self.auth # type: ignore[assignment]
client.timeout = self.timeout
@@ -220,7 +210,7 @@ def __init__(self, instance: str, client_id: str, **kwargs):
:param header_value: Format used to send the token value.
"{token}" must be present as it will be replaced by the actual token.
Token will be sent as "Bearer {token}" by default.
- :param client: httpx.Client instance that will be used to request the token.
+ :param client: httpx2.Client instance that will be used to request the token.
Use it to provide a custom proxying rule for instance.
:param kwargs: all additional authorization parameters that should be put as query parameter
in the authorization URL.
@@ -248,7 +238,7 @@ def __init__(
self,
client_id: str,
client_secret: str,
- scope: Union[str, Iterable[str]],
+ scope: str | Iterable[str],
**kwargs,
):
"""
@@ -276,7 +266,7 @@ def __init__(
:param header_value: Format used to send the token value.
"{token}" must be present as it will be replaced by the actual token.
Token will be sent as "Bearer {token}" by default.
- :param client: httpx.Client instance that will be used to request the token.
+ :param client: httpx2.Client instance that will be used to request the token.
Use it to provide a custom proxying rule for instance.
:param kwargs: all additional authorization parameters that should be put as query parameter
in the authorization URL.
diff --git a/httpx_auth/_oauth2/authorization_code_pkce.py b/httpx2_auth/_oauth2/authorization_code_pkce.py
similarity index 91%
rename from httpx_auth/_oauth2/authorization_code_pkce.py
rename to httpx2_auth/_oauth2/authorization_code_pkce.py
index 314f736..d6f7baf 100644
--- a/httpx_auth/_oauth2/authorization_code_pkce.py
+++ b/httpx2_auth/_oauth2/authorization_code_pkce.py
@@ -2,16 +2,16 @@
import os
from hashlib import sha256, sha512
-import httpx
+import httpx2
-from httpx_auth._authentication import SupportMultiAuth
-from httpx_auth._oauth2 import authentication_responses_server
-from httpx_auth._oauth2.browser import BrowserAuth
-from httpx_auth._oauth2.common import (
- request_new_grant_with_post,
+from httpx2_auth._authentication import SupportMultiAuth
+from httpx2_auth._oauth2 import authentication_responses_server
+from httpx2_auth._oauth2.browser import BrowserAuth
+from httpx2_auth._oauth2.common import (
OAuth2BaseAuth,
_add_parameters,
_pop_parameter,
+ request_new_grant_with_post,
)
@@ -50,7 +50,7 @@ def __init__(self, authorization_url: str, token_url: str, **kwargs):
Default to 30 seconds to ensure token will not expire between the time of retrieval and the time the request
reaches the actual server. Set it to 0 to deactivate this feature and use the same token until actual expiry.
:param code_field_name: Field name containing the code. code by default.
- :param client: httpx.Client instance that will be used to request the token.
+ :param client: httpx2.Client instance that will be used to request the token.
Use it to provide a custom proxying rule for instance.
:param kwargs: all additional authorization parameters that should be put as query parameter
in the authorization URL and as body parameters in the token URL.
@@ -95,10 +95,8 @@ def __init__(self, authorization_url: str, token_url: str, **kwargs):
authorization_url_without_nonce, nonce = _pop_parameter(
authorization_url_without_nonce, "nonce"
)
- state = sha512(
- authorization_url_without_nonce.encode("unicode_escape")
- ).hexdigest()
- custom_code_parameters = {
+ state = sha512(authorization_url_without_nonce.encode("unicode_escape")).hexdigest()
+ custom_code_parameters: dict[str, str | list[str]] = {
"state": state,
"redirect_uri": self.redirect_uri,
}
@@ -110,12 +108,10 @@ def __init__(self, authorization_url: str, token_url: str, **kwargs):
code_challenge = self.generate_code_challenge(code_verifier)
# add code challenge parameters to the authorization_url request
- custom_code_parameters["code_challenge"] = code_challenge
+ custom_code_parameters["code_challenge"] = code_challenge.decode("ascii")
custom_code_parameters["code_challenge_method"] = "S256"
- code_grant_url = _add_parameters(
- authorization_url_without_nonce, custom_code_parameters
- )
+ code_grant_url = _add_parameters(authorization_url_without_nonce, custom_code_parameters)
self.code_grant_details = authentication_responses_server.GrantDetails(
code_grant_url,
code_field_name,
@@ -142,14 +138,12 @@ def __init__(self, authorization_url: str, token_url: str, **kwargs):
def request_new_token(self) -> tuple:
# Request code
- state, code = authentication_responses_server.request_new_grant(
- self.code_grant_details
- )
+ _state, code = authentication_responses_server.request_new_grant(self.code_grant_details)
# As described in https://tools.ietf.org/html/rfc6749#section-4.1.3
self.token_data["code"] = code
- client = self.client or httpx.Client()
+ client = self.client or httpx2.Client()
self._configure_client(client)
try:
# As described in https://tools.ietf.org/html/rfc6749#section-4.1.4
@@ -161,14 +155,10 @@ def request_new_token(self) -> tuple:
if self.client is None:
client.close()
# Handle both Access and Bearer tokens
- return (
- (self.state, token, expires_in, refresh_token)
- if expires_in
- else (self.state, token)
- )
+ return (self.state, token, expires_in, refresh_token) if expires_in else (self.state, token)
def refresh_token(self, refresh_token: str) -> tuple:
- client = self.client or httpx.Client()
+ client = self.client or httpx2.Client()
self._configure_client(client)
try:
# As described in https://tools.ietf.org/html/rfc6749#section-6
@@ -185,7 +175,7 @@ def refresh_token(self, refresh_token: str) -> tuple:
client.close()
return self.state, token, expires_in, refresh_token
- def _configure_client(self, client: httpx.Client):
+ def _configure_client(self, client: httpx2.Client):
client.timeout = self.timeout
@staticmethod
@@ -256,7 +246,7 @@ def __init__(self, instance: str, client_id: str, **kwargs):
:param header_value: Format used to send the token value.
"{token}" must be present as it will be replaced by the actual token.
Token will be sent as "Bearer {token}" by default.
- :param client: httpx.Client instance that will be used to request the token.
+ :param client: httpx2.Client instance that will be used to request the token.
Use it to provide a custom proxying rule for instance.
:param kwargs: all additional authorization parameters that should be put as query parameter
in the authorization URL and as body parameters in the token URL.
diff --git a/httpx_auth/_oauth2/browser.py b/httpx2_auth/_oauth2/browser.py
similarity index 88%
rename from httpx_auth/_oauth2/browser.py
rename to httpx2_auth/_oauth2/browser.py
index 4c244a6..3dded8e 100644
--- a/httpx_auth/_oauth2/browser.py
+++ b/httpx2_auth/_oauth2/browser.py
@@ -12,7 +12,9 @@ def __init__(self, kwargs):
redirect_uri_domain = kwargs.pop("redirect_uri_domain", None) or "localhost"
redirect_uri_endpoint = kwargs.pop("redirect_uri_endpoint", None) or ""
self.redirect_uri_port = int(kwargs.pop("redirect_uri_port", None) or 5000)
- self.redirect_uri = f"http://{redirect_uri_domain}:{self.redirect_uri_port}/{redirect_uri_endpoint}"
+ self.redirect_uri = (
+ f"http://{redirect_uri_domain}:{self.redirect_uri_port}/{redirect_uri_endpoint}"
+ )
# Time is expressed in seconds
self.timeout = float(kwargs.pop("timeout", None) or 60)
@@ -92,8 +94,8 @@ class DisplaySettings:
{information}