├── .bandit.yml ├── .env ├── .flake8 ├── .github └── workflows │ ├── publish.yml │ └── tests.yml ├── .gitignore ├── .readthedocs.yml ├── LICENSE ├── README.md ├── docs ├── Makefile ├── _config.yml ├── conf.py └── index.rst ├── example-docs.png ├── example ├── Dockerfile ├── README.md ├── app │ ├── __init__.py │ ├── config.py │ └── main.py ├── docker-compose.yml ├── poetry.lock └── pyproject.toml ├── fastapi_third_party_auth ├── __init__.py ├── auth.py ├── discovery.py ├── grant_types.py └── idtoken_types.py ├── poetry.lock ├── pyproject.toml └── tests ├── __init__.py ├── conftest.py ├── fixtures └── AuthServerDiscovery.json ├── test_auth.py └── test_types.py /.bandit.yml: -------------------------------------------------------------------------------- 1 | ### This config may optionally select a subset of tests to run or skip by 2 | ### filling out the 'tests' and 'skips' lists given below. If no tests are 3 | ### specified for inclusion then it is assumed all tests are desired. The skips 4 | ### set will remove specific tests from the include set. This can be controlled 5 | ### using the -t/-s CLI options. Note that the same test ID should not appear 6 | ### in both 'tests' and 'skips', this would be nonsensical and is detected by 7 | ### Bandit at runtime. 8 | 9 | # Available tests: 10 | # B101 : assert_used 11 | # B102 : exec_used 12 | # B103 : set_bad_file_permissions 13 | # B104 : hardcoded_bind_all_interfaces 14 | # B105 : hardcoded_password_string 15 | # B106 : hardcoded_password_funcarg 16 | # B107 : hardcoded_password_default 17 | # B108 : hardcoded_tmp_directory 18 | # B110 : try_except_pass 19 | # B112 : try_except_continue 20 | # B201 : flask_debug_true 21 | # B301 : pickle 22 | # B302 : marshal 23 | # B303 : md5 24 | # B304 : ciphers 25 | # B305 : cipher_modes 26 | # B306 : mktemp_q 27 | # B307 : eval 28 | # B308 : mark_safe 29 | # B309 : httpsconnection 30 | # B310 : urllib_urlopen 31 | # B311 : random 32 | # B312 : telnetlib 33 | # B313 : xml_bad_cElementTree 34 | # B314 : xml_bad_ElementTree 35 | # B315 : xml_bad_expatreader 36 | # B316 : xml_bad_expatbuilder 37 | # B317 : xml_bad_sax 38 | # B318 : xml_bad_minidom 39 | # B319 : xml_bad_pulldom 40 | # B320 : xml_bad_etree 41 | # B321 : ftplib 42 | # B322 : input 43 | # B323 : unverified_context 44 | # B324 : hashlib_new_insecure_functions 45 | # B325 : tempnam 46 | # B401 : import_telnetlib 47 | # B402 : import_ftplib 48 | # B403 : import_pickle 49 | # B404 : import_subprocess 50 | # B405 : import_xml_etree 51 | # B406 : import_xml_sax 52 | # B407 : import_xml_expat 53 | # B408 : import_xml_minidom 54 | # B409 : import_xml_pulldom 55 | # B410 : import_lxml 56 | # B411 : import_xmlrpclib 57 | # B412 : import_httpoxy 58 | # B413 : import_pycrypto 59 | # B501 : request_with_no_cert_validation 60 | # B502 : ssl_with_bad_version 61 | # B503 : ssl_with_bad_defaults 62 | # B504 : ssl_with_no_version 63 | # B505 : weak_cryptographic_key 64 | # B506 : yaml_load 65 | # B507 : ssh_no_host_key_verification 66 | # B601 : paramiko_calls 67 | # B602 : subprocess_popen_with_shell_equals_true 68 | # B603 : subprocess_without_shell_equals_true 69 | # B604 : any_other_function_with_shell_equals_true 70 | # B605 : start_process_with_a_shell 71 | # B606 : start_process_with_no_shell 72 | # B607 : start_process_with_partial_path 73 | # B608 : hardcoded_sql_expressions 74 | # B609 : linux_commands_wildcard_injection 75 | # B610 : django_extra_used 76 | # B611 : django_rawsql_used 77 | # B701 : jinja2_autoescape_false 78 | # B702 : use_of_mako_templates 79 | # B703 : django_mark_safe 80 | 81 | # (optional) list included test IDs here, eg '[B101, B406]': 82 | tests: 83 | 84 | # (optional) list skipped test IDs here, eg '[B101, B406]': 85 | skips: [B101, B104] -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | PYPI_USERNAME= 2 | PYPI_PASSWORD= 3 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = W291, W293, E501, E731, W503 3 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | python-version: [3.9] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Python 17 | uses: actions/setup-python@v1 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | 21 | - name: Install Poetry 22 | uses: snok/install-poetry@v1 23 | with: 24 | virtualenvs-create: true 25 | virtualenvs-in-project: true 26 | 27 | - name: Load cached venv 28 | id: cached-poetry-dependencies 29 | uses: actions/cache@v2 30 | with: 31 | path: .venv 32 | key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} 33 | 34 | - name: Install dependencies 35 | if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' 36 | run: poetry install --no-interaction --no-root 37 | 38 | - name: Build wheels 39 | run: | 40 | poetry version $(git tag --points-at HEAD) 41 | poetry build 42 | 43 | - name: Upload 44 | env: 45 | USERNAME: __token__ 46 | PASSWORD: ${{ secrets.PYPI_TOKEN }} 47 | run: | 48 | poetry publish --username=$USERNAME --password=$PASSWORD 49 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | # Run tests and check pre-commit 2 | name: Test 3 | 4 | on: 5 | push: 6 | pull_request: 7 | types: [opened, synchronize] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | python-version: [3.8, 3.9] 16 | fail-fast: false 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Set up Python 21 | uses: actions/setup-python@v1 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | 25 | - name: Install Poetry 26 | uses: snok/install-poetry@v1 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .task 3 | scratch.py 4 | .env 5 | 6 | # Sphinx docs 7 | docs/_build 8 | docs/_static 9 | docs/_templates 10 | docs/make.bat 11 | 12 | # Byte-compiled / optimized / DLL files 13 | __pycache__/ 14 | *.py[cod] 15 | *$py.class 16 | 17 | # C extensions 18 | *.so 19 | 20 | # Distribution / packaging 21 | .Python 22 | build/ 23 | develop-eggs/ 24 | dist/ 25 | downloads/ 26 | eggs/ 27 | .eggs/ 28 | lib/ 29 | lib64/ 30 | parts/ 31 | sdist/ 32 | var/ 33 | wheels/ 34 | share/python-wheels/ 35 | *.egg-info/ 36 | .installed.cfg 37 | *.egg 38 | MANIFEST 39 | 40 | # PyInstaller 41 | # Usually these files are written by a python script from a template 42 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 43 | *.manifest 44 | *.spec 45 | 46 | # Installer logs 47 | pip-log.txt 48 | pip-delete-this-directory.txt 49 | 50 | # Unit test / coverage reports 51 | htmlcov/ 52 | .tox/ 53 | .nox/ 54 | .coverage 55 | .coverage.* 56 | .cache 57 | nosetests.xml 58 | coverage.xml 59 | *.cover 60 | *.py,cover 61 | .hypothesis/ 62 | .pytest_cache/ 63 | cover/ 64 | 65 | # Translations 66 | *.mo 67 | *.pot 68 | 69 | # Django stuff: 70 | *.log 71 | local_settings.py 72 | db.sqlite3 73 | db.sqlite3-journal 74 | 75 | # Flask stuff: 76 | instance/ 77 | .webassets-cache 78 | 79 | # Scrapy stuff: 80 | .scrapy 81 | 82 | # Sphinx documentation 83 | docs/_build/ 84 | 85 | # PyBuilder 86 | .pybuilder/ 87 | target/ 88 | 89 | # Jupyter Notebook 90 | .ipynb_checkpoints 91 | 92 | # IPython 93 | profile_default/ 94 | ipython_config.py 95 | 96 | # pyenv 97 | # For a library or package, you might want to ignore these files since the code is 98 | # intended to run in multiple environments; otherwise, check them in: 99 | # .python-version 100 | 101 | # pipenv 102 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 103 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 104 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 105 | # install all needed dependencies. 106 | #Pipfile.lock 107 | 108 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 109 | __pypackages__/ 110 | 111 | # Celery stuff 112 | celerybeat-schedule 113 | celerybeat.pid 114 | 115 | # SageMath parsed files 116 | *.sage.py 117 | 118 | # Environments 119 | .env 120 | .venv 121 | env/ 122 | venv/ 123 | ENV/ 124 | env.bak/ 125 | venv.bak/ 126 | 127 | # Spyder project settings 128 | .spyderproject 129 | .spyproject 130 | 131 | # Rope project settings 132 | .ropeproject 133 | 134 | # mkdocs documentation 135 | /site 136 | 137 | # mypy 138 | .mypy_cache/ 139 | .dmypy.json 140 | dmypy.json 141 | 142 | # Pyre type checker 143 | .pyre/ 144 | 145 | # pytype static type analyzer 146 | .pytype/ 147 | 148 | # Cython debug symbols 149 | cython_debug/ 150 | 151 | .python-version 152 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: docs/conf.py 5 | 6 | python: 7 | version: 3.8 8 | install: 9 | - method: pip 10 | path: . 11 | extra_requirements: 12 | - docs 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Harry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastAPI Third Party Auth 2 | 3 |

4 | 6 | Test 8 | 9 | 10 | Documentation Status 11 | 12 | 14 | Package version 16 | 17 |

18 | 19 | --- 20 | 21 | **Documentation**: https://fastapi-third-party-auth.readthedocs.io/ 22 | 23 | **Source Code**: https://github.com/aiwizo/fastapi-third-party-auth 24 | 25 | --- 26 | 27 | Simple library for using a third party authentication service with 28 | [FastAPI](https://github.com/tiangolo/fastapi). Verifies and decrypts 3rd party 29 | OpenID Connect tokens to protect your endpoints. 30 | 31 | Easily used with authentication services such as: 32 | 33 | - [Keycloak](https://www.keycloak.org/) (open source) 34 | - [SuperTokens](https://supertokens.com/) (open source) 35 | - [Auth0](https://auth0.com/) 36 | - [Okta](https://www.okta.com/products/authentication/) 37 | 38 | FastAPI's generated interactive documentation supports the grant flows: 39 | 40 | ```python3 41 | GrantType.AUTHORIZATION_CODE 42 | GrantType.IMPLICIT 43 | GrantType.PASSWORD 44 | GrantType.CLIENT_CREDENTIALS 45 | ``` 46 | 47 | ![example documentation](example-docs.png) 48 | 49 | ## Installation 50 | 51 | ``` 52 | poetry add fastapi-third-party-auth 53 | ``` 54 | 55 | Or, for the old-timers: 56 | 57 | ``` 58 | pip install fastapi-third-party-auth 59 | ``` 60 | 61 | ## Usage 62 | 63 | See [this example](tree/master/example) for how to use 64 | `docker-compose` to set up authentication with `fastapi-third-party-auth` + 65 | [Keycloak](https://www.keycloak.org/). 66 | 67 | ### Standard usage 68 | 69 | ```python3 70 | from fastapi import Depends 71 | from fastapi import FastAPI 72 | from fastapi import Security 73 | from fastapi import status 74 | 75 | from fastapi_third_party_auth import Auth 76 | from fastapi_third_party_auth import GrantType 77 | from fastapi_third_party_auth import KeycloakIDToken 78 | 79 | auth = Auth( 80 | openid_connect_url="http://localhost:8080/auth/realms/my-realm/.well-known/openid-configuration", 81 | issuer="http://localhost:8080/auth/realms/my-realm", # optional, verification only 82 | client_id="my-client", # optional, verification only 83 | scopes=["email"], # optional, verification only 84 | grant_types=[GrantType.IMPLICIT], # optional, docs only 85 | idtoken_model=KeycloakIDToken, # optional, verification only 86 | ) 87 | 88 | app = FastAPI( 89 | title="Example", 90 | version="dev", 91 | dependencies=[Depends(auth)], 92 | ) 93 | 94 | @app.get("/protected") 95 | def protected(id_token: KeycloakIDToken = Security(auth.required)): 96 | return dict(message=f"You are {id_token.email}") 97 | ``` 98 | 99 | ### Optional: Custom token validation 100 | 101 | The IDToken class will accept any number of extra fields but you can also 102 | validate fields in the token like this: 103 | 104 | ```python3 105 | class MyAuthenticatedUser(IDToken): 106 | custom_field: str 107 | custom_default: float = 3.14 108 | 109 | auth = Auth( 110 | ..., 111 | idtoken_model=MyAuthenticatedUser, 112 | ) 113 | ``` 114 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-midnight -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | from typing import List 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = "fastapi-third-party-auth" 21 | copyright = "2020, Harry M. Winters, Richard Löwenström" 22 | author = "Harry M. Winters, Richard Löwenström" 23 | 24 | # The full version, including alpha/beta/rc tags 25 | release = "0.0.0" 26 | 27 | 28 | # -- General configuration --------------------------------------------------- 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon"] 34 | 35 | # Add any paths that contain templates here, relative to this directory. 36 | templates_path = ["_templates"] 37 | 38 | # List of patterns, relative to source directory, that match files and 39 | # directories to ignore when looking for source files. 40 | # This pattern also affects html_static_path and html_extra_path. 41 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 42 | 43 | 44 | # -- Options for HTML output ------------------------------------------------- 45 | 46 | # The theme to use for HTML and HTML Help pages. See the documentation for 47 | # a list of builtin themes. 48 | # 49 | html_theme = "sphinx_rtd_theme" 50 | 51 | # Add any paths that contain custom static files (such as style sheets) here, 52 | # relative to this directory. They are copied after the builtin static files, 53 | # so a file named "default.css" will overwrite the builtin "default.css". 54 | html_static_path: List[str] = [] 55 | 56 | autodoc_default_options = { 57 | "members": True, 58 | "member-order": "bysource", 59 | "special-members": "__init__", 60 | "undoc-members": True, 61 | "exclude-members": "__weakref__", 62 | } 63 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to fastapi-third-party-auth's documentation! 2 | ==================================================== 3 | 4 | Verify and decrypt 3rd party OpenID Connect tokens to protect your 5 | `FastAPI `_ endpoints. 6 | 7 | Easily used with authenticators such as: 8 | 9 | - `Keycloak `_ (open source) 10 | - `SuperTokens `_ (open source) 11 | - `Auth0 `_ 12 | - `Okta `_ 13 | 14 | 15 | FastAPI's generated interactive documentation supports the grant types 16 | ``authorization_code``, ``implicit``, ``password`` and ``client_credentials``. 17 | 18 | .. toctree:: 19 | :maxdepth: 2 20 | :caption: Contents: 21 | 22 | Indices and tables 23 | ================== 24 | 25 | * :ref:`genindex` 26 | * :ref:`modindex` 27 | * :ref:`search` 28 | 29 | Installation 30 | ------------ 31 | 32 | .. code-block:: bash 33 | 34 | poetry add fastapi-third-party-auth 35 | 36 | Or, for the old-timers: 37 | 38 | .. code-block:: bash 39 | 40 | pip install fastapi-third-party-auth 41 | 42 | Example 43 | ------- 44 | 45 | Basic configuration for verifying OIDC tokens. 46 | 47 | .. code-block:: python3 48 | 49 | from fastapi import Depends 50 | from fastapi import FastAPI 51 | from fastapi import Security 52 | from fastapi import status 53 | 54 | from fastapi_third_party_auth import Auth 55 | from fastapi_third_party_auth import GrantType 56 | from fastapi_third_party_auth import KeycloakIDToken 57 | 58 | auth = Auth( 59 | openid_connect_url="http://localhost:8080/auth/realms/my-realm/.well-known/openid-configuration", 60 | issuer="http://localhost:8080/auth/realms/my-realm", # optional, verification only 61 | client_id="my-client", # optional, verification only 62 | scopes=["email"], # optional, verification only 63 | grant_types=[GrantType.IMPLICIT], # optional, docs only 64 | idtoken_model=KeycloakIDToken, # optional, verification only 65 | ) 66 | 67 | app = FastAPI( 68 | title="Example", 69 | version="dev", 70 | dependencies=[Depends(auth)], 71 | ) 72 | 73 | @app.get("/protected") 74 | def protected(id_token: KeycloakIDToken = Security(auth.required)): 75 | return dict(message=f"You are {id_token.email}") 76 | 77 | 78 | API Reference 79 | ============= 80 | 81 | Auth 82 | ---- 83 | .. automodule:: fastapi_third_party_auth.auth 84 | :members: 85 | 86 | Grant Types 87 | ----------- 88 | .. automodule:: fastapi_third_party_auth.grant_types 89 | :members: 90 | 91 | IDToken Types 92 | ------------- 93 | .. automodule:: fastapi_third_party_auth.idtoken_types 94 | :members: 95 | -------------------------------------------------------------------------------- /example-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextml-code/fastapi-third-party-auth/039ee19eef78889f4a09342f18680ecccd7e7879/example-docs.png -------------------------------------------------------------------------------- /example/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:experimental 2 | FROM python:3.8.8 3 | 4 | RUN apt-get update \ 5 | && apt-get install -y \ 6 | gcc \ 7 | ffmpeg \ 8 | libsm6 \ 9 | libxext6 \ 10 | git \ 11 | curl 12 | 13 | RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - 14 | 15 | WORKDIR /app 16 | COPY pyproject.toml . 17 | COPY poetry.lock . 18 | 19 | RUN ~/.poetry/bin/poetry install 20 | 21 | COPY app app 22 | 23 | EXPOSE 8000 24 | 25 | CMD ["/root/.poetry/bin/poetry", "run", "python", "-m", "app.main", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"] 26 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example with keycloak 2 | 3 | ## Setup your realm 4 | 5 | 1. Start up keycloak with `docker-compose up` (the fastapi app will crash since 6 | we do not have a realm yet). 7 | 2. Log into keycloak at http://localhost:8080 with username/password `admin/admin`. 8 | 3. Create a realm `my-realm`. This will set your `openid_connect_url` to `http://localhost:8080/auth/realms/my-realm/.well-known/openid-configuration` 9 | and your issuer to `http://localhost:8080/auth/realms/my-realm`. 10 | 4. Allow implicit flow (in order for login in interactive docs to work). 11 | 5. Create a user and add credentials (password). 12 | 13 | ## Login into docs with your credentials 14 | 15 | 1. Kill app and then restart with `docker-compose up`. 16 | 2. Go to `http://localhost:8000/docs` and login with your credentials by 17 | clicking `authorize` in the top right corner. 18 | -------------------------------------------------------------------------------- /example/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextml-code/fastapi-third-party-auth/039ee19eef78889f4a09342f18680ecccd7e7879/example/app/__init__.py -------------------------------------------------------------------------------- /example/app/config.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseSettings 2 | from pydantic import Field 3 | 4 | 5 | class Config(BaseSettings): 6 | openid_connect_url: str = Field(..., env="AUTH_OPENID_CONNECT_URL") 7 | issuer: str = Field(..., env="AUTH_ISSUER") 8 | client_id: str = Field(..., env="AUTH_CLIENT_ID") 9 | 10 | 11 | config = Config() 12 | -------------------------------------------------------------------------------- /example/app/main.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import uvicorn 4 | from app.config import config 5 | from fastapi import Depends 6 | from fastapi import FastAPI 7 | from fastapi import Security 8 | from fastapi import status 9 | from fastapi.middleware.cors import CORSMiddleware 10 | from starlette.responses import RedirectResponse 11 | 12 | from fastapi_third_party_auth import Auth 13 | from fastapi_third_party_auth import KeycloakIDToken 14 | 15 | auth = Auth( 16 | openid_connect_url=config.openid_connect_url, 17 | issuer=config.issuer, # optional, verification only 18 | client_id=config.client_id, # optional, verification only 19 | scopes=["email"], # optional, verification only 20 | idtoken_model=KeycloakIDToken, # optional, verification only 21 | ) 22 | 23 | app = FastAPI( 24 | title="Example", 25 | version="dev", 26 | dependencies=[Depends(auth)], 27 | ) 28 | 29 | # CORS errors instead of seeing internal exceptions 30 | # https://stackoverflow.com/questions/63606055/why-do-i-get-cors-error-reason-cors-request-did-not-succeed 31 | cors = CORSMiddleware( 32 | app=app, 33 | allow_origins=["*"], 34 | allow_credentials=True, 35 | allow_methods=["*"], 36 | allow_headers=["*"], 37 | ) 38 | 39 | 40 | @app.get("/", status_code=status.HTTP_303_SEE_OTHER) 41 | def redirect_to_docs(): 42 | return RedirectResponse(url="/docs") 43 | 44 | 45 | @app.get("/protected") 46 | def protected(id_token: KeycloakIDToken = Security(auth.required)): 47 | print(id_token) 48 | return dict(message=f"You are {id_token.email}") 49 | 50 | 51 | @app.get("/mixed") 52 | def mixed(id_token: Optional[KeycloakIDToken] = Security(auth.optional)): 53 | if id_token is None: 54 | return dict(message="Welcome guest user!") 55 | else: 56 | return dict(message=f"Welcome {id_token.email}!") 57 | 58 | 59 | if __name__ == "__main__": 60 | uvicorn.run("app.main:cors", host="0.0.0.0", port=8000, loop="asyncio", reload=True) 61 | -------------------------------------------------------------------------------- /example/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | test-fastapi-keycloak: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | depends_on: 9 | keycloak: 10 | condition: service_healthy 11 | network_mode: host 12 | 13 | keycloak: 14 | image: jboss/keycloak:15.0.2 15 | volumes: 16 | - ./my-realm-export.json:/tmp/my-realm-export.json 17 | environment: 18 | - DB_VENDOR=POSTGRES 19 | - DB_ADDR=keycloak-postgres 20 | - DB_DATABASE=keycloak 21 | - DB_USER=keycloak 22 | - DB_SCHEMA=public 23 | - DB_PASSWORD=password 24 | - KEYCLOAK_USER=admin 25 | - KEYCLOAK_PASSWORD=admin 26 | ports: 27 | - 8080:8080 28 | depends_on: 29 | - keycloak-postgres 30 | environment: 31 | - AUTH_OPENID_CONNECT_URL=http://localhost:8080/auth/realms/my-realm/.well-known/openid-configuration 32 | - AUTH_ISSUER=http://localhost:8080/auth/realms/my-realm 33 | - AUTH_CLIENT_ID=my-client 34 | healthcheck: 35 | test: "curl http://localhost:8080" 36 | interval: 30s 37 | timeout: 30s 38 | retries: 3 39 | start_period: 2m 40 | 41 | keycloak-postgres: 42 | image: postgres:13.4-alpine3.14 43 | volumes: 44 | - ./data/keycloak-postgres:/var/lib/postgresql/data/ 45 | environment: 46 | - POSTGRES_USER=keycloak 47 | - POSTGRES_PASSWORD=password 48 | - POSTGRES_DB=keycloak 49 | -------------------------------------------------------------------------------- /example/poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "cachetools" 3 | version = "4.2.4" 4 | description = "Extensible memoizing collections and decorators" 5 | category = "main" 6 | optional = false 7 | python-versions = "~=3.5" 8 | 9 | [[package]] 10 | name = "certifi" 11 | version = "2021.10.8" 12 | description = "Python package for providing Mozilla's CA Bundle." 13 | category = "main" 14 | optional = false 15 | python-versions = "*" 16 | 17 | [[package]] 18 | name = "cffi" 19 | version = "1.15.0" 20 | description = "Foreign Function Interface for Python calling C code." 21 | category = "main" 22 | optional = false 23 | python-versions = "*" 24 | 25 | [package.dependencies] 26 | pycparser = "*" 27 | 28 | [[package]] 29 | name = "charset-normalizer" 30 | version = "2.0.9" 31 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 32 | category = "main" 33 | optional = false 34 | python-versions = ">=3.5.0" 35 | 36 | [package.extras] 37 | unicode_backport = ["unicodedata2"] 38 | 39 | [[package]] 40 | name = "click" 41 | version = "7.1.2" 42 | description = "Composable command line interface toolkit" 43 | category = "main" 44 | optional = false 45 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 46 | 47 | [[package]] 48 | name = "cryptography" 49 | version = "36.0.0" 50 | description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." 51 | category = "main" 52 | optional = false 53 | python-versions = ">=3.6" 54 | 55 | [package.dependencies] 56 | cffi = ">=1.12" 57 | 58 | [package.extras] 59 | docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] 60 | docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] 61 | pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] 62 | sdist = ["setuptools_rust (>=0.11.4)"] 63 | ssh = ["bcrypt (>=3.1.5)"] 64 | test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] 65 | 66 | [[package]] 67 | name = "ecdsa" 68 | version = "0.17.0" 69 | description = "ECDSA cryptographic signature library (pure python)" 70 | category = "main" 71 | optional = false 72 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 73 | 74 | [package.dependencies] 75 | six = ">=1.9.0" 76 | 77 | [package.extras] 78 | gmpy = ["gmpy"] 79 | gmpy2 = ["gmpy2"] 80 | 81 | [[package]] 82 | name = "fastapi" 83 | version = "0.63.0" 84 | description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" 85 | category = "main" 86 | optional = false 87 | python-versions = ">=3.6" 88 | 89 | [package.dependencies] 90 | pydantic = ">=1.0.0,<2.0.0" 91 | starlette = "0.13.6" 92 | 93 | [package.extras] 94 | all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.6.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=3.0.0,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"] 95 | dev = ["python-jose[cryptography] (>=3.1.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "graphene (>=2.1.8,<3.0.0)"] 96 | doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=6.1.4,<7.0.0)", "markdown-include (>=0.5.1,<0.6.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer-cli (>=0.0.9,<0.0.10)", "pyyaml (>=5.3.1,<6.0.0)"] 97 | test = ["pytest (==5.4.3)", "pytest-cov (==2.10.0)", "pytest-asyncio (>=0.14.0,<0.15.0)", "mypy (==0.790)", "flake8 (>=3.8.3,<4.0.0)", "black (==20.8b1)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.15.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"] 98 | 99 | [[package]] 100 | name = "fastapi-third-party-auth" 101 | version = "0.1.1" 102 | description = "Simple library for using a third party authentication service like Keycloak or Auth0 with FastAPI" 103 | category = "main" 104 | optional = false 105 | python-versions = ">=3.8,<4.0" 106 | 107 | [package.dependencies] 108 | cachetools = ">=4.1.1" 109 | fastapi = ">=0.61.0" 110 | pydantic = ">=1.6.1" 111 | python-jose = {version = ">=3.2.0", extras = ["cryptography"]} 112 | requests = ">=2.24.0" 113 | 114 | [[package]] 115 | name = "h11" 116 | version = "0.12.0" 117 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 118 | category = "main" 119 | optional = false 120 | python-versions = ">=3.6" 121 | 122 | [[package]] 123 | name = "idna" 124 | version = "3.3" 125 | description = "Internationalized Domain Names in Applications (IDNA)" 126 | category = "main" 127 | optional = false 128 | python-versions = ">=3.5" 129 | 130 | [[package]] 131 | name = "pyasn1" 132 | version = "0.4.8" 133 | description = "ASN.1 types and codecs" 134 | category = "main" 135 | optional = false 136 | python-versions = "*" 137 | 138 | [[package]] 139 | name = "pycparser" 140 | version = "2.21" 141 | description = "C parser in Python" 142 | category = "main" 143 | optional = false 144 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 145 | 146 | [[package]] 147 | name = "pydantic" 148 | version = "1.8.2" 149 | description = "Data validation and settings management using python 3.6 type hinting" 150 | category = "main" 151 | optional = false 152 | python-versions = ">=3.6.1" 153 | 154 | [package.dependencies] 155 | typing-extensions = ">=3.7.4.3" 156 | 157 | [package.extras] 158 | dotenv = ["python-dotenv (>=0.10.4)"] 159 | email = ["email-validator (>=1.0.3)"] 160 | 161 | [[package]] 162 | name = "python-jose" 163 | version = "3.3.0" 164 | description = "JOSE implementation in Python" 165 | category = "main" 166 | optional = false 167 | python-versions = "*" 168 | 169 | [package.dependencies] 170 | cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"cryptography\""} 171 | ecdsa = "!=0.15" 172 | pyasn1 = "*" 173 | rsa = "*" 174 | 175 | [package.extras] 176 | cryptography = ["cryptography (>=3.4.0)"] 177 | pycrypto = ["pycrypto (>=2.6.0,<2.7.0)", "pyasn1"] 178 | pycryptodome = ["pycryptodome (>=3.3.1,<4.0.0)", "pyasn1"] 179 | 180 | [[package]] 181 | name = "requests" 182 | version = "2.26.0" 183 | description = "Python HTTP for Humans." 184 | category = "main" 185 | optional = false 186 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 187 | 188 | [package.dependencies] 189 | certifi = ">=2017.4.17" 190 | charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} 191 | idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} 192 | urllib3 = ">=1.21.1,<1.27" 193 | 194 | [package.extras] 195 | socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] 196 | use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] 197 | 198 | [[package]] 199 | name = "rsa" 200 | version = "4.8" 201 | description = "Pure-Python RSA implementation" 202 | category = "main" 203 | optional = false 204 | python-versions = ">=3.6,<4" 205 | 206 | [package.dependencies] 207 | pyasn1 = ">=0.1.3" 208 | 209 | [[package]] 210 | name = "six" 211 | version = "1.16.0" 212 | description = "Python 2 and 3 compatibility utilities" 213 | category = "main" 214 | optional = false 215 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 216 | 217 | [[package]] 218 | name = "starlette" 219 | version = "0.13.6" 220 | description = "The little ASGI library that shines." 221 | category = "main" 222 | optional = false 223 | python-versions = ">=3.6" 224 | 225 | [package.extras] 226 | full = ["aiofiles", "graphene", "itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests", "ujson"] 227 | 228 | [[package]] 229 | name = "typing-extensions" 230 | version = "4.0.1" 231 | description = "Backported and Experimental Type Hints for Python 3.6+" 232 | category = "main" 233 | optional = false 234 | python-versions = ">=3.6" 235 | 236 | [[package]] 237 | name = "urllib3" 238 | version = "1.26.7" 239 | description = "HTTP library with thread-safe connection pooling, file post, and more." 240 | category = "main" 241 | optional = false 242 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 243 | 244 | [package.extras] 245 | brotli = ["brotlipy (>=0.6.0)"] 246 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 247 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 248 | 249 | [[package]] 250 | name = "uvicorn" 251 | version = "0.13.4" 252 | description = "The lightning-fast ASGI server." 253 | category = "main" 254 | optional = false 255 | python-versions = "*" 256 | 257 | [package.dependencies] 258 | click = ">=7.0.0,<8.0.0" 259 | h11 = ">=0.8" 260 | 261 | [package.extras] 262 | standard = ["websockets (>=8.0.0,<9.0.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "httptools (>=0.1.0,<0.2.0)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"] 263 | 264 | [metadata] 265 | lock-version = "1.1" 266 | python-versions = "^3.8" 267 | content-hash = "16e8e1eeccb4e7c62035e7571890d53c61a5e0cbe686a2e35621c97811b81f32" 268 | 269 | [metadata.files] 270 | cachetools = [ 271 | {file = "cachetools-4.2.4-py3-none-any.whl", hash = "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1"}, 272 | {file = "cachetools-4.2.4.tar.gz", hash = "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693"}, 273 | ] 274 | certifi = [ 275 | {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, 276 | {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, 277 | ] 278 | cffi = [ 279 | {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, 280 | {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, 281 | {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, 282 | {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, 283 | {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, 284 | {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, 285 | {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, 286 | {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, 287 | {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, 288 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, 289 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, 290 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, 291 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, 292 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, 293 | {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, 294 | {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, 295 | {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, 296 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, 297 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, 298 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, 299 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, 300 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, 301 | {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, 302 | {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, 303 | {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, 304 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, 305 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, 306 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, 307 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, 308 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, 309 | {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, 310 | {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, 311 | {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, 312 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, 313 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, 314 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, 315 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, 316 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, 317 | {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, 318 | {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, 319 | {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, 320 | {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, 321 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, 322 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, 323 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, 324 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, 325 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, 326 | {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, 327 | {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, 328 | {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, 329 | ] 330 | charset-normalizer = [ 331 | {file = "charset-normalizer-2.0.9.tar.gz", hash = "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"}, 332 | {file = "charset_normalizer-2.0.9-py3-none-any.whl", hash = "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721"}, 333 | ] 334 | click = [ 335 | {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, 336 | {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, 337 | ] 338 | cryptography = [ 339 | {file = "cryptography-36.0.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:9511416e85e449fe1de73f7f99b21b3aa04fba4c4d335d30c486ba3756e3a2a6"}, 340 | {file = "cryptography-36.0.0-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:97199a13b772e74cdcdb03760c32109c808aff7cd49c29e9cf4b7754bb725d1d"}, 341 | {file = "cryptography-36.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:494106e9cd945c2cadfce5374fa44c94cfadf01d4566a3b13bb487d2e6c7959e"}, 342 | {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6fbbbb8aab4053fa018984bb0e95a16faeb051dd8cca15add2a27e267ba02b58"}, 343 | {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:684993ff6f67000a56454b41bdc7e015429732d65a52d06385b6e9de6181c71e"}, 344 | {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c702855cd3174666ef0d2d13dcc879090aa9c6c38f5578896407a7028f75b9f"}, 345 | {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d91bc9f535599bed58f6d2e21a2724cb0c3895bf41c6403fe881391d29096f1d"}, 346 | {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b17d83b3d1610e571fedac21b2eb36b816654d6f7496004d6a0d32f99d1d8120"}, 347 | {file = "cryptography-36.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8982c19bb90a4fa2aad3d635c6d71814e38b643649b4000a8419f8691f20ac44"}, 348 | {file = "cryptography-36.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:24469d9d33217ffd0ce4582dfcf2a76671af115663a95328f63c99ec7ece61a4"}, 349 | {file = "cryptography-36.0.0-cp36-abi3-win32.whl", hash = "sha256:f6a5a85beb33e57998dc605b9dbe7deaa806385fdf5c4810fb849fcd04640c81"}, 350 | {file = "cryptography-36.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:2deab5ec05d83ddcf9b0916319674d3dae88b0e7ee18f8962642d3cde0496568"}, 351 | {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2049f8b87f449fc6190350de443ee0c1dd631f2ce4fa99efad2984de81031681"}, 352 | {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a776bae1629c8d7198396fd93ec0265f8dd2341c553dc32b976168aaf0e6a636"}, 353 | {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:aa94d617a4cd4cdf4af9b5af65100c036bce22280ebb15d8b5262e8273ebc6ba"}, 354 | {file = "cryptography-36.0.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:5c49c9e8fb26a567a2b3fa0343c89f5d325447956cc2fc7231c943b29a973712"}, 355 | {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef216d13ac8d24d9cd851776662f75f8d29c9f2d05cdcc2d34a18d32463a9b0b"}, 356 | {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:231c4a69b11f6af79c1495a0e5a85909686ea8db946935224b7825cfb53827ed"}, 357 | {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f92556f94e476c1b616e6daec5f7ddded2c082efa7cee7f31c7aeda615906ed8"}, 358 | {file = "cryptography-36.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d73e3a96c38173e0aa5646c31bf8473bc3564837977dd480f5cbeacf1d7ef3a3"}, 359 | {file = "cryptography-36.0.0.tar.gz", hash = "sha256:52f769ecb4ef39865719aedc67b4b7eae167bafa48dbc2a26dd36fa56460507f"}, 360 | ] 361 | ecdsa = [ 362 | {file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"}, 363 | {file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"}, 364 | ] 365 | fastapi = [ 366 | {file = "fastapi-0.63.0-py3-none-any.whl", hash = "sha256:98d8ea9591d8512fdadf255d2a8fa56515cdd8624dca4af369da73727409508e"}, 367 | {file = "fastapi-0.63.0.tar.gz", hash = "sha256:63c4592f5ef3edf30afa9a44fa7c6b7ccb20e0d3f68cd9eba07b44d552058dcb"}, 368 | ] 369 | fastapi-third-party-auth = [ 370 | {file = "fastapi-third-party-auth-0.1.1.tar.gz", hash = "sha256:1eb3c2e67b55451f8bd6aa6ee798ee72c48571d67e58ec5faacc8401c3c9700c"}, 371 | {file = "fastapi_third_party_auth-0.1.1-py3-none-any.whl", hash = "sha256:27b5141a2c15bec525297e75c8d2b63d4abf4d59e374aa4af429f0e058809c8f"}, 372 | ] 373 | h11 = [ 374 | {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, 375 | {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, 376 | ] 377 | idna = [ 378 | {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, 379 | {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, 380 | ] 381 | pyasn1 = [ 382 | {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, 383 | {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, 384 | {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, 385 | {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, 386 | {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, 387 | {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, 388 | {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, 389 | {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, 390 | {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, 391 | {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, 392 | {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, 393 | {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, 394 | {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, 395 | ] 396 | pycparser = [ 397 | {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, 398 | {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, 399 | ] 400 | pydantic = [ 401 | {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, 402 | {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"}, 403 | {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"}, 404 | {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"}, 405 | {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"}, 406 | {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"}, 407 | {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"}, 408 | {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"}, 409 | {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"}, 410 | {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"}, 411 | {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"}, 412 | {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"}, 413 | {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"}, 414 | {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"}, 415 | {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"}, 416 | {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"}, 417 | {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"}, 418 | {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"}, 419 | {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"}, 420 | {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"}, 421 | {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"}, 422 | {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, 423 | ] 424 | python-jose = [ 425 | {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, 426 | {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, 427 | ] 428 | requests = [ 429 | {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, 430 | {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, 431 | ] 432 | rsa = [ 433 | {file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"}, 434 | {file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"}, 435 | ] 436 | six = [ 437 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 438 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 439 | ] 440 | starlette = [ 441 | {file = "starlette-0.13.6-py3-none-any.whl", hash = "sha256:bd2ffe5e37fb75d014728511f8e68ebf2c80b0fa3d04ca1479f4dc752ae31ac9"}, 442 | {file = "starlette-0.13.6.tar.gz", hash = "sha256:ebe8ee08d9be96a3c9f31b2cb2a24dbdf845247b745664bd8a3f9bd0c977fdbc"}, 443 | ] 444 | typing-extensions = [ 445 | {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, 446 | {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, 447 | ] 448 | urllib3 = [ 449 | {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, 450 | {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, 451 | ] 452 | uvicorn = [ 453 | {file = "uvicorn-0.13.4-py3-none-any.whl", hash = "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"}, 454 | {file = "uvicorn-0.13.4.tar.gz", hash = "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202"}, 455 | ] 456 | -------------------------------------------------------------------------------- /example/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "keycloak-example" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Richard Löwenström "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.8" 9 | fastapi = "^0.63.0" 10 | uvicorn = "^0.13.4" 11 | fastapi-third-party-auth = "^0.1.1" 12 | 13 | [tool.poetry.dev-dependencies] 14 | 15 | [build-system] 16 | requires = ["poetry-core>=1.0.0"] 17 | build-backend = "poetry.core.masonry.api" 18 | -------------------------------------------------------------------------------- /fastapi_third_party_auth/__init__.py: -------------------------------------------------------------------------------- 1 | from fastapi_third_party_auth.auth import Auth # noqa 2 | from fastapi_third_party_auth.grant_types import GrantType # noqa 3 | from fastapi_third_party_auth.idtoken_types import IDToken # noqa 4 | from fastapi_third_party_auth.idtoken_types import KeycloakIDToken # noqa 5 | from fastapi_third_party_auth.idtoken_types import OktaIDToken # noqa 6 | -------------------------------------------------------------------------------- /fastapi_third_party_auth/auth.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Module for validating Open ID Connect tokens. 4 | 5 | Usage 6 | ===== 7 | 8 | .. code-block:: python3 9 | 10 | # This assumes you've already configured Auth in your_app/auth.py 11 | from your_app.auth import auth 12 | 13 | @app.get("/auth") 14 | def test_auth(authenticated_user: IDToken = Security(auth.required)): 15 | return f"Hello {authenticated_user.preferred_username}" 16 | """ 17 | 18 | from typing import List 19 | from typing import Optional 20 | from typing import Type 21 | 22 | from fastapi import Depends 23 | from fastapi import HTTPException 24 | from fastapi import Request 25 | from fastapi import status 26 | from fastapi.openapi.models import OAuthFlowAuthorizationCode 27 | from fastapi.openapi.models import OAuthFlowClientCredentials 28 | from fastapi.openapi.models import OAuthFlowImplicit 29 | from fastapi.openapi.models import OAuthFlowPassword 30 | from fastapi.openapi.models import OAuthFlows 31 | from fastapi.security import HTTPAuthorizationCredentials 32 | from fastapi.security import HTTPBearer 33 | from fastapi.security import OAuth2 34 | from fastapi.security import SecurityScopes 35 | from jose import ExpiredSignatureError 36 | from jose import JWTError 37 | from jose import jwt 38 | from jose.exceptions import JWTClaimsError 39 | 40 | from fastapi_third_party_auth import discovery 41 | from fastapi_third_party_auth.grant_types import GrantType 42 | from fastapi_third_party_auth.idtoken_types import IDToken 43 | 44 | 45 | class Auth(OAuth2): 46 | def __init__( 47 | self, 48 | openid_connect_url: str, 49 | issuer: Optional[str] = None, 50 | client_id: Optional[str] = None, 51 | scopes: List[str] = list(), 52 | grant_types: List[GrantType] = [GrantType.IMPLICIT], 53 | signature_cache_ttl: int = 3600, 54 | idtoken_model: Type[IDToken] = IDToken, 55 | ): 56 | """Configure authentication :func:`auth = Auth(...) ` and then: 57 | 58 | 1. Show authentication in the interactive docs with :func:`Depends(auth) ` 59 | when setting up FastAPI. 60 | 2. Use :func:`Security(auth.required) ` or 61 | :func:`Security(auth.optional) ` in your endpoints to 62 | check user credentials. 63 | 64 | Args: 65 | openid_connect_url (URL): URL to the "well known" openid connect config 66 | e.g. https://dev-123456.okta.com/.well-known/openid-configuration 67 | issuer (URL): (Optional) The issuer URL from your auth server. 68 | client_id (str): (Optional) The client_id configured by your auth server. 69 | scopes (Dict[str, str]): (Optional) A dictionary of scopes and their descriptions. 70 | grant_types (List[GrantType]): (Optional) Grant types shown in docs. 71 | signature_cache_ttl (int): (Optional) How many seconds your app should 72 | cache the authorization server's public signatures. 73 | idtoken_model (Type): (Optional) The model to use for validating the ID Token. 74 | 75 | Raises: 76 | Nothing intentional 77 | """ 78 | 79 | self.openid_connect_url = openid_connect_url 80 | self.issuer = issuer 81 | self.client_id = client_id 82 | self.idtoken_model = idtoken_model 83 | self.scopes = scopes 84 | 85 | self.discover = discovery.configure(cache_ttl=signature_cache_ttl) 86 | oidc_discoveries = self.discover.auth_server( 87 | openid_connect_url=self.openid_connect_url 88 | ) 89 | # scopes_dict = { 90 | # scope: "" for scope in self.discover.supported_scopes(oidc_discoveries) 91 | # } 92 | 93 | flows = OAuthFlows() 94 | if GrantType.AUTHORIZATION_CODE in grant_types: 95 | flows.authorizationCode = OAuthFlowAuthorizationCode( 96 | authorizationUrl=self.discover.authorization_url(oidc_discoveries), 97 | tokenUrl=self.discover.token_url(oidc_discoveries), 98 | # scopes=scopes_dict, 99 | ) 100 | 101 | if GrantType.CLIENT_CREDENTIALS in grant_types: 102 | flows.clientCredentials = OAuthFlowClientCredentials( 103 | tokenUrl=self.discover.token_url(oidc_discoveries), 104 | # scopes=scopes_dict, 105 | ) 106 | 107 | if GrantType.PASSWORD in grant_types: 108 | flows.password = OAuthFlowPassword( 109 | tokenUrl=self.discover.token_url(oidc_discoveries), 110 | # scopes=scopes_dict, 111 | ) 112 | 113 | if GrantType.IMPLICIT in grant_types: 114 | flows.implicit = OAuthFlowImplicit( 115 | authorizationUrl=self.discover.authorization_url(oidc_discoveries), 116 | # scopes=scopes_dict, 117 | ) 118 | 119 | super().__init__( 120 | scheme_name="OIDC", 121 | flows=flows, 122 | auto_error=False, 123 | ) 124 | 125 | async def __call__(self, request: Request) -> None: 126 | return None 127 | 128 | def required( 129 | self, 130 | security_scopes: SecurityScopes, 131 | authorization_credentials: Optional[HTTPAuthorizationCredentials] = Depends( 132 | HTTPBearer() 133 | ), 134 | ) -> IDToken: 135 | """Validate and parse OIDC ID token against configuration. 136 | Note this function caches the signatures and algorithms of the issuing 137 | server for signature_cache_ttl seconds. 138 | 139 | Args: 140 | security_scopes (SecurityScopes): Security scopes 141 | auth_header (str): Base64 encoded OIDC Token. This is invoked 142 | behind the scenes by Depends. 143 | 144 | Return: 145 | IDToken (self.idtoken_model): User information 146 | 147 | raises: 148 | HTTPException(status_code=401, detail=f"Unauthorized: {err}") 149 | IDToken validation errors 150 | """ 151 | 152 | id_token = self.authenticate_user( 153 | security_scopes, 154 | authorization_credentials, 155 | auto_error=True, 156 | ) 157 | if id_token is None: 158 | raise HTTPException(status.HTTP_401_UNAUTHORIZED) 159 | else: 160 | return id_token 161 | 162 | def optional( 163 | self, 164 | security_scopes: SecurityScopes, 165 | authorization_credentials: Optional[HTTPAuthorizationCredentials] = Depends( 166 | HTTPBearer(auto_error=False) 167 | ), 168 | ) -> Optional[IDToken]: 169 | """Optionally validate and parse OIDC ID token against configuration. 170 | Will not raise if the user is not authenticated. Note this function 171 | caches the signatures and algorithms of the issuing server for 172 | signature_cache_ttl seconds. 173 | 174 | Args: 175 | security_scopes (SecurityScopes): Security scopes 176 | auth_header (str): Base64 encoded OIDC Token. This is invoked 177 | behind the scenes by Depends. 178 | 179 | Return: 180 | IDToken (self.idtoken_model): User information 181 | 182 | raises: 183 | IDToken validation errors 184 | """ 185 | 186 | return self.authenticate_user( 187 | security_scopes, 188 | authorization_credentials, 189 | auto_error=False, 190 | ) 191 | 192 | def authenticate_user( 193 | self, 194 | security_scopes: SecurityScopes, 195 | authorization_credentials: Optional[HTTPAuthorizationCredentials], 196 | auto_error: bool, 197 | ) -> Optional[IDToken]: 198 | """Validate and parse OIDC ID token against against configuration. 199 | Note this function caches the signatures and algorithms of the issuing server 200 | for signature_cache_ttl seconds. 201 | 202 | Args: 203 | security_scopes (SecurityScopes): Security scopes 204 | auth_header (str): Base64 encoded OIDC Token 205 | auto_error (bool): If True, will raise an HTTPException if the user 206 | is not authenticated. 207 | 208 | Return: 209 | IDToken (self.idtoken_model): User information 210 | 211 | raises: 212 | HTTPException(status_code=401, detail=f"Unauthorized: {err}") 213 | """ 214 | 215 | if ( 216 | authorization_credentials is None 217 | or authorization_credentials.scheme.lower() != "bearer" 218 | ): 219 | if auto_error: 220 | raise HTTPException( 221 | status.HTTP_401_UNAUTHORIZED, detail="Missing bearer token" 222 | ) 223 | else: 224 | return None 225 | 226 | oidc_discoveries = self.discover.auth_server( 227 | openid_connect_url=self.openid_connect_url 228 | ) 229 | key = self.discover.public_keys(oidc_discoveries) 230 | algorithms = self.discover.signing_algos(oidc_discoveries) 231 | 232 | try: 233 | id_token = jwt.decode( 234 | authorization_credentials.credentials, 235 | key, 236 | algorithms, 237 | issuer=self.issuer, 238 | audience=self.client_id, 239 | options={ 240 | # Disabled at_hash check since we aren't using the access token 241 | "verify_at_hash": False, 242 | "verify_iss": self.issuer is not None, 243 | "verify_aud": self.client_id is not None, 244 | }, 245 | ) 246 | 247 | if ( 248 | type(id_token["aud"]) == list 249 | and len(id_token["aud"]) >= 1 250 | and "azp" not in id_token 251 | ): 252 | raise JWTError( 253 | 'Missing authorized party "azp" in IDToken when there ' 254 | "are multiple audiences" 255 | ) 256 | 257 | except (ExpiredSignatureError, JWTError, JWTClaimsError) as error: 258 | raise HTTPException(status_code=401, detail=f"Unauthorized: {error}") 259 | 260 | expected_scopes = set(self.scopes + security_scopes.scopes) 261 | token_scopes = id_token.get("scope", "").split(" ") 262 | if not expected_scopes.issubset(token_scopes): 263 | raise HTTPException( 264 | status.HTTP_401_UNAUTHORIZED, 265 | detail=( 266 | f"Missing scope token, expected {expected_scopes} to be a " 267 | f"subset of received {token_scopes}", 268 | ), 269 | ) 270 | 271 | return self.idtoken_model(**id_token) 272 | -------------------------------------------------------------------------------- /fastapi_third_party_auth/discovery.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | import requests 3 | from cachetools import TTLCache 4 | from cachetools import cached 5 | from threading import Lock 6 | 7 | 8 | def configure(*_, cache_ttl: int): 9 | @cached(TTLCache(1, cache_ttl), key=lambda d: d["jwks_uri"], lock=Lock()) 10 | def get_authentication_server_public_keys(OIDC_spec: Dict): 11 | """ 12 | Retrieve the public keys used by the authentication server 13 | for signing OIDC ID tokens. 14 | """ 15 | keys_uri = OIDC_spec["jwks_uri"] 16 | r = requests.get(keys_uri) 17 | keys = r.json() 18 | return keys 19 | 20 | def get_signing_algos(OIDC_spec: Dict): 21 | algos = OIDC_spec["id_token_signing_alg_values_supported"] 22 | return algos 23 | 24 | @cached(TTLCache(1, cache_ttl), lock=Lock()) 25 | def discover_auth_server(*_, openid_connect_url: str) -> Dict: 26 | r = requests.get(openid_connect_url) 27 | # Raise if the auth server is failing since we can't verify tokens 28 | r.raise_for_status() 29 | configuration = r.json() 30 | return configuration 31 | 32 | def get_authorization_url(OIDC_spec: Dict) -> str: 33 | return OIDC_spec["authorization_endpoint"] 34 | 35 | def get_token_url(OIDC_spec: Dict) -> str: 36 | return OIDC_spec["token_endpoint"] 37 | 38 | def get_supported_scopes(OIDC_spec: Dict) -> str: 39 | return OIDC_spec["scopes_supported"] 40 | 41 | class functions: 42 | auth_server = discover_auth_server 43 | public_keys = get_authentication_server_public_keys 44 | signing_algos = get_signing_algos 45 | authorization_url = get_authorization_url 46 | token_url = get_token_url 47 | supported_scopes = get_supported_scopes 48 | 49 | return functions 50 | -------------------------------------------------------------------------------- /fastapi_third_party_auth/grant_types.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class GrantType(str, Enum): 5 | """Grant types that can be used in the interactive documentation.""" 6 | 7 | AUTHORIZATION_CODE = "authorization_code" 8 | CLIENT_CREDENTIALS = "client_credentials" 9 | IMPLICIT = "implicit" 10 | PASSWORD = "password" # nosec 11 | -------------------------------------------------------------------------------- /fastapi_third_party_auth/idtoken_types.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from typing import Union 3 | 4 | from pydantic import BaseModel 5 | from pydantic import Extra 6 | 7 | 8 | class IDToken(BaseModel): 9 | """Pydantic model representing an OIDC ID Token. 10 | 11 | ID Tokens are polymorphic and may have many attributes not defined in the spec thus this model accepts 12 | all addition fields. Only required fields are listed in the attributes section of this docstring or 13 | enforced by pydantic. 14 | 15 | See the specifications here. https://openid.net/specs/openid-connect-core-1_0.html#IDToken 16 | 17 | Parameters: 18 | iss (str): Issuer Identifier for the Issuer of the response. 19 | sub (str): Subject Identifier. 20 | aud (Union[str, List[str]]): Audience(s) that this ID Token is intended for. 21 | exp (str): Expiration time on or after which the ID Token MUST NOT be accepted for processing. 22 | iat (iat): Time at which the JWT was issued. 23 | 24 | """ 25 | 26 | iss: str 27 | sub: str 28 | aud: Union[str, List[str]] 29 | exp: int 30 | iat: int 31 | 32 | class Config: 33 | extra = Extra.allow 34 | 35 | 36 | class OktaIDToken(IDToken): 37 | """Pydantic Model for the IDToken returned by Okta's OIDC implementation.""" 38 | 39 | auth_time: int 40 | ver: int 41 | jti: str 42 | amr: List[str] 43 | idp: str 44 | nonce: str 45 | at_hash: str 46 | name: str 47 | email: str 48 | preferred_username: str 49 | 50 | 51 | class KeycloakIDToken(IDToken): 52 | """Pydantic Model for the IDToken returned by Keycloak's OIDC implementation.""" 53 | 54 | jti: str 55 | name: str 56 | email: str 57 | email_verified: bool 58 | preferred_username: str 59 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "alabaster" 3 | version = "0.7.12" 4 | description = "A configurable sidebar-enabled Sphinx theme" 5 | category = "dev" 6 | optional = false 7 | python-versions = "*" 8 | 9 | [[package]] 10 | name = "anyio" 11 | version = "3.6.1" 12 | description = "High level compatibility layer for multiple asynchronous event loop implementations" 13 | category = "main" 14 | optional = false 15 | python-versions = ">=3.6.2" 16 | 17 | [package.dependencies] 18 | idna = ">=2.8" 19 | sniffio = ">=1.1" 20 | 21 | [package.extras] 22 | doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] 23 | test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] 24 | trio = ["trio (>=0.16)"] 25 | 26 | [[package]] 27 | name = "appdirs" 28 | version = "1.4.4" 29 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 30 | category = "dev" 31 | optional = false 32 | python-versions = "*" 33 | 34 | [[package]] 35 | name = "asgiref" 36 | version = "3.5.2" 37 | description = "ASGI specs, helper code, and adapters" 38 | category = "dev" 39 | optional = false 40 | python-versions = ">=3.7" 41 | 42 | [package.extras] 43 | tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] 44 | 45 | [[package]] 46 | name = "astroid" 47 | version = "2.12.10" 48 | description = "An abstract syntax tree for Python with inference support." 49 | category = "dev" 50 | optional = false 51 | python-versions = ">=3.7.2" 52 | 53 | [package.dependencies] 54 | lazy-object-proxy = ">=1.4.0" 55 | typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} 56 | wrapt = [ 57 | {version = ">=1.11,<2", markers = "python_version < \"3.11\""}, 58 | {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, 59 | ] 60 | 61 | [[package]] 62 | name = "atomicwrites" 63 | version = "1.4.1" 64 | description = "Atomic file writes." 65 | category = "dev" 66 | optional = false 67 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 68 | 69 | [[package]] 70 | name = "attrs" 71 | version = "22.1.0" 72 | description = "Classes Without Boilerplate" 73 | category = "dev" 74 | optional = false 75 | python-versions = ">=3.5" 76 | 77 | [package.extras] 78 | dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] 79 | docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] 80 | tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] 81 | tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] 82 | 83 | [[package]] 84 | name = "Babel" 85 | version = "2.10.3" 86 | description = "Internationalization utilities" 87 | category = "dev" 88 | optional = false 89 | python-versions = ">=3.6" 90 | 91 | [package.dependencies] 92 | pytz = ">=2015.7" 93 | 94 | [[package]] 95 | name = "black" 96 | version = "21.7b0" 97 | description = "The uncompromising code formatter." 98 | category = "dev" 99 | optional = false 100 | python-versions = ">=3.6.2" 101 | 102 | [package.dependencies] 103 | appdirs = "*" 104 | click = ">=7.1.2" 105 | mypy-extensions = ">=0.4.3" 106 | pathspec = ">=0.8.1,<1" 107 | regex = ">=2020.1.8" 108 | tomli = ">=0.2.6,<2.0.0" 109 | 110 | [package.extras] 111 | colorama = ["colorama (>=0.4.3)"] 112 | d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"] 113 | python2 = ["typed-ast (>=1.4.2)"] 114 | uvloop = ["uvloop (>=0.15.2)"] 115 | 116 | [[package]] 117 | name = "cachetools" 118 | version = "5.2.0" 119 | description = "Extensible memoizing collections and decorators" 120 | category = "main" 121 | optional = false 122 | python-versions = "~=3.7" 123 | 124 | [[package]] 125 | name = "certifi" 126 | version = "2022.9.24" 127 | description = "Python package for providing Mozilla's CA Bundle." 128 | category = "main" 129 | optional = false 130 | python-versions = ">=3.6" 131 | 132 | [[package]] 133 | name = "cffi" 134 | version = "1.15.1" 135 | description = "Foreign Function Interface for Python calling C code." 136 | category = "main" 137 | optional = false 138 | python-versions = "*" 139 | 140 | [package.dependencies] 141 | pycparser = "*" 142 | 143 | [[package]] 144 | name = "charset-normalizer" 145 | version = "2.1.1" 146 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 147 | category = "main" 148 | optional = false 149 | python-versions = ">=3.6.0" 150 | 151 | [package.extras] 152 | unicode_backport = ["unicodedata2"] 153 | 154 | [[package]] 155 | name = "click" 156 | version = "8.1.3" 157 | description = "Composable command line interface toolkit" 158 | category = "dev" 159 | optional = false 160 | python-versions = ">=3.7" 161 | 162 | [package.dependencies] 163 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 164 | 165 | [[package]] 166 | name = "colorama" 167 | version = "0.4.5" 168 | description = "Cross-platform colored terminal text." 169 | category = "dev" 170 | optional = false 171 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 172 | 173 | [[package]] 174 | name = "cryptography" 175 | version = "38.0.1" 176 | description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." 177 | category = "main" 178 | optional = false 179 | python-versions = ">=3.6" 180 | 181 | [package.dependencies] 182 | cffi = ">=1.12" 183 | 184 | [package.extras] 185 | docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] 186 | docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] 187 | pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] 188 | sdist = ["setuptools-rust (>=0.11.4)"] 189 | ssh = ["bcrypt (>=3.1.5)"] 190 | test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] 191 | 192 | [[package]] 193 | name = "dill" 194 | version = "0.3.5.1" 195 | description = "serialize all of python" 196 | category = "dev" 197 | optional = false 198 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" 199 | 200 | [package.extras] 201 | graph = ["objgraph (>=1.7.2)"] 202 | 203 | [[package]] 204 | name = "docutils" 205 | version = "0.16" 206 | description = "Docutils -- Python Documentation Utilities" 207 | category = "dev" 208 | optional = false 209 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 210 | 211 | [[package]] 212 | name = "ecdsa" 213 | version = "0.18.0" 214 | description = "ECDSA cryptographic signature library (pure python)" 215 | category = "main" 216 | optional = false 217 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 218 | 219 | [package.dependencies] 220 | six = ">=1.9.0" 221 | 222 | [package.extras] 223 | gmpy = ["gmpy"] 224 | gmpy2 = ["gmpy2"] 225 | 226 | [[package]] 227 | name = "fastapi" 228 | version = "0.85.0" 229 | description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" 230 | category = "main" 231 | optional = false 232 | python-versions = ">=3.7" 233 | 234 | [package.dependencies] 235 | pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" 236 | starlette = "0.20.4" 237 | 238 | [package.extras] 239 | all = ["email-validator (>=1.1.1,<2.0.0)", "itsdangerous (>=1.1.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "uvicorn[standard] (>=0.12.0,<0.19.0)"] 240 | dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "uvicorn[standard] (>=0.12.0,<0.19.0)"] 241 | doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer (>=0.4.1,<0.7.0)"] 242 | test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.971)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-orjson (==3.6.2)", "types-ujson (==5.4.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] 243 | 244 | [[package]] 245 | name = "h11" 246 | version = "0.14.0" 247 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 248 | category = "dev" 249 | optional = false 250 | python-versions = ">=3.7" 251 | 252 | [[package]] 253 | name = "idna" 254 | version = "3.4" 255 | description = "Internationalized Domain Names in Applications (IDNA)" 256 | category = "main" 257 | optional = false 258 | python-versions = ">=3.5" 259 | 260 | [[package]] 261 | name = "imagesize" 262 | version = "1.4.1" 263 | description = "Getting image size from png/jpeg/jpeg2000/gif file" 264 | category = "dev" 265 | optional = false 266 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 267 | 268 | [[package]] 269 | name = "iniconfig" 270 | version = "1.1.1" 271 | description = "iniconfig: brain-dead simple config-ini parsing" 272 | category = "dev" 273 | optional = false 274 | python-versions = "*" 275 | 276 | [[package]] 277 | name = "isort" 278 | version = "5.10.1" 279 | description = "A Python utility / library to sort Python imports." 280 | category = "dev" 281 | optional = false 282 | python-versions = ">=3.6.1,<4.0" 283 | 284 | [package.extras] 285 | colors = ["colorama (>=0.4.3,<0.5.0)"] 286 | pipfile_deprecated_finder = ["pipreqs", "requirementslib"] 287 | plugins = ["setuptools"] 288 | requirements_deprecated_finder = ["pip-api", "pipreqs"] 289 | 290 | [[package]] 291 | name = "Jinja2" 292 | version = "3.1.2" 293 | description = "A very fast and expressive template engine." 294 | category = "dev" 295 | optional = false 296 | python-versions = ">=3.7" 297 | 298 | [package.dependencies] 299 | MarkupSafe = ">=2.0" 300 | 301 | [package.extras] 302 | i18n = ["Babel (>=2.7)"] 303 | 304 | [[package]] 305 | name = "lazy-object-proxy" 306 | version = "1.7.1" 307 | description = "A fast and thorough lazy object proxy." 308 | category = "dev" 309 | optional = false 310 | python-versions = ">=3.6" 311 | 312 | [[package]] 313 | name = "MarkupSafe" 314 | version = "2.1.1" 315 | description = "Safely add untrusted strings to HTML/XML markup." 316 | category = "dev" 317 | optional = false 318 | python-versions = ">=3.7" 319 | 320 | [[package]] 321 | name = "mccabe" 322 | version = "0.7.0" 323 | description = "McCabe checker, plugin for flake8" 324 | category = "dev" 325 | optional = false 326 | python-versions = ">=3.6" 327 | 328 | [[package]] 329 | name = "mypy" 330 | version = "0.910" 331 | description = "Optional static typing for Python" 332 | category = "dev" 333 | optional = false 334 | python-versions = ">=3.5" 335 | 336 | [package.dependencies] 337 | mypy-extensions = ">=0.4.3,<0.5.0" 338 | toml = "*" 339 | typing-extensions = ">=3.7.4" 340 | 341 | [package.extras] 342 | dmypy = ["psutil (>=4.0)"] 343 | python2 = ["typed-ast (>=1.4.0,<1.5.0)"] 344 | 345 | [[package]] 346 | name = "mypy-extensions" 347 | version = "0.4.3" 348 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 349 | category = "dev" 350 | optional = false 351 | python-versions = "*" 352 | 353 | [[package]] 354 | name = "packaging" 355 | version = "21.3" 356 | description = "Core utilities for Python packages" 357 | category = "dev" 358 | optional = false 359 | python-versions = ">=3.6" 360 | 361 | [package.dependencies] 362 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 363 | 364 | [[package]] 365 | name = "pathspec" 366 | version = "0.10.1" 367 | description = "Utility library for gitignore style pattern matching of file paths." 368 | category = "dev" 369 | optional = false 370 | python-versions = ">=3.7" 371 | 372 | [[package]] 373 | name = "platformdirs" 374 | version = "2.5.2" 375 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 376 | category = "dev" 377 | optional = false 378 | python-versions = ">=3.7" 379 | 380 | [package.extras] 381 | docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] 382 | test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] 383 | 384 | [[package]] 385 | name = "pluggy" 386 | version = "1.0.0" 387 | description = "plugin and hook calling mechanisms for python" 388 | category = "dev" 389 | optional = false 390 | python-versions = ">=3.6" 391 | 392 | [package.extras] 393 | dev = ["pre-commit", "tox"] 394 | testing = ["pytest", "pytest-benchmark"] 395 | 396 | [[package]] 397 | name = "py" 398 | version = "1.11.0" 399 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 400 | category = "dev" 401 | optional = false 402 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 403 | 404 | [[package]] 405 | name = "pyasn1" 406 | version = "0.4.8" 407 | description = "ASN.1 types and codecs" 408 | category = "main" 409 | optional = false 410 | python-versions = "*" 411 | 412 | [[package]] 413 | name = "pycparser" 414 | version = "2.21" 415 | description = "C parser in Python" 416 | category = "main" 417 | optional = false 418 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 419 | 420 | [[package]] 421 | name = "pydantic" 422 | version = "1.10.2" 423 | description = "Data validation and settings management using python type hints" 424 | category = "main" 425 | optional = false 426 | python-versions = ">=3.7" 427 | 428 | [package.dependencies] 429 | typing-extensions = ">=4.1.0" 430 | 431 | [package.extras] 432 | dotenv = ["python-dotenv (>=0.10.4)"] 433 | email = ["email-validator (>=1.0.3)"] 434 | 435 | [[package]] 436 | name = "Pygments" 437 | version = "2.13.0" 438 | description = "Pygments is a syntax highlighting package written in Python." 439 | category = "dev" 440 | optional = false 441 | python-versions = ">=3.6" 442 | 443 | [package.extras] 444 | plugins = ["importlib-metadata"] 445 | 446 | [[package]] 447 | name = "PyJWT" 448 | version = "1.7.1" 449 | description = "JSON Web Token implementation in Python" 450 | category = "dev" 451 | optional = false 452 | python-versions = "*" 453 | 454 | [package.extras] 455 | crypto = ["cryptography (>=1.4)"] 456 | flake8 = ["flake8", "flake8-import-order", "pep8-naming"] 457 | test = ["pytest (>=4.0.1,<5.0.0)", "pytest-cov (>=2.6.0,<3.0.0)", "pytest-runner (>=4.2,<5.0.0)"] 458 | 459 | [[package]] 460 | name = "pylint" 461 | version = "2.15.3" 462 | description = "python code static checker" 463 | category = "dev" 464 | optional = false 465 | python-versions = ">=3.7.2" 466 | 467 | [package.dependencies] 468 | astroid = ">=2.12.10,<=2.14.0-dev0" 469 | colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} 470 | dill = ">=0.2" 471 | isort = ">=4.2.5,<6" 472 | mccabe = ">=0.6,<0.8" 473 | platformdirs = ">=2.2.0" 474 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 475 | tomlkit = ">=0.10.1" 476 | typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} 477 | 478 | [package.extras] 479 | spelling = ["pyenchant (>=3.2,<4.0)"] 480 | testutils = ["gitpython (>3)"] 481 | 482 | [[package]] 483 | name = "pyparsing" 484 | version = "3.0.9" 485 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 486 | category = "dev" 487 | optional = false 488 | python-versions = ">=3.6.8" 489 | 490 | [package.extras] 491 | diagrams = ["jinja2", "railroad-diagrams"] 492 | 493 | [[package]] 494 | name = "pytest" 495 | version = "6.2.5" 496 | description = "pytest: simple powerful testing with Python" 497 | category = "dev" 498 | optional = false 499 | python-versions = ">=3.6" 500 | 501 | [package.dependencies] 502 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 503 | attrs = ">=19.2.0" 504 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 505 | iniconfig = "*" 506 | packaging = "*" 507 | pluggy = ">=0.12,<2.0" 508 | py = ">=1.8.2" 509 | toml = "*" 510 | 511 | [package.extras] 512 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 513 | 514 | [[package]] 515 | name = "python-jose" 516 | version = "3.3.0" 517 | description = "JOSE implementation in Python" 518 | category = "main" 519 | optional = false 520 | python-versions = "*" 521 | 522 | [package.dependencies] 523 | cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"cryptography\""} 524 | ecdsa = "!=0.15" 525 | pyasn1 = "*" 526 | rsa = "*" 527 | 528 | [package.extras] 529 | cryptography = ["cryptography (>=3.4.0)"] 530 | pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] 531 | pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] 532 | 533 | [[package]] 534 | name = "pytz" 535 | version = "2022.4" 536 | description = "World timezone definitions, modern and historical" 537 | category = "dev" 538 | optional = false 539 | python-versions = "*" 540 | 541 | [[package]] 542 | name = "regex" 543 | version = "2022.9.13" 544 | description = "Alternative regular expression module, to replace re." 545 | category = "dev" 546 | optional = false 547 | python-versions = ">=3.6" 548 | 549 | [[package]] 550 | name = "requests" 551 | version = "2.28.1" 552 | description = "Python HTTP for Humans." 553 | category = "main" 554 | optional = false 555 | python-versions = ">=3.7, <4" 556 | 557 | [package.dependencies] 558 | certifi = ">=2017.4.17" 559 | charset-normalizer = ">=2,<3" 560 | idna = ">=2.5,<4" 561 | urllib3 = ">=1.21.1,<1.27" 562 | 563 | [package.extras] 564 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 565 | use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] 566 | 567 | [[package]] 568 | name = "rsa" 569 | version = "4.9" 570 | description = "Pure-Python RSA implementation" 571 | category = "main" 572 | optional = false 573 | python-versions = ">=3.6,<4" 574 | 575 | [package.dependencies] 576 | pyasn1 = ">=0.1.3" 577 | 578 | [[package]] 579 | name = "setuptools" 580 | version = "65.4.1" 581 | description = "Easily download, build, install, upgrade, and uninstall Python packages" 582 | category = "dev" 583 | optional = false 584 | python-versions = ">=3.7" 585 | 586 | [package.extras] 587 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] 588 | testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] 589 | testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] 590 | 591 | [[package]] 592 | name = "six" 593 | version = "1.16.0" 594 | description = "Python 2 and 3 compatibility utilities" 595 | category = "main" 596 | optional = false 597 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 598 | 599 | [[package]] 600 | name = "sniffio" 601 | version = "1.3.0" 602 | description = "Sniff out which async library your code is running under" 603 | category = "main" 604 | optional = false 605 | python-versions = ">=3.7" 606 | 607 | [[package]] 608 | name = "snowballstemmer" 609 | version = "2.2.0" 610 | description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." 611 | category = "dev" 612 | optional = false 613 | python-versions = "*" 614 | 615 | [[package]] 616 | name = "Sphinx" 617 | version = "3.5.4" 618 | description = "Python documentation generator" 619 | category = "dev" 620 | optional = false 621 | python-versions = ">=3.5" 622 | 623 | [package.dependencies] 624 | alabaster = ">=0.7,<0.8" 625 | babel = ">=1.3" 626 | colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} 627 | docutils = ">=0.12,<0.17" 628 | imagesize = "*" 629 | Jinja2 = ">=2.3" 630 | packaging = "*" 631 | Pygments = ">=2.0" 632 | requests = ">=2.5.0" 633 | setuptools = "*" 634 | snowballstemmer = ">=1.1" 635 | sphinxcontrib-applehelp = "*" 636 | sphinxcontrib-devhelp = "*" 637 | sphinxcontrib-htmlhelp = "*" 638 | sphinxcontrib-jsmath = "*" 639 | sphinxcontrib-qthelp = "*" 640 | sphinxcontrib-serializinghtml = "*" 641 | 642 | [package.extras] 643 | docs = ["sphinxcontrib-websupport"] 644 | lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.800)"] 645 | test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] 646 | 647 | [[package]] 648 | name = "sphinx-rtd-theme" 649 | version = "1.0.0" 650 | description = "Read the Docs theme for Sphinx" 651 | category = "dev" 652 | optional = false 653 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" 654 | 655 | [package.dependencies] 656 | docutils = "<0.18" 657 | sphinx = ">=1.6" 658 | 659 | [package.extras] 660 | dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client"] 661 | 662 | [[package]] 663 | name = "sphinxcontrib-applehelp" 664 | version = "1.0.2" 665 | description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" 666 | category = "dev" 667 | optional = false 668 | python-versions = ">=3.5" 669 | 670 | [package.extras] 671 | lint = ["docutils-stubs", "flake8", "mypy"] 672 | test = ["pytest"] 673 | 674 | [[package]] 675 | name = "sphinxcontrib-devhelp" 676 | version = "1.0.2" 677 | description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." 678 | category = "dev" 679 | optional = false 680 | python-versions = ">=3.5" 681 | 682 | [package.extras] 683 | lint = ["docutils-stubs", "flake8", "mypy"] 684 | test = ["pytest"] 685 | 686 | [[package]] 687 | name = "sphinxcontrib-htmlhelp" 688 | version = "2.0.0" 689 | description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" 690 | category = "dev" 691 | optional = false 692 | python-versions = ">=3.6" 693 | 694 | [package.extras] 695 | lint = ["docutils-stubs", "flake8", "mypy"] 696 | test = ["html5lib", "pytest"] 697 | 698 | [[package]] 699 | name = "sphinxcontrib-jsmath" 700 | version = "1.0.1" 701 | description = "A sphinx extension which renders display math in HTML via JavaScript" 702 | category = "dev" 703 | optional = false 704 | python-versions = ">=3.5" 705 | 706 | [package.extras] 707 | test = ["flake8", "mypy", "pytest"] 708 | 709 | [[package]] 710 | name = "sphinxcontrib-qthelp" 711 | version = "1.0.3" 712 | description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." 713 | category = "dev" 714 | optional = false 715 | python-versions = ">=3.5" 716 | 717 | [package.extras] 718 | lint = ["docutils-stubs", "flake8", "mypy"] 719 | test = ["pytest"] 720 | 721 | [[package]] 722 | name = "sphinxcontrib-serializinghtml" 723 | version = "1.1.5" 724 | description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." 725 | category = "dev" 726 | optional = false 727 | python-versions = ">=3.5" 728 | 729 | [package.extras] 730 | lint = ["docutils-stubs", "flake8", "mypy"] 731 | test = ["pytest"] 732 | 733 | [[package]] 734 | name = "starlette" 735 | version = "0.20.4" 736 | description = "The little ASGI library that shines." 737 | category = "main" 738 | optional = false 739 | python-versions = ">=3.7" 740 | 741 | [package.dependencies] 742 | anyio = ">=3.4.0,<5" 743 | typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} 744 | 745 | [package.extras] 746 | full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"] 747 | 748 | [[package]] 749 | name = "toml" 750 | version = "0.10.2" 751 | description = "Python Library for Tom's Obvious, Minimal Language" 752 | category = "dev" 753 | optional = false 754 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 755 | 756 | [[package]] 757 | name = "tomli" 758 | version = "1.2.3" 759 | description = "A lil' TOML parser" 760 | category = "dev" 761 | optional = false 762 | python-versions = ">=3.6" 763 | 764 | [[package]] 765 | name = "tomlkit" 766 | version = "0.11.5" 767 | description = "Style preserving TOML library" 768 | category = "dev" 769 | optional = false 770 | python-versions = ">=3.6,<4.0" 771 | 772 | [[package]] 773 | name = "types-cachetools" 774 | version = "0.1.10" 775 | description = "Typing stubs for cachetools" 776 | category = "dev" 777 | optional = false 778 | python-versions = "*" 779 | 780 | [[package]] 781 | name = "types-requests" 782 | version = "2.28.11.1" 783 | description = "Typing stubs for requests" 784 | category = "dev" 785 | optional = false 786 | python-versions = "*" 787 | 788 | [package.dependencies] 789 | types-urllib3 = "<1.27" 790 | 791 | [[package]] 792 | name = "types-urllib3" 793 | version = "1.26.25" 794 | description = "Typing stubs for urllib3" 795 | category = "dev" 796 | optional = false 797 | python-versions = "*" 798 | 799 | [[package]] 800 | name = "typing-extensions" 801 | version = "4.3.0" 802 | description = "Backported and Experimental Type Hints for Python 3.7+" 803 | category = "main" 804 | optional = false 805 | python-versions = ">=3.7" 806 | 807 | [[package]] 808 | name = "urllib3" 809 | version = "1.26.12" 810 | description = "HTTP library with thread-safe connection pooling, file post, and more." 811 | category = "main" 812 | optional = false 813 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" 814 | 815 | [package.extras] 816 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] 817 | secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] 818 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 819 | 820 | [[package]] 821 | name = "uvicorn" 822 | version = "0.15.0" 823 | description = "The lightning-fast ASGI server." 824 | category = "dev" 825 | optional = false 826 | python-versions = "*" 827 | 828 | [package.dependencies] 829 | asgiref = ">=3.4.0" 830 | click = ">=7.0" 831 | h11 = ">=0.8" 832 | 833 | [package.extras] 834 | standard = ["PyYAML (>=5.1)", "colorama (>=0.4)", "httptools (>=0.2.0,<0.3.0)", "python-dotenv (>=0.13)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchgod (>=0.6)", "websockets (>=9.1)"] 835 | 836 | [[package]] 837 | name = "wrapt" 838 | version = "1.14.1" 839 | description = "Module for decorators, wrappers and monkey patching." 840 | category = "dev" 841 | optional = false 842 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 843 | 844 | [extras] 845 | docs = [] 846 | 847 | [metadata] 848 | lock-version = "1.1" 849 | python-versions = "^3.8" 850 | content-hash = "76eba1fbd3b013060c6a9e967cd2df4feaac84eb587d3b6b6434900a7beb398c" 851 | 852 | [metadata.files] 853 | alabaster = [ 854 | {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, 855 | {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, 856 | ] 857 | anyio = [ 858 | {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"}, 859 | {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"}, 860 | ] 861 | appdirs = [ 862 | {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, 863 | {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, 864 | ] 865 | asgiref = [ 866 | {file = "asgiref-3.5.2-py3-none-any.whl", hash = "sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4"}, 867 | {file = "asgiref-3.5.2.tar.gz", hash = "sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424"}, 868 | ] 869 | astroid = [ 870 | {file = "astroid-2.12.10-py3-none-any.whl", hash = "sha256:997e0c735df60d4a4caff27080a3afc51f9bdd693d3572a4a0b7090b645c36c5"}, 871 | {file = "astroid-2.12.10.tar.gz", hash = "sha256:81f870105d892e73bf535da77a8261aa5bde838fa4ed12bb2f435291a098c581"}, 872 | ] 873 | atomicwrites = [ 874 | {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, 875 | ] 876 | attrs = [ 877 | {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, 878 | {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, 879 | ] 880 | Babel = [ 881 | {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, 882 | {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, 883 | ] 884 | black = [ 885 | {file = "black-21.7b0-py3-none-any.whl", hash = "sha256:1c7aa6ada8ee864db745b22790a32f94b2795c253a75d6d9b5e439ff10d23116"}, 886 | {file = "black-21.7b0.tar.gz", hash = "sha256:c8373c6491de9362e39271630b65b964607bc5c79c83783547d76c839b3aa219"}, 887 | ] 888 | cachetools = [ 889 | {file = "cachetools-5.2.0-py3-none-any.whl", hash = "sha256:f9f17d2aec496a9aa6b76f53e3b614c965223c061982d434d160f930c698a9db"}, 890 | {file = "cachetools-5.2.0.tar.gz", hash = "sha256:6a94c6402995a99c3970cc7e4884bb60b4a8639938157eeed436098bf9831757"}, 891 | ] 892 | certifi = [ 893 | {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"}, 894 | {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"}, 895 | ] 896 | cffi = [ 897 | {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, 898 | {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, 899 | {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, 900 | {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, 901 | {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, 902 | {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, 903 | {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, 904 | {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, 905 | {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, 906 | {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, 907 | {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, 908 | {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, 909 | {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, 910 | {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, 911 | {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, 912 | {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, 913 | {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, 914 | {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, 915 | {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, 916 | {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, 917 | {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, 918 | {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, 919 | {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, 920 | {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, 921 | {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, 922 | {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, 923 | {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, 924 | {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, 925 | {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, 926 | {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, 927 | {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, 928 | {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, 929 | {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, 930 | {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, 931 | {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, 932 | {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, 933 | {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, 934 | {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, 935 | {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, 936 | {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, 937 | {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, 938 | {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, 939 | {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, 940 | {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, 941 | {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, 942 | {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, 943 | {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, 944 | {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, 945 | {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, 946 | {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, 947 | {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, 948 | {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, 949 | {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, 950 | {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, 951 | {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, 952 | {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, 953 | {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, 954 | {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, 955 | {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, 956 | {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, 957 | {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, 958 | {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, 959 | {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, 960 | {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, 961 | ] 962 | charset-normalizer = [ 963 | {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, 964 | {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, 965 | ] 966 | click = [ 967 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 968 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 969 | ] 970 | colorama = [ 971 | {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, 972 | {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, 973 | ] 974 | cryptography = [ 975 | {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f"}, 976 | {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad"}, 977 | {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153"}, 978 | {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407"}, 979 | {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e"}, 980 | {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0"}, 981 | {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd"}, 982 | {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6"}, 983 | {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a"}, 984 | {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294"}, 985 | {file = "cryptography-38.0.1-cp36-abi3-win32.whl", hash = "sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0"}, 986 | {file = "cryptography-38.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a"}, 987 | {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d"}, 988 | {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9"}, 989 | {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d"}, 990 | {file = "cryptography-38.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818"}, 991 | {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6"}, 992 | {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750"}, 993 | {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013"}, 994 | {file = "cryptography-38.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5"}, 995 | {file = "cryptography-38.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61"}, 996 | {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac"}, 997 | {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb"}, 998 | {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a"}, 999 | {file = "cryptography-38.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b"}, 1000 | {file = "cryptography-38.0.1.tar.gz", hash = "sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7"}, 1001 | ] 1002 | dill = [ 1003 | {file = "dill-0.3.5.1-py2.py3-none-any.whl", hash = "sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302"}, 1004 | {file = "dill-0.3.5.1.tar.gz", hash = "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"}, 1005 | ] 1006 | docutils = [ 1007 | {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, 1008 | {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, 1009 | ] 1010 | ecdsa = [ 1011 | {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, 1012 | {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, 1013 | ] 1014 | fastapi = [ 1015 | {file = "fastapi-0.85.0-py3-none-any.whl", hash = "sha256:1803d962f169dc9f8dde54a64b22eb16f6d81573f54401971f90f0a67234a8b4"}, 1016 | {file = "fastapi-0.85.0.tar.gz", hash = "sha256:bb219cfafd0d2ccf8f32310c9a257a06b0210bd8e2a03706a6f5a9f9f1416878"}, 1017 | ] 1018 | h11 = [ 1019 | {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, 1020 | {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, 1021 | ] 1022 | idna = [ 1023 | {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, 1024 | {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, 1025 | ] 1026 | imagesize = [ 1027 | {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, 1028 | {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, 1029 | ] 1030 | iniconfig = [ 1031 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 1032 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 1033 | ] 1034 | isort = [ 1035 | {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, 1036 | {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, 1037 | ] 1038 | Jinja2 = [ 1039 | {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, 1040 | {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, 1041 | ] 1042 | lazy-object-proxy = [ 1043 | {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, 1044 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, 1045 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, 1046 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, 1047 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, 1048 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, 1049 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, 1050 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, 1051 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, 1052 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, 1053 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, 1054 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, 1055 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, 1056 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, 1057 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, 1058 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, 1059 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, 1060 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, 1061 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, 1062 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, 1063 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, 1064 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, 1065 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, 1066 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, 1067 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, 1068 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, 1069 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, 1070 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, 1071 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, 1072 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, 1073 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, 1074 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, 1075 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, 1076 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, 1077 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, 1078 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, 1079 | {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, 1080 | ] 1081 | MarkupSafe = [ 1082 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, 1083 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, 1084 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, 1085 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, 1086 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, 1087 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, 1088 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, 1089 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, 1090 | {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, 1091 | {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, 1092 | {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, 1093 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, 1094 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, 1095 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, 1096 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, 1097 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, 1098 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, 1099 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, 1100 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, 1101 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, 1102 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, 1103 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, 1104 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, 1105 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, 1106 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, 1107 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, 1108 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, 1109 | {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, 1110 | {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, 1111 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, 1112 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, 1113 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, 1114 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, 1115 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, 1116 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, 1117 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, 1118 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, 1119 | {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, 1120 | {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, 1121 | {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, 1122 | ] 1123 | mccabe = [ 1124 | {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, 1125 | {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, 1126 | ] 1127 | mypy = [ 1128 | {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, 1129 | {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, 1130 | {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, 1131 | {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, 1132 | {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, 1133 | {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, 1134 | {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, 1135 | {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, 1136 | {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, 1137 | {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, 1138 | {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, 1139 | {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, 1140 | {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, 1141 | {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, 1142 | {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, 1143 | {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, 1144 | {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, 1145 | {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, 1146 | {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, 1147 | {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, 1148 | {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, 1149 | {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, 1150 | {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, 1151 | ] 1152 | mypy-extensions = [ 1153 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 1154 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 1155 | ] 1156 | packaging = [ 1157 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 1158 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 1159 | ] 1160 | pathspec = [ 1161 | {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, 1162 | {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, 1163 | ] 1164 | platformdirs = [ 1165 | {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, 1166 | {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, 1167 | ] 1168 | pluggy = [ 1169 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 1170 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 1171 | ] 1172 | py = [ 1173 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 1174 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 1175 | ] 1176 | pyasn1 = [ 1177 | {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, 1178 | {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, 1179 | ] 1180 | pycparser = [ 1181 | {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, 1182 | {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, 1183 | ] 1184 | pydantic = [ 1185 | {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"}, 1186 | {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"}, 1187 | {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"}, 1188 | {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"}, 1189 | {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"}, 1190 | {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"}, 1191 | {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"}, 1192 | {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"}, 1193 | {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"}, 1194 | {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"}, 1195 | {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"}, 1196 | {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"}, 1197 | {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"}, 1198 | {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"}, 1199 | {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"}, 1200 | {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"}, 1201 | {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"}, 1202 | {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"}, 1203 | {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"}, 1204 | {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"}, 1205 | {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"}, 1206 | {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"}, 1207 | {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"}, 1208 | {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"}, 1209 | {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"}, 1210 | {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"}, 1211 | {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"}, 1212 | {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"}, 1213 | {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"}, 1214 | {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"}, 1215 | {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"}, 1216 | {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"}, 1217 | {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"}, 1218 | {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"}, 1219 | {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"}, 1220 | {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"}, 1221 | ] 1222 | Pygments = [ 1223 | {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, 1224 | {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, 1225 | ] 1226 | PyJWT = [ 1227 | {file = "PyJWT-1.7.1-py2.py3-none-any.whl", hash = "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e"}, 1228 | {file = "PyJWT-1.7.1.tar.gz", hash = "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96"}, 1229 | ] 1230 | pylint = [ 1231 | {file = "pylint-2.15.3-py3-none-any.whl", hash = "sha256:7f6aad1d8d50807f7bc64f89ac75256a9baf8e6ed491cc9bc65592bc3f462cf1"}, 1232 | {file = "pylint-2.15.3.tar.gz", hash = "sha256:5fdfd44af182866999e6123139d265334267339f29961f00c89783155eacc60b"}, 1233 | ] 1234 | pyparsing = [ 1235 | {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, 1236 | {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, 1237 | ] 1238 | pytest = [ 1239 | {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, 1240 | {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, 1241 | ] 1242 | python-jose = [ 1243 | {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, 1244 | {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, 1245 | ] 1246 | pytz = [ 1247 | {file = "pytz-2022.4-py2.py3-none-any.whl", hash = "sha256:2c0784747071402c6e99f0bafdb7da0fa22645f06554c7ae06bf6358897e9c91"}, 1248 | {file = "pytz-2022.4.tar.gz", hash = "sha256:48ce799d83b6f8aab2020e369b627446696619e79645419610b9facd909b3174"}, 1249 | ] 1250 | regex = [ 1251 | {file = "regex-2022.9.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0394265391a86e2bbaa7606e59ac71bd9f1edf8665a59e42771a9c9adbf6fd4f"}, 1252 | {file = "regex-2022.9.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86df2049b18745f3cd4b0f4c4ef672bfac4b80ca488e6ecfd2bbfe68d2423a2c"}, 1253 | {file = "regex-2022.9.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce331b076b2b013e7d7f07157f957974ef0b0881a808e8a4a4b3b5105aee5d04"}, 1254 | {file = "regex-2022.9.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:360ffbc9357794ae41336b681dff1c0463193199dfb91fcad3ec385ea4972f46"}, 1255 | {file = "regex-2022.9.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18e503b1e515a10282b3f14f1b3d856194ecece4250e850fad230842ed31227f"}, 1256 | {file = "regex-2022.9.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e167d1ccd41d27b7b6655bb7a2dcb1b1eb1e0d2d662043470bd3b4315d8b2b"}, 1257 | {file = "regex-2022.9.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4146cb7ae6029fc83b5c905ec6d806b7e5568dc14297c423e66b86294bad6c39"}, 1258 | {file = "regex-2022.9.13-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a1aec4ae549fd7b3f52ceaf67e133010e2fba1538bf4d5fc5cd162a5e058d5df"}, 1259 | {file = "regex-2022.9.13-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cab548d6d972e1de584161487b2ac1aa82edd8430d1bde69587ba61698ad1cfb"}, 1260 | {file = "regex-2022.9.13-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3d64e1a7e6d98a4cdc8b29cb8d8ed38f73f49e55fbaa737bdb5933db99b9de22"}, 1261 | {file = "regex-2022.9.13-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:67a4c625361db04ae40ef7c49d3cbe2c1f5ff10b5a4491327ab20f19f2fb5d40"}, 1262 | {file = "regex-2022.9.13-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:5d0dd8b06896423211ce18fba0c75dacc49182a1d6514c004b535be7163dca0f"}, 1263 | {file = "regex-2022.9.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4318f69b79f9f7d84a7420e97d4bfe872dc767c72f891d4fea5fa721c74685f7"}, 1264 | {file = "regex-2022.9.13-cp310-cp310-win32.whl", hash = "sha256:26df88c9636a0c3f3bd9189dd435850a0c49d0b7d6e932500db3f99a6dd604d1"}, 1265 | {file = "regex-2022.9.13-cp310-cp310-win_amd64.whl", hash = "sha256:6fe1dd1021e0f8f3f454ce2811f1b0b148f2d25bb38c712fec00316551e93650"}, 1266 | {file = "regex-2022.9.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:83cc32a1a2fa5bac00f4abc0e6ce142e3c05d3a6d57e23bd0f187c59b4e1e43b"}, 1267 | {file = "regex-2022.9.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2effeaf50a6838f3dd4d3c5d265f06eabc748f476e8441892645ae3a697e273"}, 1268 | {file = "regex-2022.9.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59a786a55d00439d8fae4caaf71581f2aaef7297d04ee60345c3594efef5648a"}, 1269 | {file = "regex-2022.9.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b701dbc124558fd2b1b08005eeca6c9160e209108fbcbd00091fcfac641ac7"}, 1270 | {file = "regex-2022.9.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dab81cc4d58026861445230cfba27f9825e9223557926e7ec22156a1a140d55c"}, 1271 | {file = "regex-2022.9.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0c5cc3d1744a67c3b433dce91e5ef7c527d612354c1f1e8576d9e86bc5c5e2"}, 1272 | {file = "regex-2022.9.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:518272f25da93e02af4f1e94985f5042cec21557ef3591027d0716f2adda5d0a"}, 1273 | {file = "regex-2022.9.13-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8418ee2cb857b83881b8f981e4c636bc50a0587b12d98cb9b947408a3c484fe7"}, 1274 | {file = "regex-2022.9.13-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cfa4c956ff0a977c4823cb3b930b0a4e82543b060733628fec7ab3eb9b1abe37"}, 1275 | {file = "regex-2022.9.13-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a1c4d17879dd4c4432c08a1ca1ab379f12ab54af569e945b6fc1c4cf6a74ca45"}, 1276 | {file = "regex-2022.9.13-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:77c2879d3ba51e5ca6c2b47f2dcf3d04a976a623a8fc8236010a16c9e0b0a3c7"}, 1277 | {file = "regex-2022.9.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2885ec6eea629c648ecc9bde0837ec6b92208b7f36381689937fe5d64a517e8"}, 1278 | {file = "regex-2022.9.13-cp311-cp311-win32.whl", hash = "sha256:2dda4b096a6f630d6531728a45bd12c67ec3badf44342046dc77d4897277d4f2"}, 1279 | {file = "regex-2022.9.13-cp311-cp311-win_amd64.whl", hash = "sha256:592b9e2e1862168e71d9e612bfdc22c451261967dbd46681f14e76dfba7105fd"}, 1280 | {file = "regex-2022.9.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:df8fe00b60e4717662c7f80c810ba66dcc77309183c76b7754c0dff6f1d42054"}, 1281 | {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995e70bb8c91d1b99ed2aaf8ec44863e06ad1dfbb45d7df95f76ef583ec323a9"}, 1282 | {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad75173349ad79f9d21e0d0896b27dcb37bfd233b09047bc0b4d226699cf5c87"}, 1283 | {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7681c49da1a2d4b905b4f53d86c9ba4506e79fba50c4a664d9516056e0f7dfcc"}, 1284 | {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bc8edc5f8ef0ebb46f3fa0d02bd825bbe9cc63d59e428ffb6981ff9672f6de1"}, 1285 | {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bee775ff05c9d519195bd9e8aaaccfe3971db60f89f89751ee0f234e8aeac5"}, 1286 | {file = "regex-2022.9.13-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1a901ce5cd42658ab8f8eade51b71a6d26ad4b68c7cfc86b87efc577dfa95602"}, 1287 | {file = "regex-2022.9.13-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:14a7ab070fa3aec288076eed6ed828587b805ef83d37c9bfccc1a4a7cfbd8111"}, 1288 | {file = "regex-2022.9.13-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d23ac6b4bf9e32fcde5fcdb2e1fd5e7370d6693fcac51ee1d340f0e886f50d1f"}, 1289 | {file = "regex-2022.9.13-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:4cdbfa6d2befeaee0c899f19222e9b20fc5abbafe5e9c43a46ef819aeb7b75e5"}, 1290 | {file = "regex-2022.9.13-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ab07934725e6f25c6f87465976cc69aef1141e86987af49d8c839c3ffd367c72"}, 1291 | {file = "regex-2022.9.13-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d2a1371dc73e921f3c2e087c05359050f3525a9a34b476ebc8130e71bec55e97"}, 1292 | {file = "regex-2022.9.13-cp36-cp36m-win32.whl", hash = "sha256:fcbd1edff1473d90dc5cf4b52d355cf1f47b74eb7c85ba6e45f45d0116b8edbd"}, 1293 | {file = "regex-2022.9.13-cp36-cp36m-win_amd64.whl", hash = "sha256:fe428822b7a8c486bcd90b334e9ab541ce6cc0d6106993d59f201853e5e14121"}, 1294 | {file = "regex-2022.9.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d7430f041755801b712ec804aaf3b094b9b5facbaa93a6339812a8e00d7bd53a"}, 1295 | {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:079c182f99c89524069b9cd96f5410d6af437e9dca576a7d59599a574972707e"}, 1296 | {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59bac44b5a07b08a261537f652c26993af9b1bbe2a29624473968dd42fc29d56"}, 1297 | {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a59d0377e58d96a6f11636e97992f5b51b7e1e89eb66332d1c01b35adbabfe8a"}, 1298 | {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9d68eb704b24bc4d441b24e4a12653acd07d2c39940548761e0985a08bc1fff"}, 1299 | {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0385d66e73cdd4462f3cc42c76a6576ddcc12472c30e02a2ae82061bff132c32"}, 1300 | {file = "regex-2022.9.13-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:db45016364eec9ddbb5af93c8740c5c92eb7f5fc8848d1ae04205a40a1a2efc6"}, 1301 | {file = "regex-2022.9.13-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:03ff695518482b946a6d3d4ce9cbbd99a21320e20d94913080aa3841f880abcd"}, 1302 | {file = "regex-2022.9.13-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6b32b45433df1fad7fed738fe15200b6516da888e0bd1fdd6aa5e50cc16b76bc"}, 1303 | {file = "regex-2022.9.13-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:003a2e1449d425afc817b5f0b3d4c4aa9072dd5f3dfbf6c7631b8dc7b13233de"}, 1304 | {file = "regex-2022.9.13-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:a9eb9558e1d0f78e07082d8a70d5c4d631c8dd75575fae92105df9e19c736730"}, 1305 | {file = "regex-2022.9.13-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f6e0321921d2fdc082ef90c1fd0870f129c2e691bfdc4937dcb5cd308aba95c4"}, 1306 | {file = "regex-2022.9.13-cp37-cp37m-win32.whl", hash = "sha256:3f3b4594d564ed0b2f54463a9f328cf6a5b2a32610a90cdff778d6e3e561d08b"}, 1307 | {file = "regex-2022.9.13-cp37-cp37m-win_amd64.whl", hash = "sha256:8aba0d01e3dfd335f2cb107079b07fdddb4cd7fb2d8c8a1986f9cb8ce9246c24"}, 1308 | {file = "regex-2022.9.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:944567bb08f52268d8600ee5bdf1798b2b62ea002cc692a39cec113244cbdd0d"}, 1309 | {file = "regex-2022.9.13-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b664a4d33ffc6be10996606dfc25fd3248c24cc589c0b139feb4c158053565e"}, 1310 | {file = "regex-2022.9.13-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f06cc1190f3db3192ab8949e28f2c627e1809487e2cfc435b6524c1ce6a2f391"}, 1311 | {file = "regex-2022.9.13-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c57d50d4d5eb0c862569ca3c840eba2a73412f31d9ecc46ef0d6b2e621a592b"}, 1312 | {file = "regex-2022.9.13-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19a4da6f513045f5ba00e491215bd00122e5bd131847586522463e5a6b2bd65f"}, 1313 | {file = "regex-2022.9.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a926339356fe29595f8e37af71db37cd87ff764e15da8ad5129bbaff35bcc5a6"}, 1314 | {file = "regex-2022.9.13-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:091efcfdd4178a7e19a23776dc2b1fafb4f57f4d94daf340f98335817056f874"}, 1315 | {file = "regex-2022.9.13-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:880dbeb6bdde7d926b4d8e41410b16ffcd4cb3b4c6d926280fea46e2615c7a01"}, 1316 | {file = "regex-2022.9.13-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:73b985c9fc09a7896846e26d7b6f4d1fd5a20437055f4ef985d44729f9f928d0"}, 1317 | {file = "regex-2022.9.13-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c0b7cb9598795b01f9a3dd3f770ab540889259def28a3bf9b2fa24d52edecba3"}, 1318 | {file = "regex-2022.9.13-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:37e5a26e76c46f54b3baf56a6fdd56df9db89758694516413757b7d127d4c57b"}, 1319 | {file = "regex-2022.9.13-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:99945ddb4f379bb9831c05e9f80f02f079ba361a0fb1fba1fc3b267639b6bb2e"}, 1320 | {file = "regex-2022.9.13-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dcbcc9e72a791f622a32d17ff5011326a18996647509cac0609a7fc43adc229"}, 1321 | {file = "regex-2022.9.13-cp38-cp38-win32.whl", hash = "sha256:d3102ab9bf16bf541ca228012d45d88d2a567c9682a805ae2c145a79d3141fdd"}, 1322 | {file = "regex-2022.9.13-cp38-cp38-win_amd64.whl", hash = "sha256:14216ea15efc13f28d0ef1c463d86d93ca7158a79cd4aec0f9273f6d4c6bb047"}, 1323 | {file = "regex-2022.9.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9a165a05979e212b2c2d56a9f40b69c811c98a788964e669eb322de0a3e420b4"}, 1324 | {file = "regex-2022.9.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:14c71437ffb89479c89cc7022a5ea2075a842b728f37205e47c824cc17b30a42"}, 1325 | {file = "regex-2022.9.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee7045623a5ace70f3765e452528b4c1f2ce669ed31959c63f54de64fe2f6ff7"}, 1326 | {file = "regex-2022.9.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6e521d9db006c5e4a0f8acfef738399f72b704913d4e083516774eb51645ad7c"}, 1327 | {file = "regex-2022.9.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b86548b8234b2be3985dbc0b385e35f5038f0f3e6251464b827b83ebf4ed90e5"}, 1328 | {file = "regex-2022.9.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b39ee3b280e15824298b97cec3f7cbbe6539d8282cc8a6047a455b9a72c598"}, 1329 | {file = "regex-2022.9.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6e6e61e9a38b6cc60ca3e19caabc90261f070f23352e66307b3d21a24a34aaf"}, 1330 | {file = "regex-2022.9.13-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d837ccf3bd2474feabee96cd71144e991472e400ed26582edc8ca88ce259899c"}, 1331 | {file = "regex-2022.9.13-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6adfe300848d61a470ec7547adc97b0ccf86de86a99e6830f1d8c8d19ecaf6b3"}, 1332 | {file = "regex-2022.9.13-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d5b003d248e6f292475cd24b04e5f72c48412231961a675edcb653c70730e79e"}, 1333 | {file = "regex-2022.9.13-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d5edd3eb877c9fc2e385173d4a4e1d792bf692d79e25c1ca391802d36ecfaa01"}, 1334 | {file = "regex-2022.9.13-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:50e764ffbd08b06aa8c4e86b8b568b6722c75d301b33b259099f237c46b2134e"}, 1335 | {file = "regex-2022.9.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d43bd402b27e0e7eae85c612725ba1ce7798f20f6fab4e8bc3de4f263294f03"}, 1336 | {file = "regex-2022.9.13-cp39-cp39-win32.whl", hash = "sha256:7fcf7f94ccad19186820ac67e2ec7e09e0ac2dac39689f11cf71eac580503296"}, 1337 | {file = "regex-2022.9.13-cp39-cp39-win_amd64.whl", hash = "sha256:322bd5572bed36a5b39952d88e072738926759422498a96df138d93384934ff8"}, 1338 | {file = "regex-2022.9.13.tar.gz", hash = "sha256:f07373b6e56a6f3a0df3d75b651a278ca7bd357a796078a26a958ea1ce0588fd"}, 1339 | ] 1340 | requests = [ 1341 | {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, 1342 | {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, 1343 | ] 1344 | rsa = [ 1345 | {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, 1346 | {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, 1347 | ] 1348 | setuptools = [ 1349 | {file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"}, 1350 | {file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"}, 1351 | ] 1352 | six = [ 1353 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1354 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1355 | ] 1356 | sniffio = [ 1357 | {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, 1358 | {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, 1359 | ] 1360 | snowballstemmer = [ 1361 | {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, 1362 | {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, 1363 | ] 1364 | Sphinx = [ 1365 | {file = "Sphinx-3.5.4-py3-none-any.whl", hash = "sha256:2320d4e994a191f4b4be27da514e46b3d6b420f2ff895d064f52415d342461e8"}, 1366 | {file = "Sphinx-3.5.4.tar.gz", hash = "sha256:19010b7b9fa0dc7756a6e105b2aacd3a80f798af3c25c273be64d7beeb482cb1"}, 1367 | ] 1368 | sphinx-rtd-theme = [ 1369 | {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, 1370 | {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"}, 1371 | ] 1372 | sphinxcontrib-applehelp = [ 1373 | {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, 1374 | {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, 1375 | ] 1376 | sphinxcontrib-devhelp = [ 1377 | {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, 1378 | {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, 1379 | ] 1380 | sphinxcontrib-htmlhelp = [ 1381 | {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, 1382 | {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, 1383 | ] 1384 | sphinxcontrib-jsmath = [ 1385 | {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, 1386 | {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, 1387 | ] 1388 | sphinxcontrib-qthelp = [ 1389 | {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, 1390 | {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, 1391 | ] 1392 | sphinxcontrib-serializinghtml = [ 1393 | {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, 1394 | {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, 1395 | ] 1396 | starlette = [ 1397 | {file = "starlette-0.20.4-py3-none-any.whl", hash = "sha256:c0414d5a56297d37f3db96a84034d61ce29889b9eaccf65eb98a0b39441fcaa3"}, 1398 | {file = "starlette-0.20.4.tar.gz", hash = "sha256:42fcf3122f998fefce3e2c5ad7e5edbf0f02cf685d646a83a08d404726af5084"}, 1399 | ] 1400 | toml = [ 1401 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 1402 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 1403 | ] 1404 | tomli = [ 1405 | {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, 1406 | {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, 1407 | ] 1408 | tomlkit = [ 1409 | {file = "tomlkit-0.11.5-py3-none-any.whl", hash = "sha256:f2ef9da9cef846ee027947dc99a45d6b68a63b0ebc21944649505bf2e8bc5fe7"}, 1410 | {file = "tomlkit-0.11.5.tar.gz", hash = "sha256:571854ebbb5eac89abcb4a2e47d7ea27b89bf29e09c35395da6f03dd4ae23d1c"}, 1411 | ] 1412 | types-cachetools = [ 1413 | {file = "types-cachetools-0.1.10.tar.gz", hash = "sha256:f2727cf379989ba4d081f9ed11ec28c6730adbd120dc0deb2036c5ef69e515e0"}, 1414 | {file = "types_cachetools-0.1.10-py3-none-any.whl", hash = "sha256:76d312d6e55fe19f7a2ea9a3271945e6f5c56f4fd76cc3cb2366109785f3462b"}, 1415 | ] 1416 | types-requests = [ 1417 | {file = "types-requests-2.28.11.1.tar.gz", hash = "sha256:02b1806c5b9904edcd87fa29236164aea0e6cdc4d93ea020cd615ef65cb43d65"}, 1418 | {file = "types_requests-2.28.11.1-py3-none-any.whl", hash = "sha256:1ff2c1301f6fe58b5d1c66cdf631ca19734cb3b1a4bbadc878d75557d183291a"}, 1419 | ] 1420 | types-urllib3 = [ 1421 | {file = "types-urllib3-1.26.25.tar.gz", hash = "sha256:5aef0e663724eef924afa8b320b62ffef2c1736c1fa6caecfc9bc6c8ae2c3def"}, 1422 | {file = "types_urllib3-1.26.25-py3-none-any.whl", hash = "sha256:c1d78cef7bd581e162e46c20a57b2e1aa6ebecdcf01fd0713bb90978ff3e3427"}, 1423 | ] 1424 | typing-extensions = [ 1425 | {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, 1426 | {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, 1427 | ] 1428 | urllib3 = [ 1429 | {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, 1430 | {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, 1431 | ] 1432 | uvicorn = [ 1433 | {file = "uvicorn-0.15.0-py3-none-any.whl", hash = "sha256:17f898c64c71a2640514d4089da2689e5db1ce5d4086c2d53699bf99513421c1"}, 1434 | {file = "uvicorn-0.15.0.tar.gz", hash = "sha256:d9a3c0dd1ca86728d3e235182683b4cf94cd53a867c288eaeca80ee781b2caff"}, 1435 | ] 1436 | wrapt = [ 1437 | {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, 1438 | {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, 1439 | {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, 1440 | {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, 1441 | {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, 1442 | {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, 1443 | {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, 1444 | {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, 1445 | {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, 1446 | {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, 1447 | {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, 1448 | {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, 1449 | {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, 1450 | {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, 1451 | {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, 1452 | {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, 1453 | {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, 1454 | {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, 1455 | {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, 1456 | {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, 1457 | {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, 1458 | {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, 1459 | {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, 1460 | {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, 1461 | {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, 1462 | {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, 1463 | {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, 1464 | {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, 1465 | {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, 1466 | {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, 1467 | {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, 1468 | {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, 1469 | {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, 1470 | {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, 1471 | {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, 1472 | {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, 1473 | {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, 1474 | {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, 1475 | {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, 1476 | {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, 1477 | {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, 1478 | {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, 1479 | {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, 1480 | {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, 1481 | {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, 1482 | {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, 1483 | {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, 1484 | {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, 1485 | {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, 1486 | {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, 1487 | {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, 1488 | {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, 1489 | {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, 1490 | {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, 1491 | {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, 1492 | {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, 1493 | {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, 1494 | {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, 1495 | {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, 1496 | {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, 1497 | {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, 1498 | {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, 1499 | {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, 1500 | {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, 1501 | ] 1502 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "fastapi-third-party-auth" 3 | version = "0.0.0" 4 | description = "Simple library for using a third party authentication service like Keycloak or Auth0 with FastAPI" 5 | authors = ["HarryMWinters ", "Richard Löwenström "] 6 | license = "MIT" 7 | readme = "README.md" 8 | homepage = "https://github.com/aiwizo/fastapi-third-party-auth" 9 | repository = "https://github.com/aiwizo/fastapi-third-party-auth" 10 | documentation = "https://fastapi-third-party-auth.readthedocs.io/en/latest/" 11 | 12 | [tool.poetry.dependencies] 13 | python = "^3.8" 14 | fastapi = ">= 0.61.0" 15 | pydantic = ">= 1.6.1" 16 | cachetools = ">= 4.1.1" 17 | requests = ">= 2.24.0" 18 | python-jose = {extras = ["cryptography"], version = ">= 3.2.0"} 19 | 20 | [tool.poetry.dev-dependencies] 21 | pytest = "^6.0.1" 22 | black = "21.7b0" 23 | pylint = "^2.6.0" 24 | pyjwt = "^1.7.1" 25 | sphinx = "^3.3.1" 26 | mypy = "^0.910" 27 | types-cachetools = "^0.1.9" 28 | types-requests = "^2.25.0" 29 | uvicorn = "^0.15.0" 30 | sphinx-rtd-theme = "^1.0.0" 31 | 32 | [tool.poetry.extras] 33 | docs = ["sphinx"] 34 | 35 | [build-system] 36 | requires = ["poetry>=0.12"] 37 | build-backend = "poetry.masonry.api" 38 | 39 | [tool.isort] 40 | profile = "black" 41 | force_single_line = "True" 42 | known_first_party = [] 43 | known_third_party = ["app", "cachetools", "cryptography", "fastapi", "jose", "jwt", "pydantic", "pytest", "requests", "starlette", "uvicorn"] 44 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextml-code/fastapi-third-party-auth/039ee19eef78889f4a09342f18680ecccd7e7879/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import time 4 | import uuid 5 | 6 | import jwt 7 | import pytest 8 | from cryptography.hazmat.backends import default_backend 9 | from cryptography.hazmat.primitives import serialization 10 | from cryptography.hazmat.primitives.asymmetric import rsa 11 | 12 | FIXTURES_DIRECTORY = os.path.join(os.path.dirname(__file__), "fixtures") 13 | 14 | 15 | KEY = rsa.generate_private_key( 16 | backend=default_backend(), public_exponent=65537, key_size=2048 17 | ) 18 | 19 | 20 | @pytest.fixture 21 | def oidc_discovery(): 22 | with open(FIXTURES_DIRECTORY + "/AuthServerDiscovery.json") as f: 23 | OIDC_DISCOVERY_RESPONSE = json.load(f) 24 | 25 | return OIDC_DISCOVERY_RESPONSE 26 | 27 | 28 | @pytest.fixture 29 | def test_email(): 30 | return "AnticipationOfANewLoversArrivalThe@VeryLittleGravitasIndeed" 31 | 32 | 33 | @pytest.fixture 34 | def key(): 35 | # keeping the key global so it isn't regenerated with each fixture use. 36 | return KEY 37 | 38 | 39 | @pytest.fixture 40 | def private_key(key): 41 | return key.private_bytes( 42 | serialization.Encoding.PEM, 43 | serialization.PrivateFormat.PKCS8, 44 | serialization.NoEncryption(), 45 | ).decode("UTF-8") 46 | 47 | 48 | @pytest.fixture 49 | def public_key(key): 50 | return ( 51 | key.public_key() 52 | .public_bytes(serialization.Encoding.PEM, serialization.PublicFormat.PKCS1) 53 | .decode("UTF-8") 54 | ) 55 | 56 | 57 | @pytest.fixture 58 | def config_w_aud(): 59 | return { 60 | "client_id": "NeverAgain", 61 | "openid_connect_url": "WhatAreTheCivilianApplications?", 62 | "issuer": "PokeItWithAStick", 63 | "signature_cache_ttl": 6e3, 64 | } 65 | 66 | 67 | @pytest.fixture 68 | def no_audience_config(): 69 | return { 70 | "openid_connect_url": "WhatAreTheCivilianApplications?", 71 | "issuer": "PokeItWithAStick", 72 | "signature_cache_ttl": 6e3, 73 | } 74 | 75 | 76 | @pytest.fixture 77 | def token_with_audience(private_key, config_w_aud, test_email) -> str: 78 | audience: str = str(config_w_aud["client_id"]) 79 | issuer: str = str(config_w_aud["issuer"]) 80 | now = int(time.time()) 81 | 82 | return jwt.encode( 83 | { 84 | "aud": audience, 85 | "iss": issuer, 86 | "email": test_email, 87 | "name": "SweetAndFullOfGrace", 88 | "preferred_username": "Sweet", 89 | "exp": now + 30, 90 | "auth_time": now, 91 | "sub": "foo", 92 | "ver": "1", 93 | "iat": now, 94 | "jti": str(uuid.uuid4()), 95 | "amr": [], 96 | "idp": "", 97 | "nonce": "", 98 | "at_hash": "", 99 | }, 100 | private_key, 101 | algorithm="RS256", 102 | ).decode("UTF-8") 103 | 104 | 105 | @pytest.fixture 106 | def token_without_audience(private_key, no_audience_config, test_email) -> str: 107 | # Make a token where audience is client_id 108 | issuer: str = str(no_audience_config["issuer"]) 109 | now = int(time.time()) 110 | 111 | return jwt.encode( 112 | { 113 | "aud": "NoAudience", 114 | "iss": issuer, 115 | "email": test_email, 116 | "name": "SweetAndFullOfGrace", 117 | "preferred_username": "Sweet", 118 | "exp": now + 30, 119 | "auth_time": now, 120 | "sub": "foo", 121 | "ver": "1", 122 | "iat": now, 123 | "jti": str(uuid.uuid4()), 124 | "amr": [], 125 | "idp": "", 126 | "nonce": "", 127 | "at_hash": "", 128 | }, 129 | private_key, 130 | algorithm="RS256", 131 | ).decode("UTF-8") 132 | 133 | 134 | @pytest.fixture 135 | def mock_discovery(oidc_discovery, public_key): 136 | class functions: 137 | auth_server = lambda **_: oidc_discovery 138 | public_keys = lambda _: public_key 139 | signing_algos = lambda x: x["id_token_signing_alg_values_supported"] 140 | authorization_url = lambda x: x["authorization_endpoint"] 141 | token_url = lambda x: x["token_endpoint"] 142 | supported_scopes = lambda x: x["scopes_supported"] 143 | 144 | return lambda *args, **kwargs: functions 145 | -------------------------------------------------------------------------------- /tests/fixtures/AuthServerDiscovery.json: -------------------------------------------------------------------------------- 1 | { 2 | "issuer": "https://WisdomLikeSilence", 3 | "authorization_endpoint": "https://EthicsGradient/authorize", 4 | "token_endpoint": "https://EthicsGradient/token", 5 | "userinfo_endpoint": "https://EthicsGradient/userinfo", 6 | "registration_endpoint": "https://EthicsGradient/clients", 7 | "jwks_uri": "https://EthicsGradient/keys", 8 | "response_types_supported": [ 9 | "code", 10 | "code id_token", 11 | "code token", 12 | "code id_token token", 13 | "id_token", 14 | "id_token token" 15 | ], 16 | "response_modes_supported": [ 17 | "query", 18 | "fragment", 19 | "form_post", 20 | "okta_post_message" 21 | ], 22 | "grant_types_supported": [ 23 | "authorization_code", 24 | "implicit", 25 | "refresh_token", 26 | "password" 27 | ], 28 | "subject_types_supported": ["public"], 29 | "id_token_signing_alg_values_supported": ["RS256"], 30 | "scopes_supported": [ 31 | "openid", 32 | "email", 33 | "profile", 34 | "address", 35 | "phone", 36 | "offline_access", 37 | "groups" 38 | ], 39 | "token_endpoint_auth_methods_supported": [ 40 | "client_secret_basic", 41 | "client_secret_post", 42 | "client_secret_jwt", 43 | "none" 44 | ], 45 | "claims_supported": [ 46 | "iss", 47 | "ver", 48 | "sub", 49 | "aud", 50 | "iat", 51 | "exp", 52 | "jti", 53 | "auth_time", 54 | "amr", 55 | "idp", 56 | "nonce", 57 | "name", 58 | "nickname", 59 | "preferred_username", 60 | "given_name", 61 | "middle_name", 62 | "family_name", 63 | "email", 64 | "email_verified", 65 | "profile", 66 | "zoneinfo", 67 | "locale", 68 | "address", 69 | "phone_number", 70 | "picture", 71 | "website", 72 | "gender", 73 | "birthdate", 74 | "updated_at", 75 | "at_hash", 76 | "c_hash" 77 | ], 78 | "introspection_endpoint": "https://EthicsGradient/introspect", 79 | "introspection_endpoint_auth_methods_supported": [ 80 | "client_secret_basic", 81 | "client_secret_post", 82 | "client_secret_jwt", 83 | "none" 84 | ], 85 | "revocation_endpoint": "https://EthicsGradient/revoke", 86 | "revocation_endpoint_auth_methods_supported": [ 87 | "client_secret_basic", 88 | "client_secret_post", 89 | "client_secret_jwt", 90 | "none" 91 | ], 92 | "end_session_endpoint": "https://EthicsGradient/logout", 93 | "request_parameter_supported": true, 94 | "request_object_signing_alg_values_supported": ["HS256", "HS384", "HS512"] 95 | } 96 | -------------------------------------------------------------------------------- /tests/test_auth.py: -------------------------------------------------------------------------------- 1 | from fastapi.security import HTTPAuthorizationCredentials 2 | from fastapi.security import SecurityScopes 3 | 4 | import fastapi_third_party_auth 5 | from fastapi_third_party_auth import Auth 6 | from fastapi_third_party_auth.idtoken_types import IDToken 7 | 8 | 9 | def test__authenticate_user( 10 | monkeypatch, 11 | mock_discovery, 12 | token_with_audience, 13 | config_w_aud, 14 | test_email, 15 | ): 16 | monkeypatch.setattr( 17 | fastapi_third_party_auth.auth.discovery, "configure", mock_discovery 18 | ) 19 | 20 | token = token_with_audience 21 | 22 | auth = Auth(**config_w_aud) 23 | id_token = auth.required( 24 | security_scopes=SecurityScopes(scopes=[]), 25 | authorization_credentials=HTTPAuthorizationCredentials( 26 | scheme="Bearer", credentials=token 27 | ), 28 | ) 29 | 30 | assert id_token.email == test_email # nosec 31 | assert id_token.aud == config_w_aud["client_id"] 32 | 33 | 34 | def test__authenticate_user_no_aud( 35 | monkeypatch, 36 | mock_discovery, 37 | token_without_audience, 38 | no_audience_config, 39 | test_email, 40 | ): 41 | 42 | monkeypatch.setattr( 43 | fastapi_third_party_auth.auth.discovery, "configure", mock_discovery 44 | ) 45 | 46 | token = token_without_audience 47 | 48 | auth = Auth(**no_audience_config) 49 | 50 | id_token = auth.required( 51 | security_scopes=SecurityScopes(scopes=[]), 52 | authorization_credentials=HTTPAuthorizationCredentials( 53 | scheme="Bearer", credentials=token 54 | ), 55 | ) 56 | 57 | assert id_token.email == test_email # nosec 58 | 59 | 60 | def test__authenticate_user_returns_custom_tokens( 61 | monkeypatch, mock_discovery, token_without_audience, no_audience_config 62 | ): 63 | class CustomToken(IDToken): 64 | custom_field: str = "OnlySlightlyBent" 65 | 66 | monkeypatch.setattr( 67 | fastapi_third_party_auth.auth.discovery, "configure", mock_discovery 68 | ) 69 | 70 | token = token_without_audience 71 | 72 | auth = Auth( 73 | **no_audience_config, 74 | idtoken_model=CustomToken, 75 | ) 76 | 77 | custom_token = auth.required( 78 | security_scopes=SecurityScopes(scopes=[]), 79 | authorization_credentials=HTTPAuthorizationCredentials( 80 | scheme="Bearer", credentials=token 81 | ), 82 | ) 83 | 84 | assert custom_token.custom_field == "OnlySlightlyBent" 85 | -------------------------------------------------------------------------------- /tests/test_types.py: -------------------------------------------------------------------------------- 1 | import pydantic 2 | import pytest 3 | 4 | from fastapi_third_party_auth import idtoken_types 5 | 6 | 7 | def test_IDToken_raises_with_bad_field_types(): 8 | with pytest.raises(pydantic.ValidationError): 9 | idtoken_types.IDToken( 10 | iss="ClearAirTurbulence", 11 | sub="ValueJudgement", 12 | aud="SoberCounsel", 13 | exp="NowTurningToReason&ItsJustSweetness", 14 | iat="GermaneRiposte", 15 | ) 16 | 17 | 18 | def test_IDToken_only_requires_fields_in_OIDC_spec(): 19 | # Call IDToken with minimal types defined in spec 20 | assert idtoken_types.IDToken( 21 | iss="ClearAirTurbulence", 22 | sub="ValueJudgement", 23 | aud="SoberCounsel", 24 | exp=3.12, 25 | iat=42, 26 | ) 27 | 28 | 29 | def test_IDToken_takes_arbitrary_extra_fields(): 30 | assert idtoken_types.IDToken( 31 | iss="ClearAirTurbulence", 32 | sub="ValueJudgement", 33 | aud="SoberCounsel", 34 | exp=3.12, 35 | iat=42, 36 | arbitrary_extra_field="Laskuil-Hliz", 37 | ) 38 | --------------------------------------------------------------------------------