pax_global_header00006660000000000000000000000064141316030560014511gustar00rootroot0000000000000052 comment=1df8df9a0498e1d0899329fa2537a8dbf5e46deb djangorestframework-simplejwt-5.0.0/000077500000000000000000000000001413160305600176055ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/.coveragerc000066400000000000000000000002001413160305600217160ustar00rootroot00000000000000[run] omit = rest_framework_simplejwt/token_blacklist/admin.py rest_framework_simplejwt/token_blacklist/apps.py djangorestframework-simplejwt-5.0.0/.github/000077500000000000000000000000001413160305600211455ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/.github/workflows/000077500000000000000000000000001413160305600232025ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/.github/workflows/release.yml000066400000000000000000000020061413160305600253430ustar00rootroot00000000000000name: Release on: push: tags: - '*' jobs: build: if: github.repository == 'jazzband/djangorestframework-simplejwt' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v2 with: python-version: 3.8 - name: Install dependencies run: | python -m pip install -U pip python -m pip install -U setuptools twine wheel - name: Build package run: | python setup.py --version python setup.py sdist --format=gztar bdist_wheel twine check dist/* - name: Upload packages to Jazzband if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@master with: user: jazzband password: ${{ secrets.JAZZBAND_RELEASE_KEY }} repository_url: https://jazzband.co/projects/djangorestframework-simplejwt/upload djangorestframework-simplejwt-5.0.0/.github/workflows/test.yml000066400000000000000000000032201413160305600247010ustar00rootroot00000000000000name: Test on: push: branches: - main pull_request: jobs: build: name: build (Python ${{ matrix.python-version }}, Django ${{ matrix.django-version }}, DRF ${{ matrix.drf-version }}) runs-on: ubuntu-latest strategy: fail-fast: false max-parallel: 5 matrix: python-version: ['3.7', '3.8', '3.9'] django-version: ['2.2', '3.1', '3.2', 'main'] drf-version: ['3.10', '3.11', '3.12'] exclude: - python-version: '3.7' django-version: 'main' - python-version: '3.8' django-version: 'main' - django-version: '3.1' drf-version: '3.10' steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Get pip cache dir id: pip-cache run: | echo "::set-output name=dir::$(pip cache dir)" - name: Cache uses: actions/cache@v2 with: path: ${{ steps.pip-cache.outputs.dir }} key: ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }} restore-keys: | ${{ matrix.python-version }}-v1- - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install --upgrade tox tox-gh-actions - name: Tox tests run: | tox -v env: DJANGO: ${{ matrix.django-version }} DRF: ${{ matrix.drf-version }} - name: Upload coverage uses: codecov/codecov-action@v1 with: name: Python ${{ matrix.python-version }} djangorestframework-simplejwt-5.0.0/.gitignore000066400000000000000000000037701413160305600216040ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ .idea/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ djangorestframework-simplejwt-5.0.0/.isort.cfg000066400000000000000000000002431413160305600215030ustar00rootroot00000000000000[settings] skip=.tox atomic=true multi_line_output=5 known_standard_library=types,uuid,datetime,calendar,importlib,unittest,contextlib include_trailing_comma=True djangorestframework-simplejwt-5.0.0/CHANGELOG.md000066400000000000000000000243671413160305600214320ustar00rootroot00000000000000## Unreleased ## Version 5.0.0 #### Breaking * Set BLACKLIST_AFTER_ROTATION by default to False ([#455](https://github.com/jazzband/djangorestframework-simplejwt/pull/455)) #### Updates * Remove verify from jwt.decode to follow PyJWT v2.2.0 ([#472](https://github.com/jazzband/djangorestframework-simplejwt/pull/472)) * Updated import list ([#459](https://github.com/jazzband/djangorestframework-simplejwt/pull/459)) * Repair generation of OpenAPI with Spectacular ([#452](https://github.com/jazzband/djangorestframework-simplejwt/pull/452)) * Add "iat" claim to token ([#192](https://github.com/jazzband/djangorestframework-simplejwt/pull/192)) * Add blacklist view to log out users ([#306](https://github.com/jazzband/djangorestframework-simplejwt/pull/306)) * updated import list in docs ([#459](https://github.com/jazzband/djangorestframework-simplejwt/pull/459)) ## Version 4.8.0 * Add integration instructions for drf-yasg ([#145](https://github.com/jazzband/djangorestframework-simplejwt/pull/145)) * Verify Serializer Should Honour Blacklist ([#239](https://github.com/jazzband/djangorestframework-simplejwt/pull/239)) * Added missing import in getting_started docs ([#431](https://github.com/jazzband/djangorestframework-simplejwt/pull/431)) * Use import_string for token_backend ([#435](https://github.com/jazzband/djangorestframework-simplejwt/pull/435)) * Add JWKS support ([#437](https://github.com/jazzband/djangorestframework-simplejwt/pull/435)) * Use pathlib instead of open in setup.py ([#339](https://github.com/jazzband/djangorestframework-simplejwt/pull/339)) * Optimize default_user_authentication_rule ([#441](https://github.com/jazzband/djangorestframework-simplejwt/pull/441)) * Add Leeway option to decode ([#445](https://github.com/jazzband/djangorestframework-simplejwt/pull/445)) ## Version 4.7.2 * Fix BrowsableAPIRenderer needing `media_type` ([#426](https://github.com/jazzband/django-rest-framework-simplejwt/pull/426)) * Fix blacklist migrations for multiple databases ([#429](https://github.com/jazzband/django-rest-framework-simplejwt/pull/429)) * Fix Django 3.2 `default_app_config` deprecation ([#415](https://github.com/jazzband/django-rest-framework-simplejwt/pull/415)) * Fix docs specifying `INSTALLED_APPS` for SimpleJWT iff you want translations ([#420](https://github.com/jazzband/django-rest-framework-simplejwt/pull/420)) * Fix drf-yasg API Schema generation for `TokenRefreshSerializer` ([#396](https://github.com/jazzband/django-rest-framework-simplejwt/pull/396)) * Fix invalid syntax in docs for `INSTALLED_APPS` ([#416](https://github.com/jazzband/django-rest-framework-simplejwt/pull/416)) Translations: * Added Dutch translations ([#422](https://github.com/jazzband/django-rest-framework-simplejwt/pull/422)) * Added Ukrainian translations ([#423](https://github.com/jazzband/django-rest-framework-simplejwt/pull/423)) * Added Simplified Chinese translations ([#427](https://github.com/jazzband/django-rest-framework-simplejwt/pull/427)) ## Version 4.7.1 * Fixed user-generated migration file bug in token_blacklist ([#411](https://github.com/jazzband/django-rest-framework-simplejwt/pull/411)) ## Version 4.7.0 * Added support for Django 3.2 and drop Django 3.0 ([#404](https://github.com/jazzband/django-rest-framework-simplejwt/pull/404)) * Added Italian translations ([#342](https://github.com/jazzband/django-rest-framework-simplejwt/pull/342)) * Fixed DRF app registry bug, specifically `django.core.exceptions.AppRegistryNotReady` ([#331](https://github.com/jazzband/django-rest-framework-simplejwt/pull/331)) * Fixed support for PyJWT>=2.0.0 ([#376](https://github.com/jazzband/django-rest-framework-simplejwt/pull/376)) * Migrated blacklist app models to use BigAutoField IDs for Django>=3.2. ([#404](https://github.com/jazzband/django-rest-framework-simplejwt/pull/404)) ## Version 4.6 * Added support for PyJWT>=2.0.0 ([#329](https://github.com/jazzband/django-rest-framework-simplejwt/pull/329)) * Restored Python 3.7 support ([#332](https://github.com/jazzband/django-rest-framework-simplejwt/pull/332)) * Fixed Django 4.0 re_path deprecation ([#280](https://github.com/jazzband/django-rest-framework-simplejwt/pull/280)) Translations: * Added Indonesian translations ([#316](https://github.com/jazzband/django-rest-framework-simplejwt/pull/316)) ## Version 4.5 * Added `AUTH_HEADER_NAME` to settings ([#309](https://github.com/jazzband/django-rest-framework-simplejwt/pull/309)) * Added `USER_AUTHENTICATION_RULE` to settings ([#279](https://github.com/jazzband/django-rest-framework-simplejwt/pull/279)) * Added `UPDATE_LAST_LOGIN` to settings ([#238](https://github.com/jazzband/django-rest-framework-simplejwt/pull/238)) * Fixed packaging of locale folder for installation ([#117](https://github.com/jazzband/django-rest-framework-simplejwt/pull/117)) * Allowed TokenUser to be configurable ([#172](https://github.com/jazzband/django-rest-framework-simplejwt/pull/172)) * Dropped Python 3.7 and below (restored Python 3.7 but not 3.6 in next version) * Improved error message if cryptography isn't installed when developer tries to use a certain algorithm that needs the package ([#285](https://github.com/jazzband/django-rest-framework-simplejwt/pull/285)) * Fixed Django 4.0 ugettext_lazy deprecation warnings ([#186](https://github.com/jazzband/django-rest-framework-simplejwt/pull/186)) * Remove upper bound of Python version ([#225](https://github.com/jazzband/django-rest-framework-simplejwt/pull/225)) * Added DRF 3.11 support ([#230](https://github.com/jazzband/django-rest-framework-simplejwt/pull/230)) Translations: * Added French translations ([#314](https://github.com/jazzband/django-rest-framework-simplejwt/pull/314)) * Added Spanish translations ([#294](https://github.com/jazzband/django-rest-framework-simplejwt/pull/294)) * Added Argentinian Spanish translations ([#244](https://github.com/jazzband/django-rest-framework-simplejwt/pull/244)) * Added Persian translations ([#220](https://github.com/jazzband/django-rest-framework-simplejwt/pull/220)) * Added German translations ([#198](https://github.com/jazzband/django-rest-framework-simplejwt/pull/198)) * Added Czech translations ([#188](https://github.com/jazzband/django-rest-framework-simplejwt/pull/188)) * Added Polish translations ([#166](https://github.com/jazzband/django-rest-framework-simplejwt/pull/166)) * Fixed incorrect language encoding from de_CH to es_CL ([#299](https://github.com/jazzband/django-rest-framework-simplejwt/pull/299)) ## Version 4.4 * Added official support for Python 3.8 and Django 3.0. * Added settings for expected audience and issuer claims. * Documentation updates. * Updated package/python version support (check the README to see what new versions are supported and what old ones are no longer supported!) * Added Chilean Spanish language support. * Added Russian language support. ## Version 4.3 * Added `JTI_CLAIM` setting to allow storing token identifiers under a different claim. ## Version 4.2 * We now return HTTP 401 for user not found or inactive. ## Version 4.1.5 * Restricted `setup.py` config to Python 3 only. ## Version 4.1.4 * Included translation files in release package. ## Version 4.1.3 * Updated `python-jose` version requirement. ## Version 4.1.2 * Fixed `KeyError` in `TokenObtainSerializer.validate`. ## Version 4.1.1 * Added request pass-through on `django.contrib.auth.authenticate` call in `TokenObtainSerializer`. * Updated `TokenObtainSerializer` to use `fail` API from parent class. ## Version 4.1 * Added language support for Brazilian Portuguese. * Added support for automatic username lookup in `TokenUser`. ## Version 4.0 * Removed Python 2 support. * Fixed crash when empty AUTHORIZATION header is sent. * Fixed testing DB transaction issues. * Simplified/improved testing and dev setup. * Switched to using bumpversion for release process. ## Version 3.3 * Removed official support for Python 3.4. * Added support for Python 3.7. * Added support for Django 2.1. * Added support for DRF 3.9. ## Version 3.2.3 * Fixed issue with `WWW-Authenticate` header not being included in 401 responses. ## Version 3.2.2 * Added missing method `get` on `Token` base class. ## Version 3.2.1 * Simplified some blacklist app code. * Resolved possible race condition. ## Version 3.2 * Added ``TokenObtainSerializer.get_token`` method to facilitate customization of token claims. * Added ``TokenVerifyView`` to allow verification of HMAC-signed tokens by API users who have no access to the signing key. * Renamed ``AUTH_HEADER_TYPE`` setting to ``AUTH_HEADER_TYPES``. This setting now contains either a single valid auth header type or a list or tuple of valid auth header types. If authentication fails, and more than one string is present in this tuple or list, the first item in the list will be used to build the "WWW-Authenticate" header in the response. ## Version 3.1 * Moved handling of TokenError exceptions from inside of serializer `validate` methods into token view `post` methods. ## Version 3.0 * Added support for refresh token rotation via ``ROTATE_REFRESH_TOKENS`` and ``BLACKLIST_AFTER_ROTATION`` settings. See README for details. * Added `BlacklistMixin.blacklist` method to make it easier to blacklist tokens regardless of whether or not they are present in the outstanding token list. * In token blacklist app, changed `OutstandingToken.jti` field to char field to better reflect JWT spec. * Renamed `AUTH_TOKEN_CLASS` setting to `AUTH_TOKEN_CLASSES`. This setting now specifies a list of token classes (or class paths) which are used to verify tokens which are submitted for authorization. This will hopefully help anyone wishing to gradually migrate between using different token types. * Removed support for extensible JWT backends. We're just going to use PyJWT exclusively to simplify things. * Added support for more crypto algorithms. All HMAC and RSA variants from PyJWT now supported. * Renamed `SECRET_KEY` setting to `SIGNING_KEY`. * The renamed `SIGNING_KEY` setting now acts doubly as a symmetric signing/verification key for HMAC algorithms and as a private key for RSA algorithms. * Added `VERIFYING_KEY` setting for use with RSA algorithms. * Removed undocumented `TOKEN_BACKEND_CLASS` setting. ## Version 2.1 * Switched to using [PyJWT](https://github.com/jpadilla/pyjwt) as the underlying library for signing and verifying tokens. djangorestframework-simplejwt-5.0.0/CONTRIBUTING.rst000066400000000000000000000005221413160305600222450ustar00rootroot00000000000000.. image:: https://jazzband.co/static/img/jazzband.svg :target: https://jazzband.co/ :alt: Jazzband This is a `Jazzband `_ project. By contributing you agree to abide by the `Contributor Code of Conduct `_ and follow the `guidelines `_. djangorestframework-simplejwt-5.0.0/LICENSE.txt000066400000000000000000000020351413160305600214300ustar00rootroot00000000000000Copyright 2017 David Sanders Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. djangorestframework-simplejwt-5.0.0/MANIFEST.in000066400000000000000000000003221413160305600213400ustar00rootroot00000000000000include README.rst include LICENSE.txt recursive-include rest_framework_simplejwt/locale *.mo recursive-include rest_framework_simplejwt/locale *.po recursive-exclude * __pycache__ recursive-exclude * *.py[co] djangorestframework-simplejwt-5.0.0/Makefile000066400000000000000000000023421413160305600212460ustar00rootroot00000000000000.PHONY: clean clean: clean-build clean-pyc .PHONY: clean-build clean-build: rm -fr build/ rm -fr dist/ rm -fr *.egg-info .PHONY: clean-pyc clean-pyc: find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + find . -name '*~' -exec rm -f {} + .PHONY: lint lint: tox -e lint .PHONY: lint-roll lint-roll: isort rest_framework_simplejwt tests $(MAKE) lint .PHONY: tests test: pytest tests .PHONY: test-all test-all: tox .PHONY: build-docs build-docs: sphinx-apidoc -o docs/ . \ setup.py \ *confest* \ tests/* \ rest_framework_simplejwt/token_blacklist/* \ rest_framework_simplejwt/backends.py \ rest_framework_simplejwt/compat.py \ rest_framework_simplejwt/exceptions.py \ rest_framework_simplejwt/settings.py \ rest_framework_simplejwt/state.py $(MAKE) -C docs clean $(MAKE) -C docs html $(MAKE) -C docs doctest .PHONY: docs docs: build-docs open docs/_build/html/index.html .PHONY: linux-docs linux-docs: build-docs xdg-open docs/_build/html/index.html .PHONY: pushversion pushversion: git push upstream && git push upstream --tags .PHONY: publish publish: python setup.py sdist bdist_wheel twine upload dist/* .PHONY: dist dist: clean python setup.py sdist bdist_wheel ls -l dist djangorestframework-simplejwt-5.0.0/README.rst000066400000000000000000000030541413160305600212760ustar00rootroot00000000000000Simple JWT ========== .. image:: https://jazzband.co/static/img/badge.svg :target: https://jazzband.co/ :alt: Jazzband .. image:: https://github.com/jazzband/djangorestframework-simplejwt/workflows/Test/badge.svg :target: https://github.com/jazzband/djangorestframework-simplejwt/actions :alt: GitHub Actions .. image:: https://codecov.io/gh/jazzband/djangorestframework-simplejwt/branch/master/graph/badge.svg :target: https://codecov.io/gh/jazzband/djangorestframework-simplejwt .. image:: https://img.shields.io/pypi/v/djangorestframework-simplejwt.svg :target: https://pypi.python.org/pypi/djangorestframework-simplejwt .. image:: https://img.shields.io/pypi/pyversions/djangorestframework-simplejwt.svg :target: https://pypi.python.org/pypi/djangorestframework-simplejwt .. image:: https://img.shields.io/pypi/djversions/djangorestframework-simplejwt.svg :target: https://pypi.python.org/pypi/djangorestframework-simplejwt .. image:: https://readthedocs.org/projects/django-rest-framework-simplejwt/badge/?version=latest :target: https://django-rest-framework-simplejwt.readthedocs.io/en/latest/ Abstract -------- Simple JWT is a JSON Web Token authentication plugin for the `Django REST Framework `__. For full documentation, visit `django-rest-framework-simplejwt.readthedocs.io `__. Looking for Maintainers ----------------------- For more information, see `here `__. djangorestframework-simplejwt-5.0.0/codecov.yml000066400000000000000000000001311413160305600217450ustar00rootroot00000000000000coverage: status: project: false patch: false changes: false comment: off djangorestframework-simplejwt-5.0.0/docs/000077500000000000000000000000001413160305600205355ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/docs/Makefile000066400000000000000000000152621413160305600222030ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/rest_framework_simplejwt.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/rest_framework_simplejwt.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/rest_framework_simplejwt" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/rest_framework_simplejwt" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." djangorestframework-simplejwt-5.0.0/docs/blacklist_app.rst000066400000000000000000000034711413160305600241040ustar00rootroot00000000000000.. _blacklist_app: Blacklist app ============= Simple JWT includes an app that provides token blacklist functionality. To use this app, include it in your list of installed apps in ``settings.py``: .. code-block:: python # Django project settings.py ... INSTALLED_APPS = ( ... 'rest_framework_simplejwt.token_blacklist', ... ) Also, make sure to run ``python manage.py migrate`` to run the app's migrations. If the blacklist app is detected in ``INSTALLED_APPS``, Simple JWT will add any generated refresh or sliding tokens to a list of outstanding tokens. It will also check that any refresh or sliding token does not appear in a blacklist of tokens before it considers it as valid. The Simple JWT blacklist app implements its outstanding and blacklisted token lists using two models: ``OutstandingToken`` and ``BlacklistedToken``. Model admins are defined for both of these models. To add a token to the blacklist, find its corresponding ``OutstandingToken`` record in the admin and use the admin again to create a ``BlacklistedToken`` record that points to the ``OutstandingToken`` record. Alternatively, you can blacklist a token by creating a ``BlacklistMixin`` subclass instance and calling the instance's ``blacklist`` method: .. code-block:: python from rest_framework_simplejwt.tokens import RefreshToken token = RefreshToken(base64_encoded_token_string) token.blacklist() This will create unique outstanding token and blacklist records for the token's "jti" claim or whichever claim is specified by the ``JTI_CLAIM`` setting. The blacklist app also provides a management command, ``flushexpiredtokens``, which will delete any tokens from the outstanding list and blacklist that have expired. You should set up a cron job on your server or hosting platform which runs this command daily. djangorestframework-simplejwt-5.0.0/docs/conf.py000066400000000000000000000223411413160305600220360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Simple JWT documentation build configuration file, created by # sphinx-quickstart on Thu Oct 16 20:43:24 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. from pkg_resources import get_distribution # -- General configuration ------------------------------------------------ def django_configure(): from django.conf import settings settings.configure( SECRET_KEY='a random key to use', INSTALLED_APPS=( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.staticfiles', 'rest_framework', 'rest_framework_simplejwt', 'rest_framework_simplejwt.token_blacklist', ), ) try: import django django.setup() except AttributeError: pass django_configure() # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Simple JWT' copyright = '2020, David Sanders' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The full version, including alpha/beta/rc tags. release = get_distribution("djangorestframework_simplejwt").version # The short X.Y version. version = ".".join(release.split(".")[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [ '_build', 'modules.rst', ] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'rest_framework_simplejwtdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'rest_framework_simplejwt.tex', 'Simple JWT Documentation', 'David Sanders', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'rest_framework_simplejwt', 'Simple JWT Documentation', ['David Sanders'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Simple JWT', 'Simple JWT', 'David Sanders', 'Simple JWT', 'A JSON Web Token authentication plugin for the Django REST Framework.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # -- Intersphinx configuration ------------------------------------------------ intersphinx_mapping = { 'python': ('https://docs.python.org/3.8', None), } # -- Doctest configuration ---------------------------------------- import doctest doctest_default_flags = (0 | doctest.DONT_ACCEPT_TRUE_FOR_1 | doctest.ELLIPSIS | doctest.IGNORE_EXCEPTION_DETAIL | doctest.NORMALIZE_WHITESPACE ) djangorestframework-simplejwt-5.0.0/docs/creating_tokens_manually.rst000066400000000000000000000012701413160305600263500ustar00rootroot00000000000000.. _creating_tokens_manually: Creating tokens manually ======================== Sometimes, you may wish to manually create a token for a user. This could be done as follows: .. code-block:: python from rest_framework_simplejwt.tokens import RefreshToken def get_tokens_for_user(user): refresh = RefreshToken.for_user(user) return { 'refresh': str(refresh), 'access': str(refresh.access_token), } The above function ``get_tokens_for_user`` will return the serialized representations of new refresh and access tokens for the given user. In general, a token for any subclass of ``rest_framework_simplejwt.tokens.Token`` can be created in this way. djangorestframework-simplejwt-5.0.0/docs/customizing_token_claims.rst000066400000000000000000000025411413160305600263740ustar00rootroot00000000000000.. _customizing_token_claims: Customizing token claims ======================== If you wish to customize the claims contained in web tokens which are generated by the ``TokenObtainPairView`` and ``TokenObtainSlidingView`` views, create a subclass for the desired view as well as a subclass for its corresponding serializer. Here's an example of how to customize the claims in tokens generated by the ``TokenObtainPairView``: .. code-block:: python from rest_framework_simplejwt.serializers import TokenObtainPairSerializer from rest_framework_simplejwt.views import TokenObtainPairView class MyTokenObtainPairSerializer(TokenObtainPairSerializer): @classmethod def get_token(cls, user): token = super().get_token(user) # Add custom claims token['name'] = user.name # ... return token class MyTokenObtainPairView(TokenObtainPairView): serializer_class = MyTokenObtainPairSerializer Note that the example above will cause the customized claims to be present in both refresh *and* access tokens which are generated by the view. This follows from the fact that the ``get_token`` method above produces the *refresh* token for the view, which is in turn used to generate the view's access token. As with the standard token views, you'll also need to include a url route to your subclassed view. djangorestframework-simplejwt-5.0.0/docs/development_and_contributing.rst000066400000000000000000000021561413160305600272260ustar00rootroot00000000000000.. _development_and_contributing: Development and contributing ============================ To do development work for Simple JWT, make your own fork on Github, clone it locally, make and activate a virtualenv for it, then from within the project directory: .. code-block:: bash pip install --upgrade pip setuptools pip install -e .[dev] If you're running a Mac and/or with zsh, you need to escape the brackets: .. code-block:: bash pip install -e .\[dev\] To run the tests: .. code-block:: bash pytest To run the tests in all supported environments with tox, first `install pyenv `__. Next, install the relevant Python minor versions and create a ``.python-version`` file in the project directory: .. code-block:: bash pyenv install 3.9.x pyenv install 3.8.x cat > .python-version <`__. ------------------------------------------------------------------------------- Simple JWT provides a JSON Web Token authentication backend for the Django REST Framework. It aims to cover the most common use cases of JWTs by offering a conservative set of default features. It also aims to be easily extensible in case a desired feature is not present. Acknowledgments --------------- This project borrows code from the `Django REST Framework `__ as well as concepts from the implementation of another JSON web token library for the Django REST Framework, `django-rest-framework-jwt `__. The licenses from both of those projects have been included in this repository in the "licenses" directory. Contents -------- .. toctree:: :maxdepth: 3 getting_started settings customizing_token_claims creating_tokens_manually token_types blacklist_app experimental_features development_and_contributing drf_yasg_integration rest_framework_simplejwt Indices and tables ------------------ * :ref:`genindex` * :ref:`modindex` djangorestframework-simplejwt-5.0.0/docs/rest_framework_simplejwt.rst000066400000000000000000000025611413160305600264230ustar00rootroot00000000000000rest\_framework\_simplejwt package ================================== Submodules ---------- rest\_framework\_simplejwt.authentication module ------------------------------------------------ .. automodule:: rest_framework_simplejwt.authentication :members: :undoc-members: :show-inheritance: rest\_framework\_simplejwt.models module ---------------------------------------- .. automodule:: rest_framework_simplejwt.models :members: :undoc-members: :show-inheritance: rest\_framework\_simplejwt.serializers module --------------------------------------------- .. automodule:: rest_framework_simplejwt.serializers :members: :undoc-members: :show-inheritance: rest\_framework\_simplejwt.tokens module ---------------------------------------- .. automodule:: rest_framework_simplejwt.tokens :members: :undoc-members: :show-inheritance: rest\_framework\_simplejwt.utils module --------------------------------------- .. automodule:: rest_framework_simplejwt.utils :members: :undoc-members: :show-inheritance: rest\_framework\_simplejwt.views module --------------------------------------- .. automodule:: rest_framework_simplejwt.views :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: rest_framework_simplejwt :members: :undoc-members: :show-inheritance: djangorestframework-simplejwt-5.0.0/docs/settings.rst000066400000000000000000000237711413160305600231410ustar00rootroot00000000000000.. _settings: Settings ======== Some of Simple JWT's behavior can be customized through settings variables in ``settings.py``: .. code-block:: python # Django project settings.py from datetime import timedelta from django.conf import settings ... SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), 'ROTATE_REFRESH_TOKENS': False, 'BLACKLIST_AFTER_ROTATION': False, 'UPDATE_LAST_LOGIN': False, 'ALGORITHM': 'HS256', 'SIGNING_KEY': settings.SECRET_KEY, 'VERIFYING_KEY': None, 'AUDIENCE': None, 'ISSUER': None, 'JWK_URL': None, 'LEEWAY': 0, 'AUTH_HEADER_TYPES': ('Bearer',), 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', 'USER_ID_FIELD': 'id', 'USER_ID_CLAIM': 'user_id', 'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule', 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), 'TOKEN_TYPE_CLAIM': 'token_type', 'JTI_CLAIM': 'jti', 'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', 'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5), 'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1), } Above, the default values for these settings are shown. ``ACCESS_TOKEN_LIFETIME`` ------------------------- A ``datetime.timedelta`` object which specifies how long access tokens are valid. This ``timedelta`` value is added to the current UTC time during token generation to obtain the token's default "exp" claim value. ``REFRESH_TOKEN_LIFETIME`` -------------------------- A ``datetime.timedelta`` object which specifies how long refresh tokens are valid. This ``timedelta`` value is added to the current UTC time during token generation to obtain the token's default "exp" claim value. ``ROTATE_REFRESH_TOKENS`` ------------------------- When set to ``True``, if a refresh token is submitted to the ``TokenRefreshView``, a new refresh token will be returned along with the new access token. This new refresh token will be supplied via a "refresh" key in the JSON response. New refresh tokens will have a renewed expiration time which is determined by adding the timedelta in the ``REFRESH_TOKEN_LIFETIME`` setting to the current time when the request is made. If the blacklist app is in use and the ``BLACKLIST_AFTER_ROTATION`` setting is set to ``True``, refresh tokens submitted to the refresh view will be added to the blacklist. ``BLACKLIST_AFTER_ROTATION`` ---------------------------- When set to ``True``, causes refresh tokens submitted to the ``TokenRefreshView`` to be added to the blacklist if the blacklist app is in use and the ``ROTATE_REFRESH_TOKENS`` setting is set to ``True``. You need to add ``'rest_framework_simplejwt.token_blacklist',`` to your ``INSTALLED_APPS`` in the settings file to use this setting. Learn more about :doc:`/blacklist_app`. ``UPDATE_LAST_LOGIN`` ---------------------------- When set to ``True``, last_login field in the auth_user table is updated upon login (TokenObtainPairView). Warning: Updating last_login will dramatically increase the number of database transactions. People abusing the views could slow the server and this could be a security vulnerability. If you really want this, throttle the endpoint with DRF at the very least. ``ALGORITHM`` ------------- The algorithm from the PyJWT library which will be used to perform signing/verification operations on tokens. To use symmetric HMAC signing and verification, the following algorithms may be used: ``'HS256'``, ``'HS384'``, ``'HS512'``. When an HMAC algorithm is chosen, the ``SIGNING_KEY`` setting will be used as both the signing key and the verifying key. In that case, the ``VERIFYING_KEY`` setting will be ignored. To use asymmetric RSA signing and verification, the following algorithms may be used: ``'RS256'``, ``'RS384'``, ``'RS512'``. When an RSA algorithm is chosen, the ``SIGNING_KEY`` setting must be set to a string that contains an RSA private key. Likewise, the ``VERIFYING_KEY`` setting must be set to a string that contains an RSA public key. ``SIGNING_KEY`` --------------- The signing key that is used to sign the content of generated tokens. For HMAC signing, this should be a random string with at least as many bits of data as is required by the signing protocol. For RSA signing, this should be a string that contains an RSA private key that is 2048 bits or longer. Since Simple JWT defaults to using 256-bit HMAC signing, the ``SIGNING_KEY`` setting defaults to the value of the ``SECRET_KEY`` setting for your django project. Although this is the most reasonable default that Simple JWT can provide, it is recommended that developers change this setting to a value that is independent from the django project secret key. This will make changing the signing key used for tokens easier in the event that it is compromised. ``VERIFYING_KEY`` ----------------- The verifying key which is used to verify the content of generated tokens. If an HMAC algorithm has been specified by the ``ALGORITHM`` setting, the ``VERIFYING_KEY`` setting will be ignored and the value of the ``SIGNING_KEY`` setting will be used. If an RSA algorithm has been specified by the ``ALGORITHM`` setting, the ``VERIFYING_KEY`` setting must be set to a string that contains an RSA public key. ``AUDIENCE`` ------------- The audience claim to be included in generated tokens and/or validated in decoded tokens. When set to ``None``, this field is excluded from tokens and is not validated. ``ISSUER`` ---------- The issuer claim to be included in generated tokens and/or validated in decoded tokens. When set to ``None``, this field is excluded from tokens and is not validated. ``JWK_URL`` ---------- The JWK_URL is used to dynamically resolve the public keys needed to verify the signing of tokens. When using Auth0 for example you might set this to 'https://yourdomain.auth0.com/.well-known/jwks.json'. When set to ``None``, this field is excluded from the token backend and is not used during validation. ``LEEWAY`` ---------- Leeway is used to give some margin to the expiration time. This can be an integer for seconds or a ``datetime.timedelta``. Please reference https://pyjwt.readthedocs.io/en/latest/usage.html#expiration-time-claim-exp for more information. ``AUTH_HEADER_TYPES`` --------------------- The authorization header type(s) that will be accepted for views that require authentication. For example, a value of ``'Bearer'`` means that views requiring authentication would look for a header with the following format: ``Authorization: Bearer ``. This setting may also contain a list or tuple of possible header types (e.g. ``('Bearer', 'JWT')``). If a list or tuple is used in this way, and authentication fails, the first item in the collection will be used to build the "WWW-Authenticate" header in the response. ``AUTH_HEADER_NAME`` ---------------------------- The authorization header name to be used for authentication. The default is ``HTTP_AUTHORIZATION`` which will accept the ``Authorization`` header in the request. For example if you'd like to use ``X_Access_Token`` in the header of your requests please specify the ``AUTH_HEADER_NAME`` to be ``HTTP_X_ACCESS_TOKEN`` in your settings. ``USER_ID_FIELD`` ----------------- The database field from the user model that will be included in generated tokens to identify users. It is recommended that the value of this setting specifies a field that does not normally change once its initial value is chosen. For example, specifying a "username" or "email" field would be a poor choice since an account's username or email might change depending on how account management in a given service is designed. This could allow a new account to be created with an old username while an existing token is still valid which uses that username as a user identifier. ``USER_ID_CLAIM`` ----------------- The claim in generated tokens which will be used to store user identifiers. For example, a setting value of ``'user_id'`` would mean generated tokens include a "user_id" claim that contains the user's identifier. ``USER_AUTHENTICATION_RULE`` ---------------------------- Callable to determine if the user is permitted to authenticate. This rule is applied after a valid token is processed. The user object is passed to the callable as an argument. The default rule is to check that the ``is_active`` flag is still ``True``. The callable must return a boolean, ``True`` if authorized, ``False`` otherwise resulting in a 401 status code. ``AUTH_TOKEN_CLASSES`` ---------------------- A list of dot paths to classes that specify the types of token that are allowed to prove authentication. More about this in the "Token types" section below. ``TOKEN_TYPE_CLAIM`` -------------------- The claim name that is used to store a token's type. More about this in the "Token types" section below. ``JTI_CLAIM`` ------------- The claim name that is used to store a token's unique identifier. This identifier is used to identify revoked tokens in the blacklist app. It may be necessary in some cases to use another claim besides the default "jti" claim to store such a value. ``SLIDING_TOKEN_LIFETIME`` -------------------------- A ``datetime.timedelta`` object which specifies how long sliding tokens are valid to prove authentication. This ``timedelta`` value is added to the current UTC time during token generation to obtain the token's default "exp" claim value. More about this in the "Sliding tokens" section below. ``SLIDING_TOKEN_REFRESH_LIFETIME`` ---------------------------------- A ``datetime.timedelta`` object which specifies how long sliding tokens are valid to be refreshed. This ``timedelta`` value is added to the current UTC time during token generation to obtain the token's default "exp" claim value. More about this in the "Sliding tokens" section below. ``SLIDING_TOKEN_REFRESH_EXP_CLAIM`` ----------------------------------- The claim name that is used to store the expiration time of a sliding token's refresh period. More about this in the "Sliding tokens" section below. djangorestframework-simplejwt-5.0.0/docs/token_types.rst000066400000000000000000000055171413160305600236430ustar00rootroot00000000000000.. _token_types: Token types =========== Simple JWT provides two different token types that can be used to prove authentication. In a token's payload, its type can be identified by the value of its token type claim, which is "token_type" by default. This may have a value of "access", "sliding", or "refresh" however refresh tokens are not considered valid for authentication at this time. The claim name used to store the type can be customized by changing the ``TOKEN_TYPE_CLAIM`` setting. By default, Simple JWT expects an "access" token to prove authentication. The allowed auth token types are determined by the value of the ``AUTH_TOKEN_CLASSES`` setting. This setting contains a list of dot paths to token classes. It includes the ``'rest_framework_simplejwt.tokens.AccessToken'`` dot path by default but may also include the ``'rest_framework_simplejwt.tokens.SlidingToken'`` dot path. Either or both of those dot paths may be present in the list of auth token classes. If they are both present, then both of those token types may be used to prove authentication. Sliding tokens -------------- Sliding tokens offer a more convenient experience to users of tokens with the trade-offs of being less secure and, in the case that the blacklist app is being used, less performant. A sliding token is one which contains both an expiration claim and a refresh expiration claim. As long as the timestamp in a sliding token's expiration claim has not passed, it can be used to prove authentication. Additionally, as long as the timestamp in its refresh expiration claim has not passed, it may also be submitted to a refresh view to get another copy of itself with a renewed expiration claim. If you want to use sliding tokens, change the ``AUTH_TOKEN_CLASSES`` setting to ``('rest_framework_simplejwt.tokens.SlidingToken',)``. (Alternatively, the ``AUTH_TOKEN_CLASSES`` setting may include dot paths to both the ``AccessToken`` and ``SlidingToken`` token classes in the ``rest_framework_simplejwt.tokens`` module if you want to allow both token types to be used for authentication.) Also, include urls for the sliding token specific ``TokenObtainSlidingView`` and ``TokenRefreshSlidingView`` views alongside or in place of urls for the access token specific ``TokenObtainPairView`` and ``TokenRefreshView`` views: .. code-block:: python from rest_framework_simplejwt.views import ( TokenObtainSlidingView, TokenRefreshSlidingView, ) urlpatterns = [ ... path('api/token/', TokenObtainSlidingView.as_view(), name='token_obtain'), path('api/token/refresh/', TokenRefreshSlidingView.as_view(), name='token_refresh'), ... ] Be aware that, if you are using the blacklist app, Simple JWT will validate all sliding tokens against the blacklist for each authenticated request. This will reduce the performance of authenticated API views. djangorestframework-simplejwt-5.0.0/licenses/000077500000000000000000000000001413160305600214125ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/licenses/LICENSE-django-rest-framework-jwt000066400000000000000000000020711413160305600274270ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014-2017 Blimp LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. djangorestframework-simplejwt-5.0.0/licenses/LICENSE-django-rest-framework.md000066400000000000000000000024241413160305600272260ustar00rootroot00000000000000# License Copyright (c) 2011-2017, Tom Christie All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. djangorestframework-simplejwt-5.0.0/pytest.ini000066400000000000000000000002471413160305600216410ustar00rootroot00000000000000[pytest] addopts= -v --showlocals --durations 10 python_paths= . xfail_strict=true [pytest-watch] runner= pytest --failed-first --maxfail=1 --no-success-flaky-report djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/000077500000000000000000000000001413160305600247355ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/__init__.py000066400000000000000000000003461413160305600270510ustar00rootroot00000000000000from pkg_resources import DistributionNotFound, get_distribution try: __version__ = get_distribution("djangorestframework_simplejwt").version except DistributionNotFound: # package is not installed __version__ = None djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/authentication.py000066400000000000000000000116561413160305600303370ustar00rootroot00000000000000from django.contrib.auth import get_user_model from django.utils.translation import gettext_lazy as _ from rest_framework import HTTP_HEADER_ENCODING, authentication from .exceptions import AuthenticationFailed, InvalidToken, TokenError from .settings import api_settings AUTH_HEADER_TYPES = api_settings.AUTH_HEADER_TYPES if not isinstance(api_settings.AUTH_HEADER_TYPES, (list, tuple)): AUTH_HEADER_TYPES = (AUTH_HEADER_TYPES,) AUTH_HEADER_TYPE_BYTES = set( h.encode(HTTP_HEADER_ENCODING) for h in AUTH_HEADER_TYPES ) class JWTAuthentication(authentication.BaseAuthentication): """ An authentication plugin that authenticates requests through a JSON web token provided in a request header. """ www_authenticate_realm = 'api' media_type = 'application/json' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.user_model = get_user_model() def authenticate(self, request): header = self.get_header(request) if header is None: return None raw_token = self.get_raw_token(header) if raw_token is None: return None validated_token = self.get_validated_token(raw_token) return self.get_user(validated_token), validated_token def authenticate_header(self, request): return '{0} realm="{1}"'.format( AUTH_HEADER_TYPES[0], self.www_authenticate_realm, ) def get_header(self, request): """ Extracts the header containing the JSON web token from the given request. """ header = request.META.get(api_settings.AUTH_HEADER_NAME) if isinstance(header, str): # Work around django test client oddness header = header.encode(HTTP_HEADER_ENCODING) return header def get_raw_token(self, header): """ Extracts an unvalidated JSON web token from the given "Authorization" header value. """ parts = header.split() if len(parts) == 0: # Empty AUTHORIZATION header sent return None if parts[0] not in AUTH_HEADER_TYPE_BYTES: # Assume the header does not contain a JSON web token return None if len(parts) != 2: raise AuthenticationFailed( _('Authorization header must contain two space-delimited values'), code='bad_authorization_header', ) return parts[1] def get_validated_token(self, raw_token): """ Validates an encoded JSON web token and returns a validated token wrapper object. """ messages = [] for AuthToken in api_settings.AUTH_TOKEN_CLASSES: try: return AuthToken(raw_token) except TokenError as e: messages.append({'token_class': AuthToken.__name__, 'token_type': AuthToken.token_type, 'message': e.args[0]}) raise InvalidToken({ 'detail': _('Given token not valid for any token type'), 'messages': messages, }) def get_user(self, validated_token): """ Attempts to find and return a user using the given validated token. """ try: user_id = validated_token[api_settings.USER_ID_CLAIM] except KeyError: raise InvalidToken(_('Token contained no recognizable user identification')) try: user = self.user_model.objects.get(**{api_settings.USER_ID_FIELD: user_id}) except self.user_model.DoesNotExist: raise AuthenticationFailed(_('User not found'), code='user_not_found') if not user.is_active: raise AuthenticationFailed(_('User is inactive'), code='user_inactive') return user class JWTTokenUserAuthentication(JWTAuthentication): def get_user(self, validated_token): """ Returns a stateless user object which is backed by the given validated token. """ if api_settings.USER_ID_CLAIM not in validated_token: # The TokenUser class assumes tokens will have a recognizable user # identifier claim. raise InvalidToken(_('Token contained no recognizable user identification')) return api_settings.TOKEN_USER_CLASS(validated_token) def default_user_authentication_rule(user): # Prior to Django 1.10, inactive users could be authenticated with the # default `ModelBackend`. As of Django 1.10, the `ModelBackend` # prevents inactive users from authenticating. App designers can still # allow inactive users to authenticate by opting for the new # `AllowAllUsersModelBackend`. However, we explicitly prevent inactive # users from authenticating to enforce a reasonable policy and provide # sensible backwards compatibility with older Django versions. return user is not None and user.is_active djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/backends.py000066400000000000000000000064211413160305600270640ustar00rootroot00000000000000from django.utils.translation import gettext_lazy as _ import jwt from jwt import InvalidAlgorithmError, InvalidTokenError, PyJWKClient, algorithms from .exceptions import TokenBackendError from .utils import format_lazy ALLOWED_ALGORITHMS = ( 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', ) class TokenBackend: def __init__( self, algorithm, signing_key=None, verifying_key=None, audience=None, issuer=None, jwk_url: str = None, leeway=0, ): self._validate_algorithm(algorithm) self.algorithm = algorithm self.signing_key = signing_key self.audience = audience self.issuer = issuer self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None self.leeway = leeway if algorithm.startswith("HS"): self.verifying_key = signing_key else: self.verifying_key = verifying_key def _validate_algorithm(self, algorithm): """ Ensure that the nominated algorithm is recognized, and that cryptography is installed for those algorithms that require it """ if algorithm not in ALLOWED_ALGORITHMS: raise TokenBackendError(format_lazy(_("Unrecognized algorithm type '{}'"), algorithm)) if algorithm in algorithms.requires_cryptography and not algorithms.has_crypto: raise TokenBackendError(format_lazy(_("You must have cryptography installed to use {}."), algorithm)) def get_verifying_key(self, token): if self.algorithm.startswith("HS"): return self.signing_key if self.jwks_client: return self.jwks_client.get_signing_key_from_jwt(token).key return self.verifying_key def encode(self, payload): """ Returns an encoded token for the given payload dictionary. """ jwt_payload = payload.copy() if self.audience is not None: jwt_payload['aud'] = self.audience if self.issuer is not None: jwt_payload['iss'] = self.issuer token = jwt.encode(jwt_payload, self.signing_key, algorithm=self.algorithm) if isinstance(token, bytes): # For PyJWT <= 1.7.1 return token.decode('utf-8') # For PyJWT >= 2.0.0a1 return token def decode(self, token, verify=True): """ Performs a validation of the given token and returns its payload dictionary. Raises a `TokenBackendError` if the token is malformed, if its signature check fails, or if its 'exp' claim indicates it has expired. """ try: return jwt.decode( token, self.get_verifying_key(token), algorithms=[self.algorithm], audience=self.audience, issuer=self.issuer, leeway=self.leeway, options={ 'verify_aud': self.audience is not None, 'verify_signature': verify, }, ) except InvalidAlgorithmError as ex: raise TokenBackendError(_('Invalid algorithm specified')) from ex except InvalidTokenError: raise TokenBackendError(_('Token is invalid or expired')) djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/compat.py000066400000000000000000000024061413160305600265740ustar00rootroot00000000000000import warnings try: from django.urls import reverse, reverse_lazy except ImportError: from django.core.urlresolvers import reverse, reverse_lazy # NOQA class RemovedInDjango20Warning(DeprecationWarning): pass class CallableBool: # pragma: no cover """ An boolean-like object that is also callable for backwards compatibility. """ do_not_call_in_templates = True def __init__(self, value): self.value = value def __bool__(self): return self.value def __call__(self): warnings.warn( "Using user.is_authenticated() and user.is_anonymous() as a method " "is deprecated. Remove the parentheses to use it as an attribute.", RemovedInDjango20Warning, stacklevel=2 ) return self.value def __nonzero__(self): # Python 2 compatibility return self.value def __repr__(self): return 'CallableBool(%r)' % self.value def __eq__(self, other): return self.value == other def __ne__(self, other): return self.value != other def __or__(self, other): return bool(self.value or other) def __hash__(self): return hash(self.value) CallableFalse = CallableBool(False) CallableTrue = CallableBool(True) djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/exceptions.py000066400000000000000000000017421413160305600274740ustar00rootroot00000000000000from django.utils.translation import gettext_lazy as _ from rest_framework import exceptions, status class TokenError(Exception): pass class TokenBackendError(Exception): pass class DetailDictMixin: def __init__(self, detail=None, code=None): """ Builds a detail dictionary for the error to give more information to API users. """ detail_dict = {'detail': self.default_detail, 'code': self.default_code} if isinstance(detail, dict): detail_dict.update(detail) elif detail is not None: detail_dict['detail'] = detail if code is not None: detail_dict['code'] = code super().__init__(detail_dict) class AuthenticationFailed(DetailDictMixin, exceptions.AuthenticationFailed): pass class InvalidToken(AuthenticationFailed): status_code = status.HTTP_401_UNAUTHORIZED default_detail = _('Token is invalid or expired') default_code = 'token_not_valid' djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/000077500000000000000000000000001413160305600261745ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/cs/000077500000000000000000000000001413160305600266015ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/cs/LC_MESSAGES/000077500000000000000000000000001413160305600303665ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/cs/LC_MESSAGES/django.mo000066400000000000000000000040021413160305600321610ustar00rootroot00000000000000Þ•Ì|ð<ñ,.([2„O·$34h€¢·Ì è  ) 4?CàHG)9q2«7ÞBYwA‡!É#ë(?1\ Ž¯È Ü êô ø    Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundcreated atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: Last-Translator: Lukáš Rod Language: cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AutorizaÄní hlaviÄka musí obsahovat dvÄ› hodnoty oddÄ›lené mezerouNelze vytvoÅ™it token bez zadaného typu nebo životnostiDaný token není validní pro žádný typ tokenuŽádný aktivní úÄet s danými údaji nebyl nalezenNastavení '{}' bylo odstranÄ›no. Dostupná nastavení jsou v '{}'Hodnota tokenu '{}' vyprÅ¡elaToken BlacklistToken neobsahoval žádnou rozpoznatelnou identifikaci uživateleToken nemá žádnou hodnotu '{}'Token nemá žádný identifikátorToken nemá žádný typToken má Å¡patný typToken je na Äerné listinÄ›Token není validní nebo vyprÅ¡ela jeho platnostNerozpoznaný typ algoritmu '{}'Uživatel není aktivníUživatel nenalezenvytvoÅ™ený vplatí dojtiuživateldjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/cs/LC_MESSAGES/django.po000066400000000000000000000053761413160305600322030ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2019. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "Last-Translator: Lukáš Rod \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "AutorizaÄní hlaviÄka musí obsahovat dvÄ› hodnoty oddÄ›lené mezerou" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "Daný token není validní pro žádný typ tokenu" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "Token neobsahoval žádnou rozpoznatelnou identifikaci uživatele" #: authentication.py:116 msgid "User not found" msgstr "Uživatel nenalezen" #: authentication.py:119 msgid "User is inactive" msgstr "Uživatel není aktivní" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Nerozpoznaný typ algoritmu '{}'" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "" #: backends.py:74 msgid "Invalid algorithm specified" msgstr "" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Token není validní nebo vyprÅ¡ela jeho platnost" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "Žádný aktivní úÄet s danými údaji nebyl nalezen" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "Nastavení '{}' bylo odstranÄ›no. Dostupná nastavení jsou v '{}'" #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "uživatel" #: token_blacklist/admin.py:82 msgid "created at" msgstr "vytvoÅ™ený v" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "platí do" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Token Blacklist" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "Nelze vytvoÅ™it token bez zadaného typu nebo životnosti" #: tokens.py:98 msgid "Token has no id" msgstr "Token nemá žádný identifikátor" #: tokens.py:109 msgid "Token has no type" msgstr "Token nemá žádný typ" #: tokens.py:112 msgid "Token has wrong type" msgstr "Token má Å¡patný typ" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "Token nemá žádnou hodnotu '{}'" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "Hodnota tokenu '{}' vyprÅ¡ela" #: tokens.py:192 msgid "Token is blacklisted" msgstr "Token je na Äerné listinÄ›" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/de_CH/000077500000000000000000000000001413160305600271365ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/de_CH/LC_MESSAGES/000077500000000000000000000000001413160305600307235ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/de_CH/LC_MESSAGES/django.mo000066400000000000000000000040111413160305600325160ustar00rootroot00000000000000Þ•Ì|ð<ñ,.([2„O·$34h€¢·Ì è  ) 4?CHGN>–*Õ5X6"²6Âù&;V#t˜¸Ë ã ïü    Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundcreated atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: Last-Translator: rene Language: de_CH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); Der Authorizationheader muss zwei leerzeichen-getrennte Werte enthaltenEin Token ohne Typ oder Lebensdauer kann nicht erstellt werdenDer Token ist für keinen Tokentyp gültigKein aktiver Account mit diesen Zugangsdaten gefundenDie Einstellung '{}' wurde gelöscht. Bitte beachte '{}' für verfügbare Einstellungen.Das Tokenrecht '{}' ist abgelaufenToken BlacklistToken enthält keine erkennbare BenutzeridentifikationToken hat kein '{}' RechtToken hat keine IdToken hat keinen TypToken hat den falschen TypToken steht auf der BlacklistUngültiger oder abgelaufener TokenUnerkannter Algorithmustyp '{}'Inaktiver BenutzerBenutzer nicht gefundenerstellt amläuft ab amjtiBenutzerdjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/de_CH/LC_MESSAGES/django.po000066400000000000000000000054211413160305600325270ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2020. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "Last-Translator: rene \n" "Language: de_CH\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "" "Der Authorizationheader muss zwei leerzeichen-getrennte Werte enthalten" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "Der Token ist für keinen Tokentyp gültig" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "Token enthält keine erkennbare Benutzeridentifikation" #: authentication.py:116 msgid "User not found" msgstr "Benutzer nicht gefunden" #: authentication.py:119 msgid "User is inactive" msgstr "Inaktiver Benutzer" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Unerkannter Algorithmustyp '{}'" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "" #: backends.py:74 msgid "Invalid algorithm specified" msgstr "" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Ungültiger oder abgelaufener Token" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "Kein aktiver Account mit diesen Zugangsdaten gefunden" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "Die Einstellung '{}' wurde gelöscht. Bitte beachte '{}' für verfügbare " "Einstellungen." #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "Benutzer" #: token_blacklist/admin.py:82 msgid "created at" msgstr "erstellt am" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "läuft ab am" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Token Blacklist" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "Ein Token ohne Typ oder Lebensdauer kann nicht erstellt werden" #: tokens.py:98 msgid "Token has no id" msgstr "Token hat keine Id" #: tokens.py:109 msgid "Token has no type" msgstr "Token hat keinen Typ" #: tokens.py:112 msgid "Token has wrong type" msgstr "Token hat den falschen Typ" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "Token hat kein '{}' Recht" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "Das Tokenrecht '{}' ist abgelaufen" #: tokens.py:192 msgid "Token is blacklisted" msgstr "Token steht auf der Blacklist" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es/000077500000000000000000000000001413160305600266035ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es/LC_MESSAGES/000077500000000000000000000000001413160305600303705ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es/LC_MESSAGES/django.mo000066400000000000000000000044551413160305600321770ustar00rootroot00000000000000Þ•Ü%œ0<1,n(›Ä2àOc€3ÄÜìþ( Dev/… µ ÀËÏ ÔLÞ7+4c!˜9ºaô(V<•$Ò÷ !#E#c$‡¬Æ0Ü  ! %      Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeInvalid algorithm specifiedNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundYou must have cryptography installed to use {}.created atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: Last-Translator: zeack Language: es MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); El encabezado 'Authorization' debe contener valores delimitados por espaciosNo se puede crear un token sin tipo o de tan larga vidaEl token dado no es valido para ningun tipo de tokenAlgoritmo especificado no válidoLa combination de credenciales no tiene una cuenta activaLa configuración '{}' fue removida. Por favor, refiérase a '{}' para consultar las disponibles.El privilegio '{}' del token ha expiradoLista negra de TokensEl token no contenía identificación de usuario reconocibleEl token no tiene el privilegio '{}'El token no tiene idEl token no tiene tipoEl token tiene un tipo incorrectoEl token está en lista negraEl token es inválido o ha expiradoTipo de algoritmo no reconocido '{}'El usuario está inactivoUsuario no encontradoDebe tener criptografía instalada para usar {}.creado enexpira enjtiusuariodjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es/LC_MESSAGES/django.po000066400000000000000000000056541413160305600322040ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2020. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "Last-Translator: zeack \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "" "El encabezado 'Authorization' debe contener valores delimitados por espacios" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "El token dado no es valido para ningun tipo de token" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "El token no contenía identificación de usuario reconocible" #: authentication.py:116 msgid "User not found" msgstr "Usuario no encontrado" #: authentication.py:119 msgid "User is inactive" msgstr "El usuario está inactivo" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Tipo de algoritmo no reconocido '{}'" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "Debe tener criptografía instalada para usar {}." #: backends.py:74 msgid "Invalid algorithm specified" msgstr "Algoritmo especificado no válido" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "El token es inválido o ha expirado" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "La combination de credenciales no tiene una cuenta activa" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "La configuración '{}' fue removida. Por favor, refiérase a '{}' para " "consultar las disponibles." #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "usuario" #: token_blacklist/admin.py:82 msgid "created at" msgstr "creado en" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "expira en" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Lista negra de Tokens" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "No se puede crear un token sin tipo o de tan larga vida" #: tokens.py:98 msgid "Token has no id" msgstr "El token no tiene id" #: tokens.py:109 msgid "Token has no type" msgstr "El token no tiene tipo" #: tokens.py:112 msgid "Token has wrong type" msgstr "El token tiene un tipo incorrecto" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "El token no tiene el privilegio '{}'" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "El privilegio '{}' del token ha expirado" #: tokens.py:192 msgid "Token is blacklisted" msgstr "El token está en lista negra" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es_AR/000077500000000000000000000000001413160305600271655ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es_AR/LC_MESSAGES/000077500000000000000000000000001413160305600307525ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es_AR/LC_MESSAGES/django.mo000066400000000000000000000041661413160305600325600ustar00rootroot00000000000000Þ•Ì|ð<ñ,.([2„O·$34h€¢·Ì è  ) 4?CHL\6©6àHq`(Òû7$Inƒ!š ¼#Ý$&@ V `jn    Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundcreated atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: Last-Translator: Ariel Torti Language: es_AR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); El header de autorización debe contener dos valores delimitados por espacioNo es posible crear un token sin tipo o tiempo de vidaEl token dado no es válido para ningún tipo de tokenNo se encontró una cuenta de usuario activa para las credenciales dadasLa configuración '{}' fue removida. Por favor, refiérase a '{}' para consultar las configuraciones disponibles.El privilegio '{}' del token ha expiradoLista negra de TokensEl token no contiene ninguna identificación de usuarioEl token no tiene el privilegio '{}'El token no tiene idEl token no tiene tipoEl token tiene un tipo incorrectoEl token está en la lista negraEl token es inválido o ha expiradoTipo de algoritmo no reconocido '{}'El usuario está inactivoUsuario no encontradocreado enexpira enjtiusuariodjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es_AR/LC_MESSAGES/django.po000066400000000000000000000057271413160305600325670ustar00rootroot00000000000000# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Ariel Torti , 2020. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "Last-Translator: Ariel Torti \n" "Language: es_AR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "" "El header de autorización debe contener dos valores delimitados por espacio" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "El token dado no es válido para ningún tipo de token" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "El token no contiene ninguna identificación de usuario" #: authentication.py:116 msgid "User not found" msgstr "Usuario no encontrado" #: authentication.py:119 msgid "User is inactive" msgstr "El usuario está inactivo" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Tipo de algoritmo no reconocido '{}'" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "" #: backends.py:74 msgid "Invalid algorithm specified" msgstr "" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "El token es inválido o ha expirado" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "" "No se encontró una cuenta de usuario activa para las credenciales dadas" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "La configuración '{}' fue removida. Por favor, refiérase a '{}' para " "consultar las configuraciones disponibles." #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "usuario" #: token_blacklist/admin.py:82 msgid "created at" msgstr "creado en" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "expira en" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Lista negra de Tokens" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "No es posible crear un token sin tipo o tiempo de vida" #: tokens.py:98 msgid "Token has no id" msgstr "El token no tiene id" #: tokens.py:109 msgid "Token has no type" msgstr "El token no tiene tipo" #: tokens.py:112 msgid "Token has wrong type" msgstr "El token tiene un tipo incorrecto" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "El token no tiene el privilegio '{}'" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "El privilegio '{}' del token ha expirado" #: tokens.py:192 msgid "Token is blacklisted" msgstr "El token está en la lista negra" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es_CL/000077500000000000000000000000001413160305600271615ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es_CL/LC_MESSAGES/000077500000000000000000000000001413160305600307465ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es_CL/LC_MESSAGES/django.mo000066400000000000000000000041201413160305600325420ustar00rootroot00000000000000Þ•Ì|ð<ñ,.([2„O·$34h€¢·Ì è  ) 4?CHL]6ª:áLci+Íù; EdvФÀ$Û 0 :DH    Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundcreated atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: Last-Translator: Alfonso Pola Language: es_CL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); El header de autorización debe contener dos valores delimitados por espacioNo es posible crear un token sin tipo o tiempo de vidaEl token provisto no es válido para ningún tipo de tokenNo se encontró una cuenta de usuario activa para las credenciales provistasLa configuración '{}' fue removida. Por favor, refiérase a '{}' para configuraciones disponibles.El provilegio '{}' del token está expiradoToken BlacklistEl token no contiene identificación de usuario reconocibleToken no tiene privilegio '{}'Token no tiene idToken no tiene tipoToken tiene tipo erróneoToken está en la blacklistToken inválido o expiradoTipo de algoritmo no reconocido '{}'El usuario está inactivoUsuario no encontradocreado enexpira enjtiUsuariodjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/es_CL/LC_MESSAGES/django.po000066400000000000000000000055421413160305600325560ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2019. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "Last-Translator: Alfonso Pola \n" "Language: es_CL\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "" "El header de autorización debe contener dos valores delimitados por espacio" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "El token provisto no es válido para ningún tipo de token" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "El token no contiene identificación de usuario reconocible" #: authentication.py:116 msgid "User not found" msgstr "Usuario no encontrado" #: authentication.py:119 msgid "User is inactive" msgstr "El usuario está inactivo" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Tipo de algoritmo no reconocido '{}'" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "" #: backends.py:74 msgid "Invalid algorithm specified" msgstr "" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Token inválido o expirado" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "" "No se encontró una cuenta de usuario activa para las credenciales provistas" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "La configuración '{}' fue removida. Por favor, refiérase a '{}' para " "configuraciones disponibles." #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "Usuario" #: token_blacklist/admin.py:82 msgid "created at" msgstr "creado en" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "expira en" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Token Blacklist" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "No es posible crear un token sin tipo o tiempo de vida" #: tokens.py:98 msgid "Token has no id" msgstr "Token no tiene id" #: tokens.py:109 msgid "Token has no type" msgstr "Token no tiene tipo" #: tokens.py:112 msgid "Token has wrong type" msgstr "Token tiene tipo erróneo" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "Token no tiene privilegio '{}'" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "El provilegio '{}' del token está expirado" #: tokens.py:192 msgid "Token is blacklisted" msgstr "Token está en la blacklist" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/fa_IR/000077500000000000000000000000001413160305600271545ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/fa_IR/LC_MESSAGES/000077500000000000000000000000001413160305600307415ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/fa_IR/LC_MESSAGES/django.mo000066400000000000000000000044731413160305600325500ustar00rootroot00000000000000Þ•Ì|ð<ñ,.([2„O·$34h€¢·Ì è  ) 4?CðHd9KžWêTBv—%4RO0¢Óê'/-<]-š Èé  , 0     Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundcreated atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: Last-Translator: Hirad Daneshvar Language: fa_IR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit هدر اعتبارسنجی باید شامل دو مقدار جدا شده با ÙØ§ØµÙ„Ù‡ باشدتوکن بدون هیچ نوع Ùˆ طول عمر قابل ساخت نیستتوکن داده شده برای هیچ نوع توکنی معتبر نمی‌باشدهیچ اکانت ÙØ¹Ø§Ù„ÛŒ برای اطلاعات داده شده ÛŒØ§ÙØª نشدتنظیمات '{}' حذ٠شده است. Ù„Ø·ÙØ§ به '{}' برای تنظیمات موجود مراجعه کنید.'{}' claim توکن منقضی شدهلیست سیاه توکنتوکن شامل هیچ شناسه قابل تشخیصی از کاربر نیستتوکن دارای '{}' claim نمی‌باشدتوکن id نداردتوکن نوع نداردتوکن نوع اشتباهی داردتوکن به لیست سیاه Ø±ÙØªÙ‡ استتوکن نامعتبر است یا منقضی شده استنوع الگوریتم ناشناخته '{}'کاربر ØºÛŒØ±ÙØ¹Ø§Ù„ استکاربر ÛŒØ§ÙØª نشدزمان ایجادزمان انقضاjtiکاربرdjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/fa_IR/LC_MESSAGES/django.po000066400000000000000000000061001413160305600325400ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2020. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "Last-Translator: Hirad Daneshvar \n" "Language: fa_IR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "هدر اعتبارسنجی باید شامل دو مقدار جدا شده با ÙØ§ØµÙ„Ù‡ باشد" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "توکن داده شده برای هیچ نوع توکنی معتبر نمی‌باشد" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "توکن شامل هیچ شناسه قابل تشخیصی از کاربر نیست" #: authentication.py:116 msgid "User not found" msgstr "کاربر ÛŒØ§ÙØª نشد" #: authentication.py:119 msgid "User is inactive" msgstr "کاربر ØºÛŒØ±ÙØ¹Ø§Ù„ است" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "نوع الگوریتم ناشناخته '{}'" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "" #: backends.py:74 msgid "Invalid algorithm specified" msgstr "" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "توکن نامعتبر است یا منقضی شده است" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "هیچ اکانت ÙØ¹Ø§Ù„ÛŒ برای اطلاعات داده شده ÛŒØ§ÙØª نشد" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "تنظیمات '{}' حذ٠شده است. Ù„Ø·ÙØ§ به '{}' برای تنظیمات موجود مراجعه کنید." #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "کاربر" #: token_blacklist/admin.py:82 msgid "created at" msgstr "زمان ایجاد" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "زمان انقضا" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "لیست سیاه توکن" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "توکن بدون هیچ نوع Ùˆ طول عمر قابل ساخت نیست" #: tokens.py:98 msgid "Token has no id" msgstr "توکن id ندارد" #: tokens.py:109 msgid "Token has no type" msgstr "توکن نوع ندارد" #: tokens.py:112 msgid "Token has wrong type" msgstr "توکن نوع اشتباهی دارد" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "توکن دارای '{}' claim نمی‌باشد" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "'{}' claim توکن منقضی شده" #: tokens.py:192 msgid "Token is blacklisted" msgstr "توکن به لیست سیاه Ø±ÙØªÙ‡ است" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/fr/000077500000000000000000000000001413160305600266035ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/fr/LC_MESSAGES/000077500000000000000000000000001413160305600303705ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/fr/LC_MESSAGES/django.mo000066400000000000000000000044601413160305600321730ustar00rootroot00000000000000Þ•Ü%œ0<1,n(›Ä2àOc€3ÄÜìþ( Dev/… µ ÀËÏöÔPË6(S$|B¡Zä%?eM}#Ëï9 P"q”#²5Ö   $      Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeInvalid algorithm specifiedNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundYou must have cryptography installed to use {}.created atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: Last-Translator: Stéphane Malta e Sousa Language: fr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'en-tête 'Authorization' doit contenir deux valeurs séparées par des espacesNe peut pas créer de jeton sans type ni durée de vieLe type de jeton fourni n'est pas valideL'algorithme spécifié est invalideAucun compte actif n'a été trouvé avec les identifiants fournisLe paramètre '{}' a été supprimé. Voir '{}' pour la liste des paramètres disponibles.Le privilège '{}' du jeton a expiréListe des jetons bannisLe jeton ne contient aucune information permettant d'identifier l'utilisateurLe jeton n'a pas le privilège '{}'Le jeton n'a pas d'idLe jeton n'a pas de typeLe jeton a un type erronéLe jeton a été banniLe jeton est invalide ou expiréType d'algorithme non reconnu '{}'L'utilisateur est désactivéL'utilisateur n'a pas été trouvéVous devez installer cryptography afin d'utiliser {}.Créé leExpire lejtiUtilisateurdjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/fr/LC_MESSAGES/django.po000066400000000000000000000056641413160305600322050ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2020. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "Last-Translator: Stéphane Malta e Sousa \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "" "L'en-tête 'Authorization' doit contenir deux valeurs séparées par des espaces" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "Le type de jeton fourni n'est pas valide" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "" "Le jeton ne contient aucune information permettant d'identifier l'utilisateur" #: authentication.py:116 msgid "User not found" msgstr "L'utilisateur n'a pas été trouvé" #: authentication.py:119 msgid "User is inactive" msgstr "L'utilisateur est désactivé" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Type d'algorithme non reconnu '{}'" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "Vous devez installer cryptography afin d'utiliser {}." #: backends.py:74 msgid "Invalid algorithm specified" msgstr "L'algorithme spécifié est invalide" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Le jeton est invalide ou expiré" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "Aucun compte actif n'a été trouvé avec les identifiants fournis" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "Le paramètre '{}' a été supprimé. Voir '{}' pour la liste des paramètres " "disponibles." #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "Utilisateur" #: token_blacklist/admin.py:82 msgid "created at" msgstr "Créé le" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "Expire le" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Liste des jetons bannis" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "Ne peut pas créer de jeton sans type ni durée de vie" #: tokens.py:98 msgid "Token has no id" msgstr "Le jeton n'a pas d'id" #: tokens.py:109 msgid "Token has no type" msgstr "Le jeton n'a pas de type" #: tokens.py:112 msgid "Token has wrong type" msgstr "Le jeton a un type erroné" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "Le jeton n'a pas le privilège '{}'" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "Le privilège '{}' du jeton a expiré" #: tokens.py:192 msgid "Token is blacklisted" msgstr "Le jeton a été banni" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/id_ID/000077500000000000000000000000001413160305600271445ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/id_ID/LC_MESSAGES/000077500000000000000000000000001413160305600307315ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/id_ID/LC_MESSAGES/django.mo000066400000000000000000000044351413160305600325360ustar00rootroot00000000000000Þ•Ü%œ0<1,n(›Ä2àOc€3ÄÜìþ( Dev/… µ ÀËÏÔ=ë4)8^%—D½S"Vy@ŒÍí1"J"m¥5¾ ôÿ       Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeInvalid algorithm specifiedNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundYou must have cryptography installed to use {}.created atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: Last-Translator: oon arfiandwi Language: id_ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); Header otorisasi harus berisi dua nilai yang dipisahkan spasiTidak dapat membuat token tanpa tipe atau masa pakaiToken yang diberikan tidak valid untuk semua jenis tokenAlgoritma yang ditentukan tidak validTidak ada akun aktif yang ditemukan dengan kredensial yang diberikanSetelan '{}' telah dihapus. Silakan merujuk ke '{}' untuk pengaturan yang tersedia.Klaim token '{}' telah kedaluwarsaDaftar Hitam TokenToken tidak mengandung identifikasi pengguna yang dapat dikenaliToken tidak memiliki klaim '{}'Token tidak memiliki idToken tidak memiliki tipeJenis token salahToken masuk daftar hitamToken tidak valid atau kedaluwarsaJenis algoritma tidak dikenal '{}'Pengguna tidak aktifPengguna tidak ditemukanAnda harus memasang kriptografi untuk menggunakan {}.created atkedaluwarsa padajtipenggunadjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/id_ID/LC_MESSAGES/django.po000066400000000000000000000056341413160305600325430ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2020. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "Last-Translator: oon arfiandwi \n" "Language: id_ID\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "Header otorisasi harus berisi dua nilai yang dipisahkan spasi" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "Token yang diberikan tidak valid untuk semua jenis token" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "Token tidak mengandung identifikasi pengguna yang dapat dikenali" #: authentication.py:116 msgid "User not found" msgstr "Pengguna tidak ditemukan" #: authentication.py:119 msgid "User is inactive" msgstr "Pengguna tidak aktif" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Jenis algoritma tidak dikenal '{}'" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "Anda harus memasang kriptografi untuk menggunakan {}." #: backends.py:74 msgid "Invalid algorithm specified" msgstr "Algoritma yang ditentukan tidak valid" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Token tidak valid atau kedaluwarsa" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "Tidak ada akun aktif yang ditemukan dengan kredensial yang diberikan" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "Setelan '{}' telah dihapus. Silakan merujuk ke '{}' untuk pengaturan yang " "tersedia." #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "pengguna" #: token_blacklist/admin.py:82 msgid "created at" msgstr "created at" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "kedaluwarsa pada" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Daftar Hitam Token" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "Tidak dapat membuat token tanpa tipe atau masa pakai" #: tokens.py:98 msgid "Token has no id" msgstr "Token tidak memiliki id" #: tokens.py:109 msgid "Token has no type" msgstr "Token tidak memiliki tipe" #: tokens.py:112 msgid "Token has wrong type" msgstr "Jenis token salah" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "Token tidak memiliki klaim '{}'" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "Klaim token '{}' telah kedaluwarsa" #: tokens.py:192 msgid "Token is blacklisted" msgstr "Token masuk daftar hitam" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/it_IT/000077500000000000000000000000001413160305600272045ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/it_IT/LC_MESSAGES/000077500000000000000000000000001413160305600307715ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/it_IT/LC_MESSAGES/django.mo000066400000000000000000000046011413160305600325710ustar00rootroot00000000000000Þ•Ü%œ0<1,n(›Ä2àOc€3ÄÜìþ( Dev/… µ ÀËÏQÔM&/t7¤%Ü4g7&ŸÆEÚ' H^v*”#¿'ã  20 c m v z      Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeInvalid algorithm specifiedNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundYou must have cryptography installed to use {}.created atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: PO-Revision-Date: Last-Translator: Adriano Di Dio <95adriano@gmail.com> Language-Team: Language: it_IT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Poedit 2.0.6 Plural-Forms: nplurals=2; plural=(n != 1); L'header di autorizzazione deve contenere due valori delimitati da uno spazioImpossibile creare un token senza tipo o durataIl token dato non è valido per qualsiasi tipo di tokenL'algoritmo specificato non è validoNessun account attivo trovato con queste credenzialiL'impostazione '{}' è stata rimossa. Per favore utilizza '{}' per visualizzare le impostazioni valide.Il parametro '{}' del token è scadutoBlacklist dei tokenIl token non conteneva nessuna informazione riconoscibile dell'utenteIl token non contiene il parametro '{}'Il token non ha un idIl token non ha un tipoIl token ha un tipo sbagliatoIl token è stato inserito nella blacklistIl token non è valido o è scadutoAlgoritmo di tipo '{}' non riconosciutoUtente non attivoUtente non trovatoDevi avere installato cryptography per usare '{}'.creato ilscade iljtiutentedjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/it_IT/LC_MESSAGES/django.po000066400000000000000000000060131413160305600325730ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <95adriano@gmail.com>, 2020. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "PO-Revision-Date: \n" "Last-Translator: Adriano Di Dio <95adriano@gmail.com>\n" "Language-Team: \n" "Language: it_IT\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.0.6\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "" "L'header di autorizzazione deve contenere due valori delimitati da uno spazio" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "Il token dato non è valido per qualsiasi tipo di token" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "Il token non conteneva nessuna informazione riconoscibile dell'utente" #: authentication.py:116 msgid "User not found" msgstr "Utente non trovato" #: authentication.py:119 msgid "User is inactive" msgstr "Utente non attivo" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Algoritmo di tipo '{}' non riconosciuto" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "Devi avere installato cryptography per usare '{}'." #: backends.py:74 msgid "Invalid algorithm specified" msgstr "L'algoritmo specificato non è valido" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Il token non è valido o è scaduto" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "Nessun account attivo trovato con queste credenziali" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "L'impostazione '{}' è stata rimossa. Per favore utilizza '{}' per " "visualizzare le impostazioni valide." #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "utente" #: token_blacklist/admin.py:82 msgid "created at" msgstr "creato il" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "scade il" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Blacklist dei token" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "Impossibile creare un token senza tipo o durata" #: tokens.py:98 msgid "Token has no id" msgstr "Il token non ha un id" #: tokens.py:109 msgid "Token has no type" msgstr "Il token non ha un tipo" #: tokens.py:112 msgid "Token has wrong type" msgstr "Il token ha un tipo sbagliato" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "Il token non contiene il parametro '{}'" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "Il parametro '{}' del token è scaduto" #: tokens.py:192 msgid "Token is blacklisted" msgstr "Il token è stato inserito nella blacklist" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/nl_NL/000077500000000000000000000000001413160305600271765ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/nl_NL/LC_MESSAGES/000077500000000000000000000000001413160305600307635ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/nl_NL/LC_MESSAGES/django.mo000066400000000000000000000037051413160305600325670ustar00rootroot00000000000000Þ•Ì|ð<ñ,.([2„O·$34h€¢·Ì è  ) 4?CäHJ-.x.§/ÖKRo3³Ïãù .Oo…  «· »    Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundcreated atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: Last-Translator: rene Language: nl_NL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Authorisatie header moet twee waarden bevatten, gescheiden door een spatieKan geen token maken zonder type of levensduurHet token is voor geen enkel token-type geldigGeen actief account gevonden voor deze gegevensDe '{}' instelling bestaat niet meer. Zie '{}' for beschikbareinstellingen.Token '{}' recht is verlopenToken BlacklistToken bevat geen herkenbare gebruikersidentificatieToken heeft geen '{}' rechtToken heeft geen idToken heeft geen typeToken heeft het verkeerde typeToken is ge-blacklistToken is niet geldig of verlopenNiet herkend algoritme type '{}Gebruiker is inactiefGebruiker niet gevondenaangemaakt opverloopt opjtigebruikerdjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/nl_NL/LC_MESSAGES/django.po000066400000000000000000000053171413160305600325730ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2020. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-06-17 11:06+0200\n" "Last-Translator: rene \n" "Language: nl_NL\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "" "Authorisatie header moet twee waarden bevatten, gescheiden door een spatie" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "Het token is voor geen enkel token-type geldig" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "Token bevat geen herkenbare gebruikersidentificatie" #: authentication.py:116 msgid "User not found" msgstr "Gebruiker niet gevonden" #: authentication.py:119 msgid "User is inactive" msgstr "Gebruiker is inactief" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Niet herkend algoritme type '{}" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "" #: backends.py:74 msgid "Invalid algorithm specified" msgstr "" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Token is niet geldig of verlopen" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "Geen actief account gevonden voor deze gegevens" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "De '{}' instelling bestaat niet meer. Zie '{}' for beschikbareinstellingen." #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "gebruiker" #: token_blacklist/admin.py:82 msgid "created at" msgstr "aangemaakt op" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "verloopt op" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Token Blacklist" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "Kan geen token maken zonder type of levensduur" #: tokens.py:98 msgid "Token has no id" msgstr "Token heeft geen id" #: tokens.py:109 msgid "Token has no type" msgstr "Token heeft geen type" #: tokens.py:112 msgid "Token has wrong type" msgstr "Token heeft het verkeerde type" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "Token heeft geen '{}' recht" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "Token '{}' recht is verlopen" #: tokens.py:192 msgid "Token is blacklisted" msgstr "Token is ge-blacklist" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/pl_PL/000077500000000000000000000000001413160305600272025ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/pl_PL/LC_MESSAGES/000077500000000000000000000000001413160305600307675ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/pl_PL/LC_MESSAGES/django.mo000066400000000000000000000040721413160305600325710ustar00rootroot00000000000000Þ•Ì|ð<ñ,.([2„O·$34h€¢·Ì è  ) 4?CèHH1>z3¹GíM5"ƒ¦=¶$ô*D['r"š ½Þú !* .    Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundcreated atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: Last-Translator: Mateusz Slisz Language: pl_PL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nagłówek autoryzacji musi zawierać dwie wartoÅ›ci rodzielone spacjamiNie można utworzyć tokena bez podanego typu lub żywotnoÅ›ciPodany token jest błędny dla każdego typu tokenaNie znaleziono aktywnego konta dla podanych danych uwierzytelniajÄ…cychUstawienie '{}' zostaÅ‚o usuniÄ™te. DostÄ™pne ustawienia znajdujÄ… sie w '{}'Upoważnienie tokena '{}' wygasÅ‚oToken BlacklistToken nie zawieraÅ‚ rozpoznawalnej identyfikacji użytkownikaToken nie posiada upoważnienia '{}'Token nie posiada numeru identyfikacyjnegoToken nie posiada typuToken posiada zÅ‚y typToken znajdujÄ™ siÄ™ na czarnej liÅ›cieToken jest niepoprawny lub wygasÅ‚Nierozpoznany typ algorytmu '{}'Użytkownik jest nieaktywnyUżytkownik nie znalezionystworzony wwygasa ojtiużytkownikdjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/pl_PL/LC_MESSAGES/django.po000066400000000000000000000054741413160305600326030ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2019. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "Last-Translator: Mateusz Slisz \n" "Language: pl_PL\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "Nagłówek autoryzacji musi zawierać dwie wartoÅ›ci rodzielone spacjami" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "Podany token jest błędny dla każdego typu tokena" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "Token nie zawieraÅ‚ rozpoznawalnej identyfikacji użytkownika" #: authentication.py:116 msgid "User not found" msgstr "Użytkownik nie znaleziony" #: authentication.py:119 msgid "User is inactive" msgstr "Użytkownik jest nieaktywny" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Nierozpoznany typ algorytmu '{}'" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "" #: backends.py:74 msgid "Invalid algorithm specified" msgstr "" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Token jest niepoprawny lub wygasÅ‚" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "Nie znaleziono aktywnego konta dla podanych danych uwierzytelniajÄ…cych" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "Ustawienie '{}' zostaÅ‚o usuniÄ™te. DostÄ™pne ustawienia znajdujÄ… sie w '{}'" #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "użytkownik" #: token_blacklist/admin.py:82 msgid "created at" msgstr "stworzony w" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "wygasa o" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Token Blacklist" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "Nie można utworzyć tokena bez podanego typu lub żywotnoÅ›ci" #: tokens.py:98 msgid "Token has no id" msgstr "Token nie posiada numeru identyfikacyjnego" #: tokens.py:109 msgid "Token has no type" msgstr "Token nie posiada typu" #: tokens.py:112 msgid "Token has wrong type" msgstr "Token posiada zÅ‚y typ" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "Token nie posiada upoważnienia '{}'" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "Upoważnienie tokena '{}' wygasÅ‚o" #: tokens.py:192 msgid "Token is blacklisted" msgstr "Token znajdujÄ™ siÄ™ na czarnej liÅ›cie" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/pt_BR/000077500000000000000000000000001413160305600272025ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/pt_BR/LC_MESSAGES/000077500000000000000000000000001413160305600307675ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/pt_BR/LC_MESSAGES/django.mo000066400000000000000000000044241413160305600325720ustar00rootroot00000000000000Þ•Ü%œ0<1,n(›Ä2àOc€3ÄÜìþ( Dev/… µ ÀËÏÔLç74=l ª Ë[ì#HlG‚Êêü- F'g¦3¿ ó ý       Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeInvalid algorithm specifiedNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundYou must have cryptography installed to use {}.created atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: Last-Translator: Bruno Ducraux Language: pt_BR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); Cabeçalho de autorização deve conter dois valores delimitados por espaçoNão é possível criar token sem tipo ou tempo de vidaO token informado não é válido para qualquer tipo de tokenAlgoritmo inválido especificadoUsuário e/ou senha incorreto(s)A configuração '{}' foi removida. Por favor, consulte '{}' para disponível definições.O privilégio '{}' do token expirouLista negra de TokensO token não continha nenhuma identificação reconhecível do usuárioToken não tem '{}' privilégioToken não tem idToken não tem nenhum tipoToken tem tipo erradoToken está na blacklistO token é inválido ou expiradoTipo de algoritmo '{}' não reconhecidoUsuário está inativoUsuário não encontradoVocê deve ter criptografia instalada para usar {}.criado emexpira emjtiusuáriodjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/pt_BR/LC_MESSAGES/django.po000066400000000000000000000056211413160305600325750ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2019. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "Last-Translator: Bruno Ducraux \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "" "Cabeçalho de autorização deve conter dois valores delimitados por espaço" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "O token informado não é válido para qualquer tipo de token" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "O token não continha nenhuma identificação reconhecível do usuário" #: authentication.py:116 msgid "User not found" msgstr "Usuário não encontrado" #: authentication.py:119 msgid "User is inactive" msgstr "Usuário está inativo" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Tipo de algoritmo '{}' não reconhecido" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "Você deve ter criptografia instalada para usar {}." #: backends.py:74 msgid "Invalid algorithm specified" msgstr "Algoritmo inválido especificado" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "O token é inválido ou expirado" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "Usuário e/ou senha incorreto(s)" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "A configuração '{}' foi removida. Por favor, consulte '{}' para disponível " "definições." #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "usuário" #: token_blacklist/admin.py:82 msgid "created at" msgstr "criado em" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "expira em" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Lista negra de Tokens" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "Não é possível criar token sem tipo ou tempo de vida" #: tokens.py:98 msgid "Token has no id" msgstr "Token não tem id" #: tokens.py:109 msgid "Token has no type" msgstr "Token não tem nenhum tipo" #: tokens.py:112 msgid "Token has wrong type" msgstr "Token tem tipo errado" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "Token não tem '{}' privilégio" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "O privilégio '{}' do token expirou" #: tokens.py:192 msgid "Token is blacklisted" msgstr "Token está na blacklist" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/ru_RU/000077500000000000000000000000001413160305600272305ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/ru_RU/LC_MESSAGES/000077500000000000000000000000001413160305600310155ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/ru_RU/LC_MESSAGES/django.mo000066400000000000000000000053401413160305600326160ustar00rootroot00000000000000Þ•Ì|ð<ñ,.([2„O·$34h€¢·Ì è  ) 4?C£HŠì_w^×g6œžD;€T%å3 #? 5c 6™ AÐ ; +N *z ¥ ² à Ç     Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundcreated atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: PO-Revision-Date: Last-Translator: Sergey Ozeranskiy Language-Team: Language: ru_RU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2); X-Generator: Poedit 2.2.1 Заголовок авторизации должен Ñодержать два значениÑ, разделенных пробеломÐевозможно Ñоздать токен без типа или времени жизниДанный токен недейÑтвителен Ð´Ð»Ñ Ð»ÑŽÐ±Ð¾Ð³Ð¾ типа токенаÐе найдено активной учетной запиÑи Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ð¼Ð¸ даннымиПараметр '{}' был удален. ПожалуйÑта, обратитеÑÑŒ к '{}' Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра доÑтупных наÑтроек.Токен имеет проÑроченное значение '{}'Token BlacklistТокен не Ñодержит идентификатор пользователÑТокен не Ñодержит '{}'У токена нет идентификатораТокен не имеет типаТокен имеет неправильный типТокен занеÑен в черный ÑпиÑокТокен недейÑтвителен или проÑроченÐераÑпознанный тип алгоритма '{}'Пользователь неактивенПользователь не найденÑозданиÑтекаетjtiпользовательdjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/ru_RU/LC_MESSAGES/django.po000066400000000000000000000067721413160305600326330ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2019. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-22 17:30+0100\n" "PO-Revision-Date: \n" "Last-Translator: Sergey Ozeranskiy \n" "Language-Team: \n" "Language: ru_RU\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" "X-Generator: Poedit 2.2.1\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "" "Заголовок авторизации должен Ñодержать два значениÑ, разделенных пробелом" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "Данный токен недейÑтвителен Ð´Ð»Ñ Ð»ÑŽÐ±Ð¾Ð³Ð¾ типа токена" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "Токен не Ñодержит идентификатор пользователÑ" #: authentication.py:116 msgid "User not found" msgstr "Пользователь не найден" #: authentication.py:119 msgid "User is inactive" msgstr "Пользователь неактивен" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "ÐераÑпознанный тип алгоритма '{}'" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "" #: backends.py:74 msgid "Invalid algorithm specified" msgstr "" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Токен недейÑтвителен или проÑрочен" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "Ðе найдено активной учетной запиÑи Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ð¼Ð¸ данными" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "Параметр '{}' был удален. ПожалуйÑта, обратитеÑÑŒ к '{}' Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра " "доÑтупных наÑтроек." #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "пользователь" #: token_blacklist/admin.py:82 msgid "created at" msgstr "Ñоздан" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "иÑтекает" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Token Blacklist" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "Ðевозможно Ñоздать токен без типа или времени жизни" #: tokens.py:98 msgid "Token has no id" msgstr "У токена нет идентификатора" #: tokens.py:109 msgid "Token has no type" msgstr "Токен не имеет типа" #: tokens.py:112 msgid "Token has wrong type" msgstr "Токен имеет неправильный тип" #: tokens.py:149 msgid "Token has no '{}' claim" msgstr "Токен не Ñодержит '{}'" #: tokens.py:153 msgid "Token '{}' claim has expired" msgstr "Токен имеет проÑроченное значение '{}'" #: tokens.py:192 msgid "Token is blacklisted" msgstr "Токен занеÑен в черный ÑпиÑок" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/uk_UA/000077500000000000000000000000001413160305600272005ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/uk_UA/LC_MESSAGES/000077500000000000000000000000001413160305600307655ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/uk_UA/LC_MESSAGES/django.mo000066400000000000000000000057371413160305600326000ustar00rootroot00000000000000Þ•Ü%œ0<1,n(›Ä2àOc€3ÄÜìþ( Dev/… µ ÀËÏ3ÔYŠVä2;xnqç8Y(’n»?* 7j ;¢ ;Þ : ZU :° )ë , WB š ° Æ Ê      Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeInvalid algorithm specifiedNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundYou must have cryptography installed to use {}.created atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Artiukhov Artem Language-Team: LANGUAGE Language: uk_UA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ðвторизаційний заголовок має міÑтити два Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ€Ð¾Ð·Ð´Ñ–Ð»ÐµÐ½Ñ– пробіломÐеможливо Ñтворити токен без типу або Ñтроку діїÐаданий токен не відповідає жодному типу ключаВказаний невірний алгоритмÐе знайдено жодного облікового запиÑу по наданих облікових данихÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ '{}' видалене. ПодивітьÑÑ Ñƒ '{}' Ð´Ð»Ñ Ñ–Ð½ÑˆÐ¸Ñ… доÑтупнихЗаголовок '{}' токена не дійÑнийЧорний ÑпиÑок токенівÐаданий токен не мітить жодної ідентифікаційної інформаціїУ токені не міÑтитьÑÑ '{}' заголовкуУ ключі доÑтупу не міÑтитьÑÑ idУ ключі доÑтупу не міÑтитьÑÑ Ñ‚Ð¸Ð¿Ñ‚Ð¾ÐºÐµÐ½ позначений невірним типомТокен занеÑений у чорний ÑпиÑокТокен некоректний або термін його дії вичерпанийТип алгоритму '{}' не розпізнанийКориÑтувач неактивнийКориÑтувач не знайденийВÑтановіть модуль cryptography щоб викориÑтовувати {}Ñтворений одійÑтний поjtiкориÑтувачdjangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/uk_UA/LC_MESSAGES/django.po000066400000000000000000000103661413160305600325750ustar00rootroot00000000000000# This file is distributed under the same license as the PACKAGE package. # Artiukhov Artem , 2021. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-06-17 12:32+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Artiukhov Artem \n" "Language-Team: LANGUAGE \n" "Language: uk_UA\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: rest_framework_simplejwt/authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "Ðвторизаційний заголовок має міÑтити два Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ€Ð¾Ð·Ð´Ñ–Ð»ÐµÐ½Ñ– пробілом" #: rest_framework_simplejwt/authentication.py:100 msgid "Given token not valid for any token type" msgstr "Ðаданий токен не відповідає жодному типу ключа" #: rest_framework_simplejwt/authentication.py:111 #: rest_framework_simplejwt/authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "Ðаданий токен не мітить жодної ідентифікаційної інформації" #: rest_framework_simplejwt/authentication.py:116 msgid "User not found" msgstr "КориÑтувач не знайдений" #: rest_framework_simplejwt/authentication.py:119 msgid "User is inactive" msgstr "КориÑтувач неактивний" #: rest_framework_simplejwt/backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "Тип алгоритму '{}' не розпізнаний" #: rest_framework_simplejwt/backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "Ð’Ñтановіть модуль cryptography щоб викориÑтовувати {}" #: rest_framework_simplejwt/backends.py:74 msgid "Invalid algorithm specified" msgstr "Вказаний невірний алгоритм" #: rest_framework_simplejwt/backends.py:76 #: rest_framework_simplejwt/exceptions.py:38 #: rest_framework_simplejwt/tokens.py:44 msgid "Token is invalid or expired" msgstr "Токен некоректний або термін його дії вичерпаний" #: rest_framework_simplejwt/serializers.py:24 msgid "No active account found with the given credentials" msgstr "Ðе знайдено жодного облікового запиÑу по наданих облікових даних" #: rest_framework_simplejwt/settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ '{}' видалене. ПодивітьÑÑ Ñƒ '{}' Ð´Ð»Ñ Ñ–Ð½ÑˆÐ¸Ñ… доÑтупних" #: rest_framework_simplejwt/token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: rest_framework_simplejwt/token_blacklist/admin.py:77 msgid "user" msgstr "кориÑтувач" #: rest_framework_simplejwt/token_blacklist/admin.py:82 msgid "created at" msgstr "Ñтворений о" #: rest_framework_simplejwt/token_blacklist/admin.py:87 msgid "expires at" msgstr "дійÑтний по" #: rest_framework_simplejwt/token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "Чорний ÑпиÑок токенів" #: rest_framework_simplejwt/tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "Ðеможливо Ñтворити токен без типу або Ñтроку дії" #: rest_framework_simplejwt/tokens.py:98 msgid "Token has no id" msgstr "У ключі доÑтупу не міÑтитьÑÑ id" #: rest_framework_simplejwt/tokens.py:109 msgid "Token has no type" msgstr "У ключі доÑтупу не міÑтитьÑÑ Ñ‚Ð¸Ð¿" #: rest_framework_simplejwt/tokens.py:112 msgid "Token has wrong type" msgstr "токен позначений невірним типом" #: rest_framework_simplejwt/tokens.py:149 msgid "Token has no '{}' claim" msgstr "У токені не міÑтитьÑÑ '{}' заголовку" #: rest_framework_simplejwt/tokens.py:153 msgid "Token '{}' claim has expired" msgstr "Заголовок '{}' токена не дійÑний" #: rest_framework_simplejwt/tokens.py:192 msgid "Token is blacklisted" msgstr "Токен занеÑений у чорний ÑпиÑок" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/zh_Hans/000077500000000000000000000000001413160305600275665ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/zh_Hans/LC_MESSAGES/000077500000000000000000000000001413160305600313535ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/zh_Hans/LC_MESSAGES/django.mo000066400000000000000000000041621413160305600331550ustar00rootroot00000000000000Þ•Ü%œ0<1,n(›Ä2àOc€3ÄÜìþ( Dev/… µ ÀËÏ*Ô0ÿ-0'^†*œBÇ &6Un„—ªÆß÷ / M Zgk     Authorization header must contain two space-delimited valuesCannot create token with no type or lifetimeGiven token not valid for any token typeInvalid algorithm specifiedNo active account found with the given credentialsThe '{}' setting has been removed. Please refer to '{}' for available settings.Token '{}' claim has expiredToken BlacklistToken contained no recognizable user identificationToken has no '{}' claimToken has no idToken has no typeToken has wrong typeToken is blacklistedToken is invalid or expiredUnrecognized algorithm type '{}'User is inactiveUser not foundYou must have cryptography installed to use {}.created atexpires atjtiuserProject-Id-Version: djangorestframework_simplejwt Report-Msgid-Bugs-To: PO-Revision-Date: 2021-06-23 13:29+080 Last-Translator: zengqiu Language: zh_Hans MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=1; plural=0; 授æƒå¤´å¿…须包å«ä¸¤ä¸ªç”¨ç©ºæ ¼åˆ†éš”的值无法创建没有类型或生存期的令牌此令牌对任何类型的令牌无效指定的算法无效找ä¸åˆ°æŒ‡å®šå‡­æ®å¯¹åº”的有效用户'{}' é…置已被移除。 请å‚阅 '{}' 获å–å¯ç”¨çš„é…置。令牌 '{}' 声明已过期令牌黑åå•令牌未包å«ç”¨æˆ·æ ‡è¯†ç¬¦ä»¤ç‰Œæ²¡æœ‰ '{}' 声明令牌没有标识符令牌没有类型令牌类型错误令牌已被加入黑åå•令牌无效或已过期未知算法类型 '{}'该用户已ç¦ç”¨æœªæ‰¾åˆ°è¯¥ç”¨æˆ·ä½ å¿…须安装 cryptography æ‰èƒ½ä½¿ç”¨ {}。创建时间过期时间jti用户djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/locale/zh_Hans/LC_MESSAGES/django.po000066400000000000000000000054321413160305600331610ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) 2021 # This file is distributed under the same license as the Simple JWT package. # zengqiu , 2021. msgid "" msgstr "" "Project-Id-Version: djangorestframework_simplejwt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-06-23 13:29+0800\n" "PO-Revision-Date: 2021-06-23 13:29+080\n" "Last-Translator: zengqiu \n" "Language: zh_Hans\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: authentication.py:79 msgid "Authorization header must contain two space-delimited values" msgstr "授æƒå¤´å¿…须包å«ä¸¤ä¸ªç”¨ç©ºæ ¼åˆ†éš”的值" #: authentication.py:100 msgid "Given token not valid for any token type" msgstr "此令牌对任何类型的令牌无效" #: authentication.py:111 authentication.py:133 msgid "Token contained no recognizable user identification" msgstr "令牌未包å«ç”¨æˆ·æ ‡è¯†ç¬¦" #: authentication.py:116 msgid "User not found" msgstr "未找到该用户" #: authentication.py:119 msgid "User is inactive" msgstr "该用户已ç¦ç”¨" #: backends.py:37 msgid "Unrecognized algorithm type '{}'" msgstr "未知算法类型 '{}'" #: backends.py:40 msgid "You must have cryptography installed to use {}." msgstr "你必须安装 cryptography æ‰èƒ½ä½¿ç”¨ {}。" #: backends.py:74 msgid "Invalid algorithm specified" msgstr "指定的算法无效" #: backends.py:76 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "令牌无效或已过期" #: serializers.py:24 msgid "No active account found with the given credentials" msgstr "找ä¸åˆ°æŒ‡å®šå‡­æ®å¯¹åº”的有效用户" #: settings.py:63 msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" "'{}' é…置已被移除。 请å‚阅 '{}' 获å–å¯ç”¨çš„" "é…置。" #: token_blacklist/admin.py:72 msgid "jti" msgstr "jti" #: token_blacklist/admin.py:77 msgid "user" msgstr "用户" #: token_blacklist/admin.py:82 msgid "created at" msgstr "创建时间" #: token_blacklist/admin.py:87 msgid "expires at" msgstr "过期时间" #: token_blacklist/apps.py:7 msgid "Token Blacklist" msgstr "令牌黑åå•" #: tokens.py:30 msgid "Cannot create token with no type or lifetime" msgstr "无法创建没有类型或生存期的令牌" #: tokens.py:96 msgid "Token has no id" msgstr "令牌没有标识符" #: tokens.py:107 msgid "Token has no type" msgstr "令牌没有类型" #: tokens.py:110 msgid "Token has wrong type" msgstr "令牌类型错误" #: tokens.py:147 msgid "Token has no '{}' claim" msgstr "令牌没有 '{}' 声明" #: tokens.py:151 msgid "Token '{}' claim has expired" msgstr "令牌 '{}' 声明已过期" #: tokens.py:194 msgid "Token is blacklisted" msgstr "令牌已被加入黑åå•" djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/models.py000066400000000000000000000054201413160305600265730ustar00rootroot00000000000000from django.contrib.auth import models as auth_models from django.db.models.manager import EmptyManager from django.utils.functional import cached_property from .compat import CallableFalse, CallableTrue from .settings import api_settings class TokenUser: """ A dummy user class modeled after django.contrib.auth.models.AnonymousUser. Used in conjunction with the `JWTTokenUserAuthentication` backend to implement single sign-on functionality across services which share the same secret key. `JWTTokenUserAuthentication` will return an instance of this class instead of a `User` model instance. Instances of this class act as stateless user objects which are backed by validated tokens. """ # User is always active since Simple JWT will never issue a token for an # inactive user is_active = True _groups = EmptyManager(auth_models.Group) _user_permissions = EmptyManager(auth_models.Permission) def __init__(self, token): self.token = token def __str__(self): return 'TokenUser {}'.format(self.id) @cached_property def id(self): return self.token[api_settings.USER_ID_CLAIM] @cached_property def pk(self): return self.id @cached_property def username(self): return self.token.get('username', '') @cached_property def is_staff(self): return self.token.get('is_staff', False) @cached_property def is_superuser(self): return self.token.get('is_superuser', False) def __eq__(self, other): return self.id == other.id def __ne__(self, other): return not self.__eq__(other) def __hash__(self): return hash(self.id) def save(self): raise NotImplementedError('Token users have no DB representation') def delete(self): raise NotImplementedError('Token users have no DB representation') def set_password(self, raw_password): raise NotImplementedError('Token users have no DB representation') def check_password(self, raw_password): raise NotImplementedError('Token users have no DB representation') @property def groups(self): return self._groups @property def user_permissions(self): return self._user_permissions def get_group_permissions(self, obj=None): return set() def get_all_permissions(self, obj=None): return set() def has_perm(self, perm, obj=None): return False def has_perms(self, perm_list, obj=None): return False def has_module_perms(self, module): return False @property def is_anonymous(self): return CallableFalse @property def is_authenticated(self): return CallableTrue def get_username(self): return self.username djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/serializers.py000066400000000000000000000113361413160305600276470ustar00rootroot00000000000000from django.contrib.auth import authenticate, get_user_model from django.contrib.auth.models import update_last_login from django.utils.translation import gettext_lazy as _ from rest_framework import exceptions, serializers from rest_framework.exceptions import ValidationError from .settings import api_settings from .tokens import RefreshToken, SlidingToken, UntypedToken if api_settings.BLACKLIST_AFTER_ROTATION: from .token_blacklist.models import BlacklistedToken class PasswordField(serializers.CharField): def __init__(self, *args, **kwargs): kwargs.setdefault('style', {}) kwargs['style']['input_type'] = 'password' kwargs['write_only'] = True super().__init__(*args, **kwargs) class TokenObtainSerializer(serializers.Serializer): username_field = get_user_model().USERNAME_FIELD default_error_messages = { 'no_active_account': _('No active account found with the given credentials') } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields[self.username_field] = serializers.CharField() self.fields['password'] = PasswordField() def validate(self, attrs): authenticate_kwargs = { self.username_field: attrs[self.username_field], 'password': attrs['password'], } try: authenticate_kwargs['request'] = self.context['request'] except KeyError: pass self.user = authenticate(**authenticate_kwargs) if not api_settings.USER_AUTHENTICATION_RULE(self.user): raise exceptions.AuthenticationFailed( self.error_messages['no_active_account'], 'no_active_account', ) return {} @classmethod def get_token(cls, user): raise NotImplementedError('Must implement `get_token` method for `TokenObtainSerializer` subclasses') class TokenObtainPairSerializer(TokenObtainSerializer): @classmethod def get_token(cls, user): return RefreshToken.for_user(user) def validate(self, attrs): data = super().validate(attrs) refresh = self.get_token(self.user) data['refresh'] = str(refresh) data['access'] = str(refresh.access_token) if api_settings.UPDATE_LAST_LOGIN: update_last_login(None, self.user) return data class TokenObtainSlidingSerializer(TokenObtainSerializer): @classmethod def get_token(cls, user): return SlidingToken.for_user(user) def validate(self, attrs): data = super().validate(attrs) token = self.get_token(self.user) data['token'] = str(token) if api_settings.UPDATE_LAST_LOGIN: update_last_login(None, self.user) return data class TokenRefreshSerializer(serializers.Serializer): refresh = serializers.CharField() access = serializers.CharField(read_only=True) def validate(self, attrs): refresh = RefreshToken(attrs['refresh']) data = {'access': str(refresh.access_token)} if api_settings.ROTATE_REFRESH_TOKENS: if api_settings.BLACKLIST_AFTER_ROTATION: try: # Attempt to blacklist the given refresh token refresh.blacklist() except AttributeError: # If blacklist app not installed, `blacklist` method will # not be present pass refresh.set_jti() refresh.set_exp() refresh.set_iat() data['refresh'] = str(refresh) return data class TokenRefreshSlidingSerializer(serializers.Serializer): token = serializers.CharField() def validate(self, attrs): token = SlidingToken(attrs['token']) # Check that the timestamp in the "refresh_exp" claim has not # passed token.check_exp(api_settings.SLIDING_TOKEN_REFRESH_EXP_CLAIM) # Update the "exp" and "iat" claims token.set_exp() token.set_iat() return {'token': str(token)} class TokenVerifySerializer(serializers.Serializer): token = serializers.CharField() def validate(self, attrs): token = UntypedToken(attrs['token']) if api_settings.BLACKLIST_AFTER_ROTATION: jti = token.get(api_settings.JTI_CLAIM) if BlacklistedToken.objects.filter(token__jti=jti).exists(): raise ValidationError("Token is blacklisted") return {} class TokenBlacklistSerializer(serializers.Serializer): refresh = serializers.CharField() def validate(self, attrs): refresh = RefreshToken(attrs['refresh']) try: refresh.blacklist() except AttributeError: pass return {} djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/settings.py000066400000000000000000000046411413160305600271540ustar00rootroot00000000000000from datetime import timedelta from django.conf import settings from django.test.signals import setting_changed from django.utils.translation import gettext_lazy as _ from rest_framework.settings import APISettings as _APISettings from .utils import format_lazy USER_SETTINGS = getattr(settings, 'SIMPLE_JWT', None) DEFAULTS = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), 'ROTATE_REFRESH_TOKENS': False, 'BLACKLIST_AFTER_ROTATION': False, 'UPDATE_LAST_LOGIN': False, 'ALGORITHM': 'HS256', 'SIGNING_KEY': settings.SECRET_KEY, 'VERIFYING_KEY': None, 'AUDIENCE': None, 'ISSUER': None, 'JWK_URL': None, 'LEEWAY': 0, 'AUTH_HEADER_TYPES': ('Bearer',), 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', 'USER_ID_FIELD': 'id', 'USER_ID_CLAIM': 'user_id', 'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule', 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), 'TOKEN_TYPE_CLAIM': 'token_type', 'JTI_CLAIM': 'jti', 'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser', 'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', 'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5), 'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1), } IMPORT_STRINGS = ( 'AUTH_TOKEN_CLASSES', 'TOKEN_USER_CLASS', 'USER_AUTHENTICATION_RULE', ) REMOVED_SETTINGS = ( 'AUTH_HEADER_TYPE', 'AUTH_TOKEN_CLASS', 'SECRET_KEY', 'TOKEN_BACKEND_CLASS', ) class APISettings(_APISettings): # pragma: no cover def __check_user_settings(self, user_settings): SETTINGS_DOC = 'https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html' for setting in REMOVED_SETTINGS: if setting in user_settings: raise RuntimeError(format_lazy( _("The '{}' setting has been removed. Please refer to '{}' for available settings."), setting, SETTINGS_DOC, )) return user_settings api_settings = APISettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS) def reload_api_settings(*args, **kwargs): # pragma: no cover global api_settings setting, value = kwargs['setting'], kwargs['value'] if setting == 'SIMPLE_JWT': api_settings = APISettings(value, DEFAULTS, IMPORT_STRINGS) setting_changed.connect(reload_api_settings) djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/state.py000066400000000000000000000004501413160305600264260ustar00rootroot00000000000000from .backends import TokenBackend from .settings import api_settings token_backend = TokenBackend( api_settings.ALGORITHM, api_settings.SIGNING_KEY, api_settings.VERIFYING_KEY, api_settings.AUDIENCE, api_settings.ISSUER, api_settings.JWK_URL, api_settings.LEEWAY, ) djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/000077500000000000000000000000001413160305600301055ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/__init__.py000066400000000000000000000002171413160305600322160ustar00rootroot00000000000000from django import VERSION if VERSION < (3, 2): default_app_config = 'rest_framework_simplejwt.token_blacklist.apps.TokenBlacklistConfig' djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/admin.py000066400000000000000000000044501413160305600315520ustar00rootroot00000000000000from django.contrib import admin from django.utils.translation import gettext_lazy as _ from .models import BlacklistedToken, OutstandingToken class OutstandingTokenAdmin(admin.ModelAdmin): list_display = ( 'jti', 'user', 'created_at', 'expires_at', ) search_fields = ( 'user__id', 'jti', ) ordering = ( 'user', ) def get_queryset(self, *args, **kwargs): qs = super().get_queryset(*args, **kwargs) return qs.select_related('user') # Read-only behavior defined below actions = None def get_readonly_fields(self, *args, **kwargs): return [f.name for f in self.model._meta.fields] def has_add_permission(self, *args, **kwargs): return False def has_delete_permission(self, *args, **kwargs): return False def has_change_permission(self, request, obj=None): return ( request.method in ['GET', 'HEAD'] and # noqa: W504 super().has_change_permission(request, obj) ) admin.site.register(OutstandingToken, OutstandingTokenAdmin) class BlacklistedTokenAdmin(admin.ModelAdmin): list_display = ( 'token_jti', 'token_user', 'token_created_at', 'token_expires_at', 'blacklisted_at', ) search_fields = ( 'token__user__id', 'token__jti', ) ordering = ( 'token__user', ) def get_queryset(self, *args, **kwargs): qs = super().get_queryset(*args, **kwargs) return qs.select_related('token__user') def token_jti(self, obj): return obj.token.jti token_jti.short_description = _('jti') token_jti.admin_order_field = 'token__jti' def token_user(self, obj): return obj.token.user token_user.short_description = _('user') token_user.admin_order_field = 'token__user' def token_created_at(self, obj): return obj.token.created_at token_created_at.short_description = _('created at') token_created_at.admin_order_field = 'token__created_at' def token_expires_at(self, obj): return obj.token.expires_at token_expires_at.short_description = _('expires at') token_expires_at.admin_order_field = 'token__expires_at' admin.site.register(BlacklistedToken, BlacklistedTokenAdmin) djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/apps.py000066400000000000000000000004311413160305600314200ustar00rootroot00000000000000from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ class TokenBlacklistConfig(AppConfig): name = 'rest_framework_simplejwt.token_blacklist' verbose_name = _('Token Blacklist') default_auto_field = 'django.db.models.BigAutoField' djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/management/000077500000000000000000000000001413160305600322215ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/management/__init__.py000066400000000000000000000000001413160305600343200ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/management/commands/000077500000000000000000000000001413160305600340225ustar00rootroot00000000000000__init__.py000066400000000000000000000000001413160305600360420ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/management/commandsflushexpiredtokens.py000066400000000000000000000005621413160305600402460ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/management/commandsfrom django.core.management.base import BaseCommand from rest_framework_simplejwt.utils import aware_utcnow from ...models import OutstandingToken class Command(BaseCommand): help = 'Flushes any expired tokens in the outstanding token list' def handle(self, *args, **kwargs): OutstandingToken.objects.filter(expires_at__lte=aware_utcnow()).delete() djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrations/000077500000000000000000000000001413160305600322615ustar00rootroot000000000000000001_initial.py000066400000000000000000000026771413160305600346610ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrationsimport django.db.models.deletion from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='BlacklistedToken', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('blacklisted_at', models.DateTimeField(auto_now_add=True)), ], ), migrations.CreateModel( name='OutstandingToken', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('jti', models.UUIDField(unique=True)), ('token', models.TextField()), ('created_at', models.DateTimeField()), ('expires_at', models.DateTimeField()), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ 'ordering': ('user',), }, ), migrations.AddField( model_name='blacklistedtoken', name='token', field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='token_blacklist.OutstandingToken'), ), ] 0002_outstandingtoken_jti_hex.py000066400000000000000000000005571413160305600403360ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrationsfrom django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('token_blacklist', '0001_initial'), ] operations = [ migrations.AddField( model_name='outstandingtoken', name='jti_hex', field=models.CharField(blank=True, null=True, max_length=255), ), ] 0003_auto_20171017_2007.py000066400000000000000000000016131413160305600356210ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrationsfrom uuid import UUID from django.db import migrations def populate_jti_hex(apps, schema_editor): OutstandingToken = apps.get_model('token_blacklist', 'OutstandingToken') db_alias = schema_editor.connection.alias for token in OutstandingToken.objects.using(db_alias).all(): token.jti_hex = token.jti.hex token.save() def reverse_populate_jti_hex(apps, schema_editor): # pragma: no cover OutstandingToken = apps.get_model('token_blacklist', 'OutstandingToken') db_alias = schema_editor.connection.alias for token in OutstandingToken.objects.using(db_alias).all(): token.jti = UUID(hex=token.jti_hex) token.save() class Migration(migrations.Migration): dependencies = [ ('token_blacklist', '0002_outstandingtoken_jti_hex'), ] operations = [ migrations.RunPython(populate_jti_hex, reverse_populate_jti_hex), ] 0004_auto_20171017_2013.py000066400000000000000000000005621413160305600356210ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrationsfrom django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('token_blacklist', '0003_auto_20171017_2007'), ] operations = [ migrations.AlterField( model_name='outstandingtoken', name='jti_hex', field=models.CharField(unique=True, max_length=255), ), ] 0005_remove_outstandingtoken_jti.py000066400000000000000000000004461413160305600410470ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrationsfrom django.db import migrations class Migration(migrations.Migration): dependencies = [ ('token_blacklist', '0004_auto_20171017_2013'), ] operations = [ migrations.RemoveField( model_name='outstandingtoken', name='jti', ), ] 0006_auto_20171017_2113.py000066400000000000000000000005231413160305600356210ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrationsfrom django.db import migrations class Migration(migrations.Migration): dependencies = [ ('token_blacklist', '0005_remove_outstandingtoken_jti'), ] operations = [ migrations.RenameField( model_name='outstandingtoken', old_name='jti_hex', new_name='jti', ), ] 0007_auto_20171017_2214.py000066400000000000000000000012511413160305600356230ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrationsimport django.db.models.deletion from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('token_blacklist', '0006_auto_20171017_2113'), ] operations = [ migrations.AlterField( model_name='outstandingtoken', name='created_at', field=models.DateTimeField(blank=True, null=True), ), migrations.AlterField( model_name='outstandingtoken', name='user', field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), ), ] 0008_migrate_to_bigautofield.py000066400000000000000000000011701413160305600400720ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrationsfrom django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('token_blacklist', '0007_auto_20171017_2214'), ] operations = [ migrations.AlterField( model_name='blacklistedtoken', name='id', field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), ), migrations.AlterField( model_name='outstandingtoken', name='id', field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), ), ] 0010_fix_migrate_to_bigautofield.py000066400000000000000000000012651413160305600407360ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrations# Generated by Django 3.2.3 on 2021-05-27 17:46 from pathlib import Path from django.db import migrations, models parent_dir = Path(__file__).resolve(strict=True).parent class Migration(migrations.Migration): dependencies = [ ('token_blacklist', '0008_migrate_to_bigautofield'), ] operations = [ migrations.AlterField( model_name='blacklistedtoken', name='id', field=models.BigAutoField(primary_key=True, serialize=False), ), migrations.AlterField( model_name='outstandingtoken', name='id', field=models.BigAutoField(primary_key=True, serialize=False), ), ] 0011_linearizes_history.py000066400000000000000000000011361413160305600371440ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrationsimport fnmatch import os from pathlib import Path from django.db import migrations, models # noqa F401 parent_dir = Path(__file__).resolve(strict=True).parent class Migration(migrations.Migration): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.dependencies = [ ('token_blacklist', '0010_fix_migrate_to_bigautofield') ] _m = sorted(fnmatch.filter(os.listdir(parent_dir), "000*.py")) if len(_m) == 9: self.dependencies.insert(0, ('token_blacklist', os.path.splitext(_m[8])[0])) operations = [ ] djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/migrations/__init__.py000066400000000000000000000000001413160305600343600ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/token_blacklist/models.py000066400000000000000000000030761413160305600317500ustar00rootroot00000000000000from django.conf import settings from django.db import models class OutstandingToken(models.Model): id = models.BigAutoField(primary_key=True, serialize=False) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True) jti = models.CharField(unique=True, max_length=255) token = models.TextField() created_at = models.DateTimeField(null=True, blank=True) expires_at = models.DateTimeField() class Meta: # Work around for a bug in Django: # https://code.djangoproject.com/ticket/19422 # # Also see corresponding ticket: # https://github.com/encode/django-rest-framework/issues/705 abstract = 'rest_framework_simplejwt.token_blacklist' not in settings.INSTALLED_APPS ordering = ('user',) def __str__(self): return 'Token for {} ({})'.format( self.user, self.jti, ) class BlacklistedToken(models.Model): id = models.BigAutoField(primary_key=True, serialize=False) token = models.OneToOneField(OutstandingToken, on_delete=models.CASCADE) blacklisted_at = models.DateTimeField(auto_now_add=True) class Meta: # Work around for a bug in Django: # https://code.djangoproject.com/ticket/19422 # # Also see corresponding ticket: # https://github.com/encode/django-rest-framework/issues/705 abstract = 'rest_framework_simplejwt.token_blacklist' not in settings.INSTALLED_APPS def __str__(self): return 'Blacklisted token for {}'.format(self.token.user) djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/tokens.py000066400000000000000000000245251413160305600266220ustar00rootroot00000000000000from datetime import timedelta from uuid import uuid4 from django.conf import settings from django.utils.translation import gettext_lazy as _ from django.utils.module_loading import import_string from .exceptions import TokenBackendError, TokenError from .settings import api_settings from .token_blacklist.models import BlacklistedToken, OutstandingToken from .utils import ( aware_utcnow, datetime_from_epoch, datetime_to_epoch, format_lazy, ) class Token: """ A class which validates and wraps an existing JWT or can be used to build a new JWT. """ token_type = None lifetime = None def __init__(self, token=None, verify=True): """ !!!! IMPORTANT !!!! MUST raise a TokenError with a user-facing error message if the given token is invalid, expired, or otherwise not safe to use. """ if self.token_type is None or self.lifetime is None: raise TokenError(_('Cannot create token with no type or lifetime')) self.token = token self.current_time = aware_utcnow() # Set up token if token is not None: # An encoded token was provided token_backend = self.get_token_backend() # Decode token try: self.payload = token_backend.decode(token, verify=verify) except TokenBackendError: raise TokenError(_('Token is invalid or expired')) if verify: self.verify() else: # New token. Skip all the verification steps. self.payload = {api_settings.TOKEN_TYPE_CLAIM: self.token_type} # Set "exp" and "iat" claims with default value self.set_exp(from_time=self.current_time, lifetime=self.lifetime) self.set_iat(at_time=self.current_time) # Set "jti" claim self.set_jti() def __repr__(self): return repr(self.payload) def __getitem__(self, key): return self.payload[key] def __setitem__(self, key, value): self.payload[key] = value def __delitem__(self, key): del self.payload[key] def __contains__(self, key): return key in self.payload def get(self, key, default=None): return self.payload.get(key, default) def __str__(self): """ Signs and returns a token as a base64 encoded string. """ return self.get_token_backend().encode(self.payload) def verify(self): """ Performs additional validation steps which were not performed when this token was decoded. This method is part of the "public" API to indicate the intention that it may be overridden in subclasses. """ # According to RFC 7519, the "exp" claim is OPTIONAL # (https://tools.ietf.org/html/rfc7519#section-4.1.4). As a more # correct behavior for authorization tokens, we require an "exp" # claim. We don't want any zombie tokens walking around. self.check_exp() # Ensure token id is present if api_settings.JTI_CLAIM not in self.payload: raise TokenError(_('Token has no id')) self.verify_token_type() def verify_token_type(self): """ Ensures that the token type claim is present and has the correct value. """ try: token_type = self.payload[api_settings.TOKEN_TYPE_CLAIM] except KeyError: raise TokenError(_('Token has no type')) if self.token_type != token_type: raise TokenError(_('Token has wrong type')) def set_jti(self): """ Populates the configured jti claim of a token with a string where there is a negligible probability that the same string will be chosen at a later time. See here: https://tools.ietf.org/html/rfc7519#section-4.1.7 """ self.payload[api_settings.JTI_CLAIM] = uuid4().hex def set_exp(self, claim='exp', from_time=None, lifetime=None): """ Updates the expiration time of a token. See here: https://tools.ietf.org/html/rfc7519#section-4.1.4 """ if from_time is None: from_time = self.current_time if lifetime is None: lifetime = self.lifetime self.payload[claim] = datetime_to_epoch(from_time + lifetime) def set_iat(self, claim='iat', at_time=None): """ Updates the time at which the token was issued. See here: https://tools.ietf.org/html/rfc7519#section-4.1.6 """ if at_time is None: at_time = self.current_time self.payload[claim] = datetime_to_epoch(at_time) def check_exp(self, claim='exp', current_time=None): """ Checks whether a timestamp value in the given claim has passed (since the given datetime value in `current_time`). Raises a TokenError with a user-facing error message if so. """ if current_time is None: current_time = self.current_time try: claim_value = self.payload[claim] except KeyError: raise TokenError(format_lazy(_("Token has no '{}' claim"), claim)) claim_time = datetime_from_epoch(claim_value) if claim_time <= current_time: raise TokenError(format_lazy(_("Token '{}' claim has expired"), claim)) @classmethod def for_user(cls, user): """ Returns an authorization token for the given user that will be provided after authenticating the user's credentials. """ user_id = getattr(user, api_settings.USER_ID_FIELD) if not isinstance(user_id, int): user_id = str(user_id) token = cls() token[api_settings.USER_ID_CLAIM] = user_id return token _token_backend = None def get_token_backend(self): if self._token_backend is None: self._token_backend = import_string( "rest_framework_simplejwt.state.token_backend" ) return self._token_backend class BlacklistMixin: """ If the `rest_framework_simplejwt.token_blacklist` app was configured to be used, tokens created from `BlacklistMixin` subclasses will insert themselves into an outstanding token list and also check for their membership in a token blacklist. """ if 'rest_framework_simplejwt.token_blacklist' in settings.INSTALLED_APPS: def verify(self, *args, **kwargs): self.check_blacklist() super().verify(*args, **kwargs) def check_blacklist(self): """ Checks if this token is present in the token blacklist. Raises `TokenError` if so. """ jti = self.payload[api_settings.JTI_CLAIM] if BlacklistedToken.objects.filter(token__jti=jti).exists(): raise TokenError(_('Token is blacklisted')) def blacklist(self): """ Ensures this token is included in the outstanding token list and adds it to the blacklist. """ jti = self.payload[api_settings.JTI_CLAIM] exp = self.payload['exp'] # Ensure outstanding token exists with given jti token, _ = OutstandingToken.objects.get_or_create( jti=jti, defaults={ 'token': str(self), 'expires_at': datetime_from_epoch(exp), }, ) return BlacklistedToken.objects.get_or_create(token=token) @classmethod def for_user(cls, user): """ Adds this token to the outstanding token list. """ token = super().for_user(user) jti = token[api_settings.JTI_CLAIM] exp = token['exp'] OutstandingToken.objects.create( user=user, jti=jti, token=str(token), created_at=token.current_time, expires_at=datetime_from_epoch(exp), ) return token class SlidingToken(BlacklistMixin, Token): token_type = 'sliding' lifetime = api_settings.SLIDING_TOKEN_LIFETIME def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.token is None: # Set sliding refresh expiration claim if new token self.set_exp( api_settings.SLIDING_TOKEN_REFRESH_EXP_CLAIM, from_time=self.current_time, lifetime=api_settings.SLIDING_TOKEN_REFRESH_LIFETIME, ) class RefreshToken(BlacklistMixin, Token): token_type = 'refresh' lifetime = api_settings.REFRESH_TOKEN_LIFETIME no_copy_claims = ( api_settings.TOKEN_TYPE_CLAIM, 'exp', # Both of these claims are included even though they may be the same. # It seems possible that a third party token might have a custom or # namespaced JTI claim as well as a default "jti" claim. In that case, # we wouldn't want to copy either one. api_settings.JTI_CLAIM, 'jti', ) @property def access_token(self): """ Returns an access token created from this refresh token. Copies all claims present in this refresh token to the new access token except those claims listed in the `no_copy_claims` attribute. """ access = AccessToken() # Use instantiation time of refresh token as relative timestamp for # access token "exp" claim. This ensures that both a refresh and # access token expire relative to the same time if they are created as # a pair. access.set_exp(from_time=self.current_time) no_copy = self.no_copy_claims for claim, value in self.payload.items(): if claim in no_copy: continue access[claim] = value return access class AccessToken(Token): token_type = 'access' lifetime = api_settings.ACCESS_TOKEN_LIFETIME class UntypedToken(Token): token_type = 'untyped' lifetime = timedelta(seconds=0) def verify_token_type(self): """ Untyped tokens do not verify the "token_type" claim. This is useful when performing general validation of a token's signature and other properties which do not relate to the token's intended use. """ pass djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/utils.py000066400000000000000000000011751413160305600264530ustar00rootroot00000000000000from calendar import timegm from datetime import datetime from django.conf import settings from django.utils.functional import lazy from django.utils.timezone import is_naive, make_aware, utc def make_utc(dt): if settings.USE_TZ and is_naive(dt): return make_aware(dt, timezone=utc) return dt def aware_utcnow(): return make_utc(datetime.utcnow()) def datetime_to_epoch(dt): return timegm(dt.utctimetuple()) def datetime_from_epoch(ts): return make_utc(datetime.utcfromtimestamp(ts)) def format_lazy(s, *args, **kwargs): return s.format(*args, **kwargs) format_lazy = lazy(format_lazy, str) djangorestframework-simplejwt-5.0.0/rest_framework_simplejwt/views.py000066400000000000000000000051731413160305600264520ustar00rootroot00000000000000from rest_framework import generics, status from rest_framework.response import Response from . import serializers from .authentication import AUTH_HEADER_TYPES from .exceptions import InvalidToken, TokenError class TokenViewBase(generics.GenericAPIView): permission_classes = () authentication_classes = () serializer_class = None www_authenticate_realm = 'api' def get_authenticate_header(self, request): return '{0} realm="{1}"'.format( AUTH_HEADER_TYPES[0], self.www_authenticate_realm, ) def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) try: serializer.is_valid(raise_exception=True) except TokenError as e: raise InvalidToken(e.args[0]) return Response(serializer.validated_data, status=status.HTTP_200_OK) class TokenObtainPairView(TokenViewBase): """ Takes a set of user credentials and returns an access and refresh JSON web token pair to prove the authentication of those credentials. """ serializer_class = serializers.TokenObtainPairSerializer token_obtain_pair = TokenObtainPairView.as_view() class TokenRefreshView(TokenViewBase): """ Takes a refresh type JSON web token and returns an access type JSON web token if the refresh token is valid. """ serializer_class = serializers.TokenRefreshSerializer token_refresh = TokenRefreshView.as_view() class TokenObtainSlidingView(TokenViewBase): """ Takes a set of user credentials and returns a sliding JSON web token to prove the authentication of those credentials. """ serializer_class = serializers.TokenObtainSlidingSerializer token_obtain_sliding = TokenObtainSlidingView.as_view() class TokenRefreshSlidingView(TokenViewBase): """ Takes a sliding JSON web token and returns a new, refreshed version if the token's refresh period has not expired. """ serializer_class = serializers.TokenRefreshSlidingSerializer token_refresh_sliding = TokenRefreshSlidingView.as_view() class TokenVerifyView(TokenViewBase): """ Takes a token and indicates if it is valid. This view provides no information about a token's fitness for a particular use. """ serializer_class = serializers.TokenVerifySerializer token_verify = TokenVerifyView.as_view() class TokenBlacklistView(TokenViewBase): """ Takes a token and blacklists it. Must be used with the `rest_framework_simplejwt.token_blacklist` app installed. """ serializer_class = serializers.TokenBlacklistSerializer token_blacklist = TokenBlacklistView.as_view() djangorestframework-simplejwt-5.0.0/setup.cfg000066400000000000000000000000461413160305600214260ustar00rootroot00000000000000[metadata] license_file = LICENSE.txt djangorestframework-simplejwt-5.0.0/setup.py000077500000000000000000000043331413160305600213250ustar00rootroot00000000000000#!/usr/bin/env python from pathlib import Path from setuptools import ( setup, find_packages, ) extras_require = { 'test': [ 'cryptography', 'pytest-cov', 'pytest-django', 'pytest-xdist', 'pytest', 'tox', ], 'lint': [ 'flake8', 'pep8', 'isort', ], 'doc': [ 'Sphinx>=1.6.5,<2', 'sphinx_rtd_theme>=0.1.9', ], 'dev': [ 'pytest-watch', 'wheel', 'twine', 'ipython', ], 'python-jose': [ 'python-jose==3.0.0', ], } extras_require['dev'] = ( extras_require['dev'] + # noqa: W504 extras_require['test'] + # noqa: W504 extras_require['lint'] + # noqa: W504 extras_require['doc'] + # noqa: W504 extras_require['python-jose'] ) setup( name='djangorestframework_simplejwt', use_scm_version={"version_scheme": "post-release"}, setup_requires=["setuptools_scm"], url='https://github.com/jazzband/djangorestframework-simplejwt', license='MIT', description='A minimal JSON Web Token authentication plugin for Django REST Framework', long_description=Path('README.rst').read_text(encoding='utf-8'), author='David Sanders', author_email='davesque@gmail.com', install_requires=[ 'django', 'djangorestframework', 'pyjwt>=2,<3', ], python_requires='>=3.7', extras_require=extras_require, packages=find_packages(exclude=['tests', 'tests.*', 'licenses', 'requirements']), include_package_data=True, zip_safe=False, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', "Framework :: Django :: 2.2", "Framework :: Django :: 3.1", "Framework :: Django :: 3.2", 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Topic :: Internet :: WWW/HTTP', ] ) djangorestframework-simplejwt-5.0.0/tests/000077500000000000000000000000001413160305600207475ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/tests/__init__.py000066400000000000000000000000001413160305600230460ustar00rootroot00000000000000djangorestframework-simplejwt-5.0.0/tests/conftest.py000066400000000000000000000033461413160305600231540ustar00rootroot00000000000000def pytest_configure(): from django.conf import settings MIDDLEWARE = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ) settings.configure( DEBUG_PROPAGATE_EXCEPTIONS=True, DATABASES={ 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:' }, 'other': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'other' } }, SITE_ID=1, SECRET_KEY='not very secret in tests', USE_I18N=True, USE_L10N=True, STATIC_URL='/static/', ROOT_URLCONF='tests.urls', TEMPLATES=[ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, }, ], MIDDLEWARE=MIDDLEWARE, MIDDLEWARE_CLASSES=MIDDLEWARE, INSTALLED_APPS=( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.staticfiles', 'rest_framework', 'rest_framework_simplejwt', 'rest_framework_simplejwt.token_blacklist', 'tests', ), PASSWORD_HASHERS=( 'django.contrib.auth.hashers.MD5PasswordHasher', ), SIMPLE_JWT={ 'BLACKLIST_AFTER_ROTATION': True, }, ) try: import django django.setup() except AttributeError: pass djangorestframework-simplejwt-5.0.0/tests/keys.py000066400000000000000000000141661413160305600223040ustar00rootroot00000000000000PRIVATE_KEY = """ -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA3xMJfyl8TOdrsjDLSIodsArJ/NnQB3ZdfbFC5onxATDfRLLA CHFo3ye694doBKeSe1NFYbfXPvahl6ODX1a23oQyoRQwlL+M99cLcdCa0gGuJXdb AaF6Em8E+7uSb3290mI+rZmjqyc7gMtKVWKL4e5i2PerFFBoYkZ7E90KOp2t0ZAD x2uqF4VTOfYLHG0cPgSw9/ptDStJqJVAOiRRqbv0j0GOFMDYNcN0mDlnpryhQFbQ iMqn4IJIURZUVBJujFSa45cJPvSmMb6NrzZ1crg5UN6/5Mu2mxQzAi21+vpgGL+E EuekUd7sRgEAjTHjLKzotLAGo7EGa8sL1vMSFwIDAQABAoIBAQCGGWabF/BONswq CWUazVR9cG7uXm3NHp2jIr1p40CLC7scDCyeprZ5d+PQS4j/S1Ema++Ih8CQbCjG BJjD5lf2OhhJdt6hfOkcUBzkJZf8aOAsS6zctRqyHCUtwxuLhFZpM4AkUfjuuZ3u lcawv5YBkpG/hltE0fV+Jop0bWtpwiKxVsHXVcS0WEPXic0lsOTBCw8m81JXqjir PCBOnkxgNpHSt69S1xnW3l9fPUWVlduO3EIZ5PZG2BxU081eZW31yIlKsDJhfgm6 R5Vlr5DynqeojAd6SNliCzNXZP28GOpQBrYIeVQWA1yMANvkvd4apz9GmDrjF/Fd g8Chah+5AoGBAPc/+zyuDZKVHK7MxwLPlchCm5Zb4eou4ycbwEB+P3gDS7MODGu4 qvx7cstTZMuMavNRcJsfoiMMrke9JrqGe4rFGiKRFLVBY2Xwr+95pKNC11EWI1lF 5qDAmreDsj2alVJT5yZ9hsAWTsk2i+xj+/XHWYVkr67pRvOPRAmGMB+NAoGBAOb4 CBHe184Hn6Ie+gSD4OjewyUVmr3JDJ41s8cjb1kBvDJ/wv9Rvo9yz2imMr2F0YGc ytHraM77v8KOJuJWpvGjEg8I0a/rSttxWQ+J0oYJSIPn+eDpAijNWfOp1aKRNALT pboCXcnSn+djJFKkNJ2hR7R/vrrM6Jyly1jcVS0zAoGAQpdt4Cr0pt0YS5AFraEh Mz2VUArRLtSQA3F69yPJjlY85i3LdJvZGYVaJp8AT74y8/OkQ3NipNP+gH3WV3hu /7IUVukCTcsdrVAE4pe9mucevM0cmie0dOlLAlArCmJ/Axxr7jbyuvuHHrRdPT60 lr6pQr8afh6AKIsWhQYqIeUCgYA+v9IJcN52hhGzjPDl+yJGggbIc3cn6pA4B2UB TDo7F0KXAajrjrzT4iBBUS3l2Y5SxVNA9tDxsumlJNOhmGMgsOn+FapKPgWHWuMU WqBMdAc0dvinRwakKS4wCcsVsJdN0UxsHap3Y3a3+XJr1VrKHIALpM0fmP31WQHG 8Y1eiwKBgF6AYXxo0FzZacAommZrAYoxFZT1u4/rE/uvJ2K9HYRxLOVKZe+89ki3 D7AOmrxe/CAc/D+nNrtUIv3RFGfadfSBWzyLw36ekW76xPdJgqJsSz5XJ/FgzDW+ WNC5oOtiPOMCymP75oKOjuZJZ2SPLRmiuO/qvI5uAzBHxRC1BKdt -----END RSA PRIVATE KEY----- """ PUBLIC_KEY = """ -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3xMJfyl8TOdrsjDLSIod sArJ/NnQB3ZdfbFC5onxATDfRLLACHFo3ye694doBKeSe1NFYbfXPvahl6ODX1a2 3oQyoRQwlL+M99cLcdCa0gGuJXdbAaF6Em8E+7uSb3290mI+rZmjqyc7gMtKVWKL 4e5i2PerFFBoYkZ7E90KOp2t0ZADx2uqF4VTOfYLHG0cPgSw9/ptDStJqJVAOiRR qbv0j0GOFMDYNcN0mDlnpryhQFbQiMqn4IJIURZUVBJujFSa45cJPvSmMb6NrzZ1 crg5UN6/5Mu2mxQzAi21+vpgGL+EEuekUd7sRgEAjTHjLKzotLAGo7EGa8sL1vMS FwIDAQAB -----END PUBLIC KEY----- """ PRIVATE_KEY_2 = """ -----BEGIN RSA PRIVATE KEY----- MIIJKQIBAAKCAgEAwpwcivLRv5T/2pb9zZpfh4Fy1vla3vm5N2WtkhjB0DX1HmOh SN9O1Q3byW9VLQtuNAdZ8+hc2jU5DIVApFF+b1Uo1C76qZWOLLwK/yvweACTdT4M ISeYHaUd/B7YUNtUQEZF0xxjMM8jRXEuI902pLEUBx9cd2d8KzCgXyC2dsNIt3zg WCu6RhL1V10lhBXMwl0N1+DAIRsXjCazwaxBMrTNOsXrex98aaPk3++V1KkPbLnV Zk8I7dbpt9EYSvMB7DaXFiLL4XySamFfYmtq9n9bxKXxnzpObolWkD0SbioaxkCq vEFyy2/y4ZIxYC1mHhRgTtSicnk8WGOgv6Ax1sRHpbRVDtXMmDHn7oiJdd8YCGvT 9AQoibeccPUZbwaiuiULSVjphKErXLg7nH6/ct8bVYZnh0GnYTljC3O0f3/D1A9Z PxLHJPc9K2GCmZ+TNMmDDmGbLz/TgFiSZE3CQEORxbGyNh5MZuIrGub9xaKB4Hon Rf8HXwVa/zL4dKxpzYzHNBH8wqPOWCLXjp65lcMW7JBAnrqshI0wOCEbk13uLudB aNL4k/h9M9RKaEW24tKpg29XsWu9II3Bt94x7gwhokE7gICsycUSQWTGaBovbvn0 C8k4gkPq6teGAD6AJ6/YV2hiPbdoRL5EkMS3E01hmaHk9xlOpo73IjUxhXUCAwEA AQKCAgEAjEfNx1cbXNdBysa2cuOJYvsr1cxu9XXbThRsFnjkFHsgkuRMWWQmxis0 ODKZmlu396co70mazOw6kEzpeMkJs6UWRkULCP02PAbcgm2g7E+1+3hbc/a/jvb7 80Yktbw0MhS1tmSrF37otODN2qpV/kdq4Wt40tV0ywlFQO0qudcw7psEeGok3uhB k9Uf+uNf8uby2J84v2RxB+TKBJxvbuanXWtXwCvFGb07eTSRs3aeGMioDBSCojcd yBPgR/59b1E2fY1dm8+ZFzfTcvVtZ/wMIWdhEV8NNF6pWFW9mE2feTMaH5Op9P1g fbtM/kAbcSlM9uYNpyi/GBPQxvDpmttbBVyuSx2G7ct9EeMjJQ0QjC6DWG3zwz/b S8f9y/K2pnzKZdUrQBI9RRYu2OqHlfLQ/RWnr2/mRvFr9bd3pZEYchnB/PyKKvZB eS0X3LibP7ktmSFyB/xtsap/S/qHf9acY5Uu7w0gSXoNFTAG1zW/cHTXMYC0oYlD L8F3fO7ddo2nx2YOxEm5e5GDgc3V952GTgFZclsc6jn6AkOVyzoPYEfJy3JjPyTv doDrK5lPJ5ekmdyTdMhw376dHmkSB4D+27U/WMHN0EgMCmJznoWhvGuLG/mLbK+q d/K3Cy3ipUJDhb3OrDzfJ+Ps7E8BYBxZp69yhV5gV1T40pgG/WECggEBAPtF+3Uh CBh5amhFQOHc4hMR8UGTOBWF/uz15+PImzLcpN/gpgoWaIExQsrbQCStemMO7hhi N3/pf1+V0hbRb8cLN/B/BG/31pw3bCJKB+nYr1elCHOhS9+1txekcoqA3pVdmyZ0 TcyTczgFluXmWurIJqj9dp+KOJlh6Q6qehKNX6D/E9RszuZiHOeKzgaDCy+WI3DK bxtthfddasBxBYji6ObRn0BY6RjUnkHAra6Lib3M8Y8qxJnQozjBuWCwf2bflN++ 2tcO/m5s2tksNDWfu4q+ruh2zWGPbaEvhEs0o2z0kucCukEBqqbMT+p9HdFbkom4 XJOT4ZUCJfCdGIkCggEBAMZFQ3BW4I18TVRaMQMv/5ivp9/Qd/Ls/jU+wfiiZnq9 zT5a/9LkI/rWq4G+gT93KxRtH43FpJlo1N+1e4sEYf6a0gcgo8inyE2MZCJFuN7g GbOU/qgzLmCtHmbUjyHNzBk/+SK2Jh4PpF3DLOSU8+AWmI8aF9Cl7UojXDTLGhmR qS7MBv/jNUT93xTJUrSvkp2HWF8GOwrVd9CUo6zNnFb3nIP3fUARCpa/aurPaiim U+mv7NlpK/wUiP8dWUu3+hqa3yPE9WGHixdfMd6o4KfAyqMb5WErR1laU3wbl0B1 FzFxECn5Dkt9p93LzXMIn58eT2ZeneNdzWKQn0BOco0CggEBAOoN9/rUt+vEPR+/ Un6Q92z4C5gff+Bcnmcvb783v4kTCekYItHGqbWdoy++JvODPDtFTvcblcLqRyFM NxPWJp5rjsHQLtv1Kcz9uxX9i32Bv2KOcV7z4e8SHuhA4AivnaXYOYsKTuW+e1a1 rieb+Rg1M/25i2N0puAI2cQ1e9wIIAmhUGFQsTDcNzxeiSZ7rlG3Mm//wJr13BHc zHFRVex6IKPQotyXdRkSBBAPYDjz9Wv8mQ3YsqTsOP3HRdwQy7uRi+UWrFYiu1E0 yG3+xOsmTNUiZV5YO1si9OVtk3dSIuB8uNHCMqgW21Tff5lWzg2TlN4AAwvcdgYM qDaGvrECggEAOT+IkGhVYCTzAxcjrcLvLzwQ4dwEtlzdrawYP91Mb8Zb+9Q0p8T9 6pCPZuAF27hh9PzpLntR4oXVaV6ydFponSZA3JP9FpPzjwipZQfysE/Ou/6aZSCa FIoIDDL1vRH6C5RgMDid2vIzSGtxi/LCVALSPAeRtsoiMNTy6791IsrfKcb5gmst V2ViQ1M6ETfcwqVwy8c1xxQKC2zPsbaQnL/ULnqIbLY+83YDvhbzlRcphYEph0EJ 1ThsshTcUrOlgIcVRPO60lVbwPzYnmzuqSFOoTgNzDe92zvsfRpOWus0Li9yNlxW V0/J543QHZXw2PXcgTdyqVLNWddeVCgShQKCAQBp63G+/O26R/jYjh74FCg0PQ6r yAk2kmjHLsIPRFIiW/u6DXn5hufk2wM/JTQf/9LRXhjLdzqehOoQasQSJAR+wiOx CeTDD91sHtMqQb0sAbQyLGKsDBG3dfnB1BEGQccfZnDZKx7N7kvzkN5/4CYpscWn CNnBq6IE9iu31w3VEKxAc0bdLxPdOw79NhgwzW1JgysFBQAEdtZ1qlMPQOj9IMnB eszY1HhshLbaaUwuG8SUDvKzZUpEEq711yQoW4y1yzOrcMmTYzRFuMczcB4v0tqe /sno2/CDUEKR5SsDgnqB9hPzFJDclfN/MPdpx9X29JF1RlGsz8RJS29ztWyh -----END RSA PRIVATE KEY----- """ PUBLIC_KEY_2 = """ -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwpwcivLRv5T/2pb9zZpf h4Fy1vla3vm5N2WtkhjB0DX1HmOhSN9O1Q3byW9VLQtuNAdZ8+hc2jU5DIVApFF+ b1Uo1C76qZWOLLwK/yvweACTdT4MISeYHaUd/B7YUNtUQEZF0xxjMM8jRXEuI902 pLEUBx9cd2d8KzCgXyC2dsNIt3zgWCu6RhL1V10lhBXMwl0N1+DAIRsXjCazwaxB MrTNOsXrex98aaPk3++V1KkPbLnVZk8I7dbpt9EYSvMB7DaXFiLL4XySamFfYmtq 9n9bxKXxnzpObolWkD0SbioaxkCqvEFyy2/y4ZIxYC1mHhRgTtSicnk8WGOgv6Ax 1sRHpbRVDtXMmDHn7oiJdd8YCGvT9AQoibeccPUZbwaiuiULSVjphKErXLg7nH6/ ct8bVYZnh0GnYTljC3O0f3/D1A9ZPxLHJPc9K2GCmZ+TNMmDDmGbLz/TgFiSZE3C QEORxbGyNh5MZuIrGub9xaKB4HonRf8HXwVa/zL4dKxpzYzHNBH8wqPOWCLXjp65 lcMW7JBAnrqshI0wOCEbk13uLudBaNL4k/h9M9RKaEW24tKpg29XsWu9II3Bt94x 7gwhokE7gICsycUSQWTGaBovbvn0C8k4gkPq6teGAD6AJ6/YV2hiPbdoRL5EkMS3 E01hmaHk9xlOpo73IjUxhXUCAwEAAQ== -----END PUBLIC KEY----- """ djangorestframework-simplejwt-5.0.0/tests/models.py000066400000000000000000000000221413160305600225760ustar00rootroot00000000000000# Add models here djangorestframework-simplejwt-5.0.0/tests/test_authentication.py000066400000000000000000000161141413160305600254020ustar00rootroot00000000000000from datetime import timedelta from importlib import reload from django.contrib.auth import get_user_model from django.test import TestCase from rest_framework.test import APIRequestFactory from rest_framework_simplejwt import authentication from rest_framework_simplejwt.exceptions import ( AuthenticationFailed, InvalidToken, ) from rest_framework_simplejwt.models import TokenUser from rest_framework_simplejwt.settings import api_settings from rest_framework_simplejwt.tokens import AccessToken, SlidingToken from .utils import override_api_settings User = get_user_model() AuthToken = api_settings.AUTH_TOKEN_CLASSES[0] class TestJWTAuthentication(TestCase): def setUp(self): self.factory = APIRequestFactory() self.backend = authentication.JWTAuthentication() self.fake_token = b'TokenMcTokenface' self.fake_header = b'Bearer ' + self.fake_token def test_get_header(self): # Should return None if no authorization header request = self.factory.get('/test-url/') self.assertIsNone(self.backend.get_header(request)) # Should pull correct header off request request = self.factory.get('/test-url/', HTTP_AUTHORIZATION=self.fake_header) self.assertEqual(self.backend.get_header(request), self.fake_header) # Should work for unicode headers request = self.factory.get('/test-url/', HTTP_AUTHORIZATION=self.fake_header.decode('utf-8')) self.assertEqual(self.backend.get_header(request), self.fake_header) # Should work with the x_access_token with override_api_settings(AUTH_HEADER_NAME='HTTP_X_ACCESS_TOKEN'): # Should pull correct header off request when using X_ACCESS_TOKEN request = self.factory.get('/test-url/', HTTP_X_ACCESS_TOKEN=self.fake_header) self.assertEqual(self.backend.get_header(request), self.fake_header) # Should work for unicode headers when using request = self.factory.get('/test-url/', HTTP_X_ACCESS_TOKEN=self.fake_header.decode('utf-8')) self.assertEqual(self.backend.get_header(request), self.fake_header) def test_get_raw_token(self): # Should return None if header lacks correct type keyword with override_api_settings(AUTH_HEADER_TYPES='JWT'): reload(authentication) self.assertIsNone(self.backend.get_raw_token(self.fake_header)) reload(authentication) # Should return None if an empty AUTHORIZATION header is sent self.assertIsNone(self.backend.get_raw_token(b'')) # Should raise error if header is malformed with self.assertRaises(AuthenticationFailed): self.backend.get_raw_token(b'Bearer one two') with self.assertRaises(AuthenticationFailed): self.backend.get_raw_token(b'Bearer') # Otherwise, should return unvalidated token in header self.assertEqual(self.backend.get_raw_token(self.fake_header), self.fake_token) # Should return token if header has one of many valid token types with override_api_settings(AUTH_HEADER_TYPES=('JWT', 'Bearer')): reload(authentication) self.assertEqual( self.backend.get_raw_token(self.fake_header), self.fake_token, ) reload(authentication) def test_get_validated_token(self): # Should raise InvalidToken if token not valid token = AuthToken() token.set_exp(lifetime=-timedelta(days=1)) with self.assertRaises(InvalidToken): self.backend.get_validated_token(str(token)) # Otherwise, should return validated token token.set_exp() self.assertEqual(self.backend.get_validated_token(str(token)).payload, token.payload) # Should not accept tokens not included in AUTH_TOKEN_CLASSES sliding_token = SlidingToken() with override_api_settings(AUTH_TOKEN_CLASSES=( 'rest_framework_simplejwt.tokens.AccessToken', )): with self.assertRaises(InvalidToken) as e: self.backend.get_validated_token(str(sliding_token)) messages = e.exception.detail['messages'] self.assertEqual(1, len(messages)) self.assertEqual( { 'token_class': 'AccessToken', 'token_type': 'access', 'message': 'Token has wrong type', }, messages[0], ) # Should accept tokens included in AUTH_TOKEN_CLASSES access_token = AccessToken() sliding_token = SlidingToken() with override_api_settings(AUTH_TOKEN_CLASSES=( 'rest_framework_simplejwt.tokens.AccessToken', 'rest_framework_simplejwt.tokens.SlidingToken', )): self.backend.get_validated_token(str(access_token)) self.backend.get_validated_token(str(sliding_token)) def test_get_user(self): payload = {'some_other_id': 'foo'} # Should raise error if no recognizable user identification with self.assertRaises(InvalidToken): self.backend.get_user(payload) payload[api_settings.USER_ID_CLAIM] = 42 # Should raise exception if user not found with self.assertRaises(AuthenticationFailed): self.backend.get_user(payload) u = User.objects.create_user(username='markhamill') u.is_active = False u.save() payload[api_settings.USER_ID_CLAIM] = getattr(u, api_settings.USER_ID_FIELD) # Should raise exception if user is inactive with self.assertRaises(AuthenticationFailed): self.backend.get_user(payload) u.is_active = True u.save() # Otherwise, should return correct user self.assertEqual(self.backend.get_user(payload).id, u.id) class TestJWTTokenUserAuthentication(TestCase): def setUp(self): self.backend = authentication.JWTTokenUserAuthentication() def test_get_user(self): payload = {'some_other_id': 'foo'} # Should raise error if no recognizable user identification with self.assertRaises(InvalidToken): self.backend.get_user(payload) payload[api_settings.USER_ID_CLAIM] = 42 # Otherwise, should return a token user object user = self.backend.get_user(payload) self.assertIsInstance(user, TokenUser) self.assertEqual(user.id, 42) def test_custom_tokenuser(self): from django.utils.functional import cached_property class BobSaget(TokenUser): @cached_property def username(self): return "bsaget" temp = api_settings.TOKEN_USER_CLASS api_settings.TOKEN_USER_CLASS = BobSaget # Should return a token user object payload = {api_settings.USER_ID_CLAIM: 42} user = self.backend.get_user(payload) self.assertIsInstance(user, api_settings.TOKEN_USER_CLASS) self.assertEqual(user.id, 42) self.assertEqual(user.username, "bsaget") # Restore default TokenUser for future tests api_settings.TOKEN_USER_CLASS = temp djangorestframework-simplejwt-5.0.0/tests/test_backends.py000066400000000000000000000441111413160305600241330ustar00rootroot00000000000000from datetime import datetime, timedelta from unittest import mock from unittest.mock import patch import jwt from django.test import TestCase from jwt import PyJWS, algorithms from rest_framework_simplejwt.backends import TokenBackend from rest_framework_simplejwt.exceptions import TokenBackendError from rest_framework_simplejwt.utils import ( aware_utcnow, datetime_to_epoch, make_utc, ) from tests.keys import PRIVATE_KEY, PRIVATE_KEY_2, PUBLIC_KEY, PUBLIC_KEY_2 SECRET = 'not_secret' AUDIENCE = 'openid-client-id' ISSUER = 'https://www.myoidcprovider.com' JWK_URL = 'https://randomstring.auth0.com/.well-known/jwks.json' LEEWAY = 100 class TestTokenBackend(TestCase): def setUp(self): self.hmac_token_backend = TokenBackend('HS256', SECRET) self.hmac_leeway_token_backend = TokenBackend('HS256', SECRET, leeway=LEEWAY) self.rsa_token_backend = TokenBackend('RS256', PRIVATE_KEY, PUBLIC_KEY) self.aud_iss_token_backend = TokenBackend('RS256', PRIVATE_KEY, PUBLIC_KEY, AUDIENCE, ISSUER) self.payload = {'foo': 'bar'} def test_init(self): # Should reject unknown algorithms with self.assertRaises(TokenBackendError): TokenBackend('oienarst oieanrsto i', 'not_secret') TokenBackend('HS256', 'not_secret') @patch.object(algorithms, 'has_crypto', new=False) def test_init_fails_for_rs_algorithms_when_crypto_not_installed(self): with self.assertRaisesRegex(TokenBackendError, 'You must have cryptography installed to use RS256.'): TokenBackend('RS256', 'not_secret') with self.assertRaisesRegex(TokenBackendError, 'You must have cryptography installed to use RS384.'): TokenBackend('RS384', 'not_secret') with self.assertRaisesRegex(TokenBackendError, 'You must have cryptography installed to use RS512.'): TokenBackend('RS512', 'not_secret') def test_encode_hmac(self): # Should return a JSON web token for the given payload payload = {'exp': make_utc(datetime(year=2000, month=1, day=1))} hmac_token = self.hmac_token_backend.encode(payload) # Token could be one of two depending on header dict ordering self.assertIn( hmac_token, ( 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjk0NjY4NDgwMH0.NHpdD2X8ub4SE_MZLBedWa57FCpntGaN_r6f8kNKdUs', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk0NjY4NDgwMH0.jvxQgXCSDToR8uKoRJcMT-LmMJJn2-NM76nfSR2FOgs', ), ) def test_encode_rsa(self): # Should return a JSON web token for the given payload payload = {'exp': make_utc(datetime(year=2000, month=1, day=1))} rsa_token = self.rsa_token_backend.encode(payload) # Token could be one of two depending on header dict ordering self.assertIn( rsa_token, ( 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjk0NjY4NDgwMH0.cuE6ocmdxVHXVrGufzn-_ZbXDV475TPPb5jtSacvJsnR3s3tLIm9yR7MF4vGcCAUGJn2hU_JgSOhs9sxntPPVjdwvC3-sxAwfUQ5AgUCEAF5XC7wTvGhmvufzhAgEG_DNsactCh79P8xRnc0iugtlGNyFp_YK582-ZrlfQEp-7C0L9BNG_JCS2J9DsGR7ojO2xGFkFfzezKRkrVTJMPLwpl0JAiZ0iqbQE-Tex7redCrGI388_mgh52GLsNlSIvW2gQMqCVMYndMuYx32Pd5tuToZmLUQ2PJ9RyAZ4fOMApTzoshItg4lGqtnt9CDYzypHULJZohJIPcxFVZZfHxhw', 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk0NjY4NDgwMH0.pzHTOaVvKJMMkSqksGh-NdeEvQy8Thre3hBM3smUW5Sohtg77KnHpaUYjq30DyRmYQRmPSjEVprh1Yvic_-OeAXPW8WVsF-r4YdJuxWUpuZbIPwJ9E-cMfTZkDkOl18z1zOdlsLtsP2kXyAlptyy9QQsM7AxoqM6cyXoQ5TI0geWccgoahTy3cBtA6pmjm7H0nfeDGqpqYQBhtaFmRuIWn-_XtdN9C6NVmRCcZwyjH-rP3oEm6wtuKJEN25sVWlZm8YRQ-rj7A7SNqBB5tFK2anM_iv4rmBlIEkmr_f2s_WqMxn2EWUSNeqbqiwabR6CZUyJtKx1cPG0B2PqOTcZsg', ), ) def test_encode_aud_iss(self): # Should return a JSON web token for the given payload original_payload = {'exp': make_utc(datetime(year=2000, month=1, day=1))} payload = original_payload.copy() rsa_token = self.aud_iss_token_backend.encode(payload) # Assert that payload has not been mutated by the encode() function self.assertEqual(payload, original_payload) # Token could be one of 12 depending on header dict ordering self.assertIn( rsa_token, ( 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjk0NjY4NDgwMCwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCIsImlzcyI6Imh0dHBzOi8vd3d3Lm15b2lkY3Byb3ZpZGVyLmNvbSJ9.kSz7KyUZgpKaeQHYSQlhsE-UFLG2zhBiJ2MFCIvhstA4lSIKj3U1fdP1OhEDg7X66EquRRIZrby6M7RncqCdsjRwKrEIaL74KgC4s5PDXa_HC6dtpi2GhXqaLz8YxfCPaNGZ_9q9rs4Z4O6WpwBLNmMQrTxNno9p0uT93Z2yKj5hGih8a9C_CSf_rKtsHW9AJShWGoKpR6qQFKVNP1GAwQOQ6IeEvZenq_LSEywnrfiWp4Y5UF7xi42wWx7_YPQtM9_Bp5sB-DbrKg_8t0zSc-OHeVDgH0TKqygGEea09W0QkmJcROkaEbxt2LxJg9OuSdXgudVytV8ewpgNtWNE4g', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjk0NjY4NDgwMCwiaXNzIjoiaHR0cHM6Ly93d3cubXlvaWRjcHJvdmlkZXIuY29tIiwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCJ9.l-sJR5VKGKNrEn5W8sfO4tEpPq4oQ-Fm5ttQyqUkF6FRJHmCfS1TZIUSXieDLHmarnb4hdIGLr5y-EAbykiqYaTn8d25oT2_zIPlCYHt0DxxeuOliGad5l3AXbWee0qPoZL7YCV8FaSdv2EjtMDOEiJBG5yTkaqZlRmSkbfqu1_y2DRErv3X5LpfEHuKoum4jv5YpoCS6wAWDaWJ9cXMPQaGc4gXg4cuSQxb_EjiQ3QYyztHhG37gOu1J-r_rudaiiIk_VZQdYNfCcirp8isS0z2dcNij_0bELp_oOaipsF7uwzc6WfNGR7xP50X1a_K6EBZzVs0eXFxvl9b3C_d8A', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiZXhwIjo5NDY2ODQ4MDAsImlzcyI6Imh0dHBzOi8vd3d3Lm15b2lkY3Byb3ZpZGVyLmNvbSJ9.aTwQEIxSzhI5fN4ffQMzZ6h61Ur-Gzh_gPkgOyyWvMX890Z18tC2RisEjXeL5xDGKe2XiEAVtuJa9CjXB9eJoCxNN1k05C-ph82cco-0m_TbMbs0d1MFnfC9ESr4JKynP_Klxi8bi0iZMazduT15pH4UhRkEGsnp8rOKtlt_8_8UOGBJAzG34161lM4JbZqrZDit1DvQdGxaC0lmMgosKg3NDMECfkPe3pGLJ5F_su5yhQk0xyKNCjYyE2FNlilWoDV2KkGiCWdsFOgRMAJwHZ-cdgPg8Vyh2WthBNblsbRVfDrGtfPX0VuW5B0RhBhvI5Iut34P9kkxKRFo3NaiEg', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiaXNzIjoiaHR0cHM6Ly93d3cubXlvaWRjcHJvdmlkZXIuY29tIiwiZXhwIjo5NDY2ODQ4MDB9.w46s7u28LgsnmK6K_5O15q1SFkKeRgkkplFLi5idq1z7qJjXUi45qpXIyQw3W8a0k1fwa22WB_0XC1MTo22OK3Z0aGNCI2ZCBxvZGOAc1WcCUae44a9LckPHp80q0Hs03NvjsuRVLGXRwDVHrYxuGnFxQSEMbZ650-MQkfVFIXVzMOOAn5Yl4ntjigLcw8iPEqJPnDLdFUSnObZjRzK1M6mJf0-125pqcFsCJaa49rjdbTtnN-VuGnKmv9wV1GwevRQPWjx2vinETURVO9IyZCDtdaLJkvL7Z5IpToK5jrZPc1UWAR0VR8WeWfussFoHzJF86LxVxnqIeXnqOhq5SQ', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3d3dy5teW9pZGNwcm92aWRlci5jb20iLCJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiZXhwIjo5NDY2ODQ4MDB9.Np_SJYye14vz0cvALvfYNqZXvXMD_gY6kIaxA458kbeu6veC_Ds45uWgjJFhTSYFAFWd3TB6M7qZbWgiO0ycION2-B9Yfgaf82WzNtPfgDhu51w1cbLnvuOSRvgX69Q6Z2i1SByewKaSDw25BaMv9Ty4DBdoCbG62qELnNKjDSQvuHlz8cRJv2I6xJBeOYgZV-YN8Zmxsles44a57Vvcj-DjVouHj5m4LperIxb9islNUQBPRTbvw1d_tR8O8ny0mQqbsWL7e2J-wfwdduVf1kVCPePkhHMM6GLhPIrSoTgMzZuRYLBJ61yphuDK98zTknNBM-Jtn5cMyBwP9JBJvA', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3d3dy5teW9pZGNwcm92aWRlci5jb20iLCJleHAiOjk0NjY4NDgwMCwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCJ9.KJcWZtEluPrkRbYj2i_QmdWpZqGZt63Q8nO4QAJ4B4418ZfmgB6A54_vUmWd3Xc18DYgReLoNPlaOuRXtR7rzlMk0-ADjV0bsca5NwTNAV2F-gR9Xsr9iFlcPMNAYf4CAs85gg7deMIxlbGTAaoft__58ah2_vdd8o_nem1PdzsPC198AYtcwnIV206qpeCNR8S_ZTU46OaHwCoySVRx9E7tNG13YSCmGvBaEqgQHKH82qLXo0KivFrjGmGP0xePFG1B8HCZl-LH1euXGGCp6S48q-tmepX5GJwvzaZNBam4pfxQ0GIHa7z11e7sEN-196iDPCK8NzDcXFwHOAnyaA', 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk0NjY4NDgwMCwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCIsImlzcyI6Imh0dHBzOi8vd3d3Lm15b2lkY3Byb3ZpZGVyLmNvbSJ9.MfhVcFN-9Rd0j11CLtxopzREKdyJH1loSpD4ibjP6Aco4-iM5C99B6gliPgOldtuevhneXV2I7NGhmZFULaYhulcLrAgKe3Gj_TK-sHvwb62e14ArugmK_FAhN7UqbX8hU9wP42LaWXqA7ps4kkJSx-sfgHqMzCKewlAZWwyZBoFgWEgoheKZ7ILkSGf0jzBZlS_1R0jFRSrlYD9rI1S4Px-HllS0t32wRCSbzkp6aVMRs46S0ePrN1mK3spsuQXtYhE2913ZC7p2KwyTGfC2FOOeJdRJknh6kI3Z7pTcsjN2jnQN3o8vPEkN3wl7MbAgAsHcUV45pvyxn4SNBmTMQ', 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk0NjY4NDgwMCwiaXNzIjoiaHR0cHM6Ly93d3cubXlvaWRjcHJvdmlkZXIuY29tIiwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCJ9.3NjgS14SGFyJ6cix2XJZFPlcyeSu4LSduEMUIH0grJuCljbhalyoib5s4JnBaK4slKrQv1WHlhnKB737hX1FF7_EwQM3toCf--DBjrIuq5cYK3rzcn71JDe_op3CvClwpVyxd2vQZtQfP_tWqN6cNTuWF8ZQ0rJGug6Zb-NeE5h68YK_9tXLZC_i5anyjjAVONOc3Nd-TeIUBaLQKQXOddw0gcTcA7vg3uS0gXTEDq-_ZkF-v9bn1ua4_lgRPbuaYvrBFbXSCEdvNORPfPz4zfL3XU09q0gmnmXC9nxjUFVX4BjkP_YiCCO42sqUKY4y7STTB_IkK_04e2wntonVZA', 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3d3dy5teW9pZGNwcm92aWRlci5jb20iLCJleHAiOjk0NjY4NDgwMCwiYXVkIjoib3BlbmlkLWNsaWVudC1pZCJ9.b4pdohov81oqzLyCIp4y7e4VYz7LSez7bH0t1o0Zwzau1uXPYXcasT9lxxNMEEiZwHIovPLyWQ6XvF0bMWTk9vc4PyIpkLqsLBJPsuQ-wYUOD04fECmqUX_JaINqm2pPbohTcOQwl0xsE1UMIKTFBZDL1hEXGEMdW9lrPcXilhbC1ikyMpzsmVh55Q_wL2GqydssnOOcDQTqEkWoKvELJJhBcE-YuQkUp8jEVhF3VZ4jEZkzCErTlyXcfe1qXZHkWtw2QNo9s_SfLuRy_fACOo8XE9pHBoE7rqiSm-FmISgiLO1Jj3Pqq-abjN4SnAbU7PZWcV3fUoO1eYLGORmAcw', 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3d3dy5teW9pZGNwcm92aWRlci5jb20iLCJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiZXhwIjo5NDY2ODQ4MDB9.yDGMBeee4hj8yDtEvVtIsS4tnkPjDMQADTkNh74wtb3oYPgQNqMRWKngXiwjvW2FmnsCUue2cFzLgTbpqlDq0QKcBP0i_UwBiXk9m2wLo0WRFtgw2zNHYSsowu26sFoEjKLgpPZzKrPlU4pnxqa8u3yqg8vIcSTlpX8t3uDqNqhUKP6x-w6wb25h67XDmnORiMwhaOZE_Gs9-H6uWnKdguTIlU1Tj4CjUEnZgZN7dJORiDnO_vHiAyL5yvRjhp5YK0Pq-TtCj5kWoJsjQiKc4laIcgofAKoq_b62Psns8MhxzAxwM7i0rbQZXXYB0VKMUho88uHlpbSWCZxu415lWw', 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiaXNzIjoiaHR0cHM6Ly93d3cubXlvaWRjcHJvdmlkZXIuY29tIiwiZXhwIjo5NDY2ODQ4MDB9.BHSCjFeXS6B7KFJi1LpQMEd3ib4Bp9FY3WcB-v7dtP3Ay0SxQZz_gxIbi-tYiNCBQIlfKcfq6vELOjE1WJ5zxPDQM8uV0Pjl41hqYBu3qFv4649a-o2Cd-MaSPUSUogPxzTh2Bk4IdM3sG1Zbd_At4DR_DQwWJDuChA8duA5yG2XPkZr0YF1ZJ326O_jEowvCJiZpzOpH9QsLVPbiX49jtWTwqQGhvpKEj3ztTLFo8VHO-p8bhOGEph2F73F6-GB0XqiWk2Dm1yKAunJCMsM4qXooWfaX6gj-WFhPI9kEXNFfGmPal5i1gb17YoeinbdV2kjN42oxast2Iaa3CMldw', 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJvcGVuaWQtY2xpZW50LWlkIiwiZXhwIjo5NDY2ODQ4MDAsImlzcyI6Imh0dHBzOi8vd3d3Lm15b2lkY3Byb3ZpZGVyLmNvbSJ9.s6sElpfKL8WHWfbD_Kbwiy_ip4O082V8ElZqwugvDpS-7yQ3FTvQ3WXqtVAJc-fBZe4ZsBnrXUWwZV0Nhoe6iWKjEjTPjonQWbWL_WUJmIC2HVz18AOISnqReV2rcuLSHQ2ckhsyktlE9K1Rfj-Hi6f3HzzzePEgTsL2ZdBH6GrcmJVDFKqLLrkvOHShoPp7rcwuFBr0J_S1oqYac5O0B-u0OVnxBXTwij0ThrTGMgVCp2rn6Hk0NvtF6CE49Eu4XP8Ue-APT8l5_SjqX9GcrjkJp8Gif_oyBheL-zRg_v-cU60X6qY9wVolO8WodVPSnlE02XyYhLVxvfK-w5129A', ), ) def test_decode_hmac_with_no_expiry(self): no_exp_token = jwt.encode(self.payload, SECRET, algorithm='HS256') self.hmac_token_backend.decode(no_exp_token) def test_decode_hmac_with_no_expiry_no_verify(self): no_exp_token = jwt.encode(self.payload, SECRET, algorithm='HS256') self.assertEqual( self.hmac_token_backend.decode(no_exp_token, verify=False), self.payload, ) def test_decode_hmac_with_expiry(self): self.payload['exp'] = aware_utcnow() - timedelta(seconds=1) expired_token = jwt.encode(self.payload, SECRET, algorithm='HS256') with self.assertRaises(TokenBackendError): self.hmac_token_backend.decode(expired_token) def test_decode_hmac_with_invalid_sig(self): self.payload['exp'] = aware_utcnow() + timedelta(days=1) token_1 = jwt.encode(self.payload, SECRET, algorithm='HS256') self.payload['foo'] = 'baz' token_2 = jwt.encode(self.payload, SECRET, algorithm='HS256') token_2_payload = token_2.rsplit('.', 1)[0] token_1_sig = token_1.rsplit('.', 1)[-1] invalid_token = token_2_payload + '.' + token_1_sig with self.assertRaises(TokenBackendError): self.hmac_token_backend.decode(invalid_token) def test_decode_hmac_with_invalid_sig_no_verify(self): self.payload['exp'] = aware_utcnow() + timedelta(days=1) token_1 = jwt.encode(self.payload, SECRET, algorithm='HS256') self.payload['foo'] = 'baz' token_2 = jwt.encode(self.payload, SECRET, algorithm='HS256') # Payload copied self.payload["exp"] = datetime_to_epoch(self.payload["exp"]) token_2_payload = token_2.rsplit('.', 1)[0] token_1_sig = token_1.rsplit('.', 1)[-1] invalid_token = token_2_payload + '.' + token_1_sig self.assertEqual( self.hmac_token_backend.decode(invalid_token, verify=False), self.payload, ) def test_decode_hmac_success(self): self.payload['exp'] = aware_utcnow() + timedelta(days=1) self.payload['foo'] = 'baz' token = jwt.encode(self.payload, SECRET, algorithm='HS256') # Payload copied self.payload["exp"] = datetime_to_epoch(self.payload["exp"]) self.assertEqual(self.hmac_token_backend.decode(token), self.payload) def test_decode_rsa_with_no_expiry(self): no_exp_token = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256') self.rsa_token_backend.decode(no_exp_token) def test_decode_rsa_with_no_expiry_no_verify(self): no_exp_token = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256') self.assertEqual( self.hmac_token_backend.decode(no_exp_token, verify=False), self.payload, ) def test_decode_rsa_with_expiry(self): self.payload['exp'] = aware_utcnow() - timedelta(seconds=1) expired_token = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256') with self.assertRaises(TokenBackendError): self.rsa_token_backend.decode(expired_token) def test_decode_rsa_with_invalid_sig(self): self.payload['exp'] = aware_utcnow() + timedelta(days=1) token_1 = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256') self.payload['foo'] = 'baz' token_2 = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256') token_2_payload = token_2.rsplit('.', 1)[0] token_1_sig = token_1.rsplit('.', 1)[-1] invalid_token = token_2_payload + '.' + token_1_sig with self.assertRaises(TokenBackendError): self.rsa_token_backend.decode(invalid_token) def test_decode_rsa_with_invalid_sig_no_verify(self): self.payload['exp'] = aware_utcnow() + timedelta(days=1) token_1 = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256') self.payload['foo'] = 'baz' token_2 = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256') token_2_payload = token_2.rsplit('.', 1)[0] token_1_sig = token_1.rsplit('.', 1)[-1] invalid_token = token_2_payload + '.' + token_1_sig # Payload copied self.payload["exp"] = datetime_to_epoch(self.payload["exp"]) self.assertEqual( self.hmac_token_backend.decode(invalid_token, verify=False), self.payload, ) def test_decode_rsa_success(self): self.payload['exp'] = aware_utcnow() + timedelta(days=1) self.payload['foo'] = 'baz' token = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256') # Payload copied self.payload["exp"] = datetime_to_epoch(self.payload["exp"]) self.assertEqual(self.rsa_token_backend.decode(token), self.payload) def test_decode_aud_iss_success(self): self.payload['exp'] = aware_utcnow() + timedelta(days=1) self.payload['foo'] = 'baz' self.payload['aud'] = AUDIENCE self.payload['iss'] = ISSUER token = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256') # Payload copied self.payload["exp"] = datetime_to_epoch(self.payload["exp"]) self.assertEqual(self.aud_iss_token_backend.decode(token), self.payload) def test_decode_rsa_aud_iss_jwk_success(self): self.payload["exp"] = aware_utcnow() + timedelta(days=1) self.payload["foo"] = "baz" self.payload["aud"] = AUDIENCE self.payload["iss"] = ISSUER token = jwt.encode( self.payload, PRIVATE_KEY_2, algorithm="RS256", headers={"kid": "230498151c214b788dd97f22b85410a5"}, ) # Payload copied self.payload["exp"] = datetime_to_epoch(self.payload["exp"]) mock_jwk_module = mock.MagicMock() with patch("rest_framework_simplejwt.backends.PyJWKClient") as mock_jwk_module: mock_jwk_client = mock.MagicMock() mock_signing_key = mock.MagicMock() mock_jwk_module.return_value = mock_jwk_client mock_jwk_client.get_signing_key_from_jwt.return_value = mock_signing_key type(mock_signing_key).key = mock.PropertyMock(return_value=PUBLIC_KEY_2) # Note the PRIV,PUB care is intentially the original pairing jwk_token_backend = TokenBackend( "RS256", PRIVATE_KEY, PUBLIC_KEY, AUDIENCE, ISSUER, JWK_URL ) self.assertEqual(jwk_token_backend.decode(token), self.payload) def test_decode_when_algorithm_not_available(self): token = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256') pyjwt_without_rsa = PyJWS() pyjwt_without_rsa.unregister_algorithm('RS256') def _decode(jwt, key, algorithms, options, audience, issuer, leeway): return pyjwt_without_rsa.decode(jwt, key, algorithms, options) with patch.object(jwt, 'decode', new=_decode): with self.assertRaisesRegex(TokenBackendError, 'Invalid algorithm specified'): self.rsa_token_backend.decode(token) def test_decode_when_token_algorithm_does_not_match(self): token = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256') with self.assertRaisesRegex(TokenBackendError, 'Invalid algorithm specified'): self.hmac_token_backend.decode(token) def test_decode_leeway_hmac_fail(self): self.payload["exp"] = datetime_to_epoch(aware_utcnow() - timedelta(seconds=LEEWAY * 2)) expired_token = jwt.encode(self.payload, SECRET, algorithm='HS256') with self.assertRaises(TokenBackendError): self.hmac_leeway_token_backend.decode(expired_token) def test_decode_leeway_hmac_success(self): self.payload["exp"] = datetime_to_epoch(aware_utcnow() - timedelta(seconds=LEEWAY / 2)) expired_token = jwt.encode(self.payload, SECRET, algorithm='HS256') self.assertEqual( self.hmac_leeway_token_backend.decode(expired_token), self.payload, ) djangorestframework-simplejwt-5.0.0/tests/test_integration.py000066400000000000000000000075051413160305600247120ustar00rootroot00000000000000from datetime import timedelta from django.contrib.auth import get_user_model from rest_framework_simplejwt.compat import reverse from rest_framework_simplejwt.settings import api_settings from rest_framework_simplejwt.tokens import AccessToken from .utils import APIViewTestCase, override_api_settings User = get_user_model() class TestTestView(APIViewTestCase): view_name = 'test_view' def setUp(self): self.username = 'test_user' self.password = 'test_password' self.user = User.objects.create_user( username=self.username, password=self.password, ) def test_no_authorization(self): res = self.view_get() self.assertEqual(res.status_code, 401) self.assertIn('credentials were not provided', res.data['detail']) def test_wrong_auth_type(self): res = self.client.post( reverse('token_obtain_sliding'), data={ User.USERNAME_FIELD: self.username, 'password': self.password, }, ) token = res.data['token'] self.authenticate_with_token('Wrong', token) res = self.view_get() self.assertEqual(res.status_code, 401) self.assertIn('credentials were not provided', res.data['detail']) def test_expired_token(self): old_lifetime = AccessToken.lifetime AccessToken.lifetime = timedelta(seconds=0) try: res = self.client.post( reverse('token_obtain_pair'), data={ User.USERNAME_FIELD: self.username, 'password': self.password, }, ) finally: AccessToken.lifetime = old_lifetime access = res.data['access'] self.authenticate_with_token(api_settings.AUTH_HEADER_TYPES[0], access) with override_api_settings(AUTH_TOKEN_CLASSES=('rest_framework_simplejwt.tokens.AccessToken',)): res = self.view_get() self.assertEqual(res.status_code, 401) self.assertEqual('token_not_valid', res.data['code']) def test_user_can_get_sliding_token_and_use_it(self): res = self.client.post( reverse('token_obtain_sliding'), data={ User.USERNAME_FIELD: self.username, 'password': self.password, }, ) token = res.data['token'] self.authenticate_with_token(api_settings.AUTH_HEADER_TYPES[0], token) with override_api_settings(AUTH_TOKEN_CLASSES=('rest_framework_simplejwt.tokens.SlidingToken',)): res = self.view_get() self.assertEqual(res.status_code, 200) self.assertEqual(res.data['foo'], 'bar') def test_user_can_get_access_and_refresh_tokens_and_use_them(self): res = self.client.post( reverse('token_obtain_pair'), data={ User.USERNAME_FIELD: self.username, 'password': self.password, }, ) access = res.data['access'] refresh = res.data['refresh'] self.authenticate_with_token(api_settings.AUTH_HEADER_TYPES[0], access) with override_api_settings(AUTH_TOKEN_CLASSES=('rest_framework_simplejwt.tokens.AccessToken',)): res = self.view_get() self.assertEqual(res.status_code, 200) self.assertEqual(res.data['foo'], 'bar') res = self.client.post( reverse('token_refresh'), data={'refresh': refresh}, ) access = res.data['access'] self.authenticate_with_token(api_settings.AUTH_HEADER_TYPES[0], access) with override_api_settings(AUTH_TOKEN_CLASSES=('rest_framework_simplejwt.tokens.AccessToken',)): res = self.view_get() self.assertEqual(res.status_code, 200) self.assertEqual(res.data['foo'], 'bar') djangorestframework-simplejwt-5.0.0/tests/test_models.py000066400000000000000000000061011413160305600236410ustar00rootroot00000000000000from django.test import TestCase from rest_framework_simplejwt.models import TokenUser from rest_framework_simplejwt.settings import api_settings AuthToken = api_settings.AUTH_TOKEN_CLASSES[0] class TestTokenUser(TestCase): def setUp(self): self.token = AuthToken() self.token[api_settings.USER_ID_CLAIM] = 42 self.token['username'] = 'deep-thought' self.token['some_other_stuff'] = 'arstarst' self.user = TokenUser(self.token) def test_username(self): self.assertEqual(self.user.username, 'deep-thought') def test_is_active(self): self.assertTrue(self.user.is_active) def test_str(self): self.assertEqual(str(self.user), 'TokenUser 42') def test_id(self): self.assertEqual(self.user.id, 42) def test_pk(self): self.assertEqual(self.user.pk, 42) def test_is_staff(self): payload = {api_settings.USER_ID_CLAIM: 42} user = TokenUser(payload) self.assertFalse(user.is_staff) payload['is_staff'] = True user = TokenUser(payload) self.assertTrue(user.is_staff) def test_is_superuser(self): payload = {api_settings.USER_ID_CLAIM: 42} user = TokenUser(payload) self.assertFalse(user.is_superuser) payload['is_superuser'] = True user = TokenUser(payload) self.assertTrue(user.is_superuser) def test_eq(self): user1 = TokenUser({api_settings.USER_ID_CLAIM: 1}) user2 = TokenUser({api_settings.USER_ID_CLAIM: 2}) user3 = TokenUser({api_settings.USER_ID_CLAIM: 1}) self.assertNotEqual(user1, user2) self.assertEqual(user1, user3) def test_hash(self): self.assertEqual(hash(self.user), hash(self.user.id)) def test_not_implemented(self): with self.assertRaises(NotImplementedError): self.user.save() with self.assertRaises(NotImplementedError): self.user.delete() with self.assertRaises(NotImplementedError): self.user.set_password('arst') with self.assertRaises(NotImplementedError): self.user.check_password('arst') def test_groups(self): self.assertFalse(self.user.groups.exists()) def test_user_permissions(self): self.assertFalse(self.user.user_permissions.exists()) def test_get_group_permissions(self): self.assertEqual(len(self.user.get_group_permissions()), 0) def test_get_all_permissions(self): self.assertEqual(len(self.user.get_all_permissions()), 0) def test_has_perm(self): self.assertFalse(self.user.has_perm('test_perm')) def test_has_perms(self): self.assertFalse(self.user.has_perms(['test_perm'])) def test_has_module_perms(self): self.assertFalse(self.user.has_module_perms('test_module')) def test_is_anonymous(self): self.assertFalse(self.user.is_anonymous) def test_is_authenticated(self): self.assertTrue(self.user.is_authenticated) def test_get_username(self): self.assertEqual(self.user.get_username(), 'deep-thought') djangorestframework-simplejwt-5.0.0/tests/test_serializers.py000066400000000000000000000362671413160305600247320ustar00rootroot00000000000000from datetime import timedelta from unittest.mock import MagicMock, patch from django.contrib.auth import get_user_model from django.test import TestCase from rest_framework import exceptions as drf_exceptions from rest_framework_simplejwt.exceptions import TokenError from rest_framework_simplejwt.serializers import ( TokenBlacklistSerializer, TokenObtainPairSerializer, TokenObtainSerializer, TokenObtainSlidingSerializer, TokenRefreshSerializer, TokenRefreshSlidingSerializer, TokenVerifySerializer, ) from rest_framework_simplejwt.settings import api_settings from rest_framework_simplejwt.token_blacklist.models import ( BlacklistedToken, OutstandingToken, ) from rest_framework_simplejwt.tokens import ( AccessToken, RefreshToken, SlidingToken, ) from rest_framework_simplejwt.utils import ( aware_utcnow, datetime_from_epoch, datetime_to_epoch, ) from .utils import override_api_settings User = get_user_model() class TestTokenObtainSerializer(TestCase): def setUp(self): self.username = 'test_user' self.password = 'test_password' self.user = User.objects.create_user( username=self.username, password=self.password, ) def test_it_should_not_validate_if_any_fields_missing(self): s = TokenObtainSerializer(data={}) self.assertFalse(s.is_valid()) self.assertIn(s.username_field, s.errors) self.assertIn('password', s.errors) s = TokenObtainSerializer(data={ TokenObtainSerializer.username_field: 'oieanrst', }) self.assertFalse(s.is_valid()) self.assertIn('password', s.errors) s = TokenObtainSerializer(data={ 'password': 'oieanrst', }) self.assertFalse(s.is_valid()) self.assertIn(s.username_field, s.errors) def test_it_should_not_validate_if_user_not_found(self): s = TokenObtainSerializer(context=MagicMock(), data={ TokenObtainSerializer.username_field: 'missing', 'password': 'pass', }) with self.assertRaises(drf_exceptions.AuthenticationFailed): s.is_valid() def test_it_should_raise_if_user_not_active(self): self.user.is_active = False self.user.save() s = TokenObtainSerializer(context=MagicMock(), data={ TokenObtainSerializer.username_field: self.username, 'password': self.password, }) with self.assertRaises(drf_exceptions.AuthenticationFailed): s.is_valid() class TestTokenObtainSlidingSerializer(TestCase): def setUp(self): self.username = 'test_user' self.password = 'test_password' self.user = User.objects.create_user( username=self.username, password=self.password, ) def test_it_should_produce_a_json_web_token_when_valid(self): s = TokenObtainSlidingSerializer(context=MagicMock(), data={ TokenObtainSlidingSerializer.username_field: self.username, 'password': self.password, }) self.assertTrue(s.is_valid()) self.assertIn('token', s.validated_data) # Expecting token type claim to be correct for sliding token. If this # is the case, instantiating a `SlidingToken` instance with encoded # token should not raise an exception. SlidingToken(s.validated_data['token']) class TestTokenObtainPairSerializer(TestCase): def setUp(self): self.username = 'test_user' self.password = 'test_password' self.user = User.objects.create_user( username=self.username, password=self.password, ) def test_it_should_produce_a_json_web_token_when_valid(self): s = TokenObtainPairSerializer(context=MagicMock(), data={ TokenObtainPairSerializer.username_field: self.username, 'password': self.password, }) self.assertTrue(s.is_valid()) self.assertIn('access', s.validated_data) self.assertIn('refresh', s.validated_data) # Expecting token type claim to be correct for both tokens. If this is # the case, instantiating appropriate token subclass instances with # encoded tokens should not raise an exception. AccessToken(s.validated_data['access']) RefreshToken(s.validated_data['refresh']) class TestTokenRefreshSlidingSerializer(TestCase): def test_it_should_not_validate_if_token_invalid(self): token = SlidingToken() del token['exp'] s = TokenRefreshSlidingSerializer(data={'token': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn("has no 'exp' claim", e.exception.args[0]) token.set_exp(lifetime=-timedelta(days=1)) s = TokenRefreshSlidingSerializer(data={'token': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn('invalid or expired', e.exception.args[0]) def test_it_should_raise_token_error_if_token_has_no_refresh_exp_claim(self): token = SlidingToken() del token[api_settings.SLIDING_TOKEN_REFRESH_EXP_CLAIM] s = TokenRefreshSlidingSerializer(data={'token': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn("has no '{}' claim".format(api_settings.SLIDING_TOKEN_REFRESH_EXP_CLAIM), e.exception.args[0]) def test_it_should_raise_token_error_if_token_has_refresh_period_expired(self): token = SlidingToken() token.set_exp(api_settings.SLIDING_TOKEN_REFRESH_EXP_CLAIM, lifetime=-timedelta(days=1)) s = TokenRefreshSlidingSerializer(data={'token': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn("'{}' claim has expired".format(api_settings.SLIDING_TOKEN_REFRESH_EXP_CLAIM), e.exception.args[0]) def test_it_should_raise_token_error_if_token_has_wrong_type(self): token = SlidingToken() token[api_settings.TOKEN_TYPE_CLAIM] = 'wrong_type' s = TokenRefreshSlidingSerializer(data={'token': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn("wrong type", e.exception.args[0]) def test_it_should_update_token_exp_claim_if_everything_ok(self): old_token = SlidingToken() lifetime = api_settings.SLIDING_TOKEN_LIFETIME - timedelta(seconds=1) old_exp = old_token.current_time + lifetime old_token.set_exp(lifetime=lifetime) # Serializer validates s = TokenRefreshSlidingSerializer(data={'token': str(old_token)}) self.assertTrue(s.is_valid()) # Expiration claim has moved into future new_token = SlidingToken(s.validated_data['token']) new_exp = datetime_from_epoch(new_token['exp']) self.assertTrue(old_exp < new_exp) class TestTokenRefreshSerializer(TestCase): def test_it_should_raise_token_error_if_token_invalid(self): token = RefreshToken() del token['exp'] s = TokenRefreshSerializer(data={'refresh': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn("has no 'exp' claim", e.exception.args[0]) token.set_exp(lifetime=-timedelta(days=1)) s = TokenRefreshSerializer(data={'refresh': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn('invalid or expired', e.exception.args[0]) def test_it_should_raise_token_error_if_token_has_wrong_type(self): token = RefreshToken() token[api_settings.TOKEN_TYPE_CLAIM] = 'wrong_type' s = TokenRefreshSerializer(data={'refresh': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn("wrong type", e.exception.args[0]) def test_it_should_return_access_token_if_everything_ok(self): refresh = RefreshToken() refresh['test_claim'] = 'arst' # Serializer validates s = TokenRefreshSerializer(data={'refresh': str(refresh)}) now = aware_utcnow() - api_settings.ACCESS_TOKEN_LIFETIME / 2 with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = now self.assertTrue(s.is_valid()) access = AccessToken(s.validated_data['access']) self.assertEqual(refresh['test_claim'], access['test_claim']) self.assertEqual(access['exp'], datetime_to_epoch(now + api_settings.ACCESS_TOKEN_LIFETIME)) def test_it_should_return_refresh_token_if_tokens_should_be_rotated(self): refresh = RefreshToken() refresh['test_claim'] = 'arst' old_jti = refresh['jti'] old_exp = refresh['exp'] # Serializer validates ser = TokenRefreshSerializer(data={'refresh': str(refresh)}) now = aware_utcnow() - api_settings.ACCESS_TOKEN_LIFETIME / 2 with override_api_settings(ROTATE_REFRESH_TOKENS=True, BLACKLIST_AFTER_ROTATION=False): with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = now self.assertTrue(ser.is_valid()) access = AccessToken(ser.validated_data['access']) new_refresh = RefreshToken(ser.validated_data['refresh']) self.assertEqual(refresh['test_claim'], access['test_claim']) self.assertEqual(refresh['test_claim'], new_refresh['test_claim']) self.assertNotEqual(old_jti, new_refresh['jti']) self.assertNotEqual(old_exp, new_refresh['exp']) self.assertEqual(access['exp'], datetime_to_epoch(now + api_settings.ACCESS_TOKEN_LIFETIME)) self.assertEqual(new_refresh['exp'], datetime_to_epoch(now + api_settings.REFRESH_TOKEN_LIFETIME)) def test_it_should_blacklist_refresh_token_if_tokens_should_be_rotated_and_blacklisted(self): self.assertEqual(OutstandingToken.objects.count(), 0) self.assertEqual(BlacklistedToken.objects.count(), 0) refresh = RefreshToken() refresh['test_claim'] = 'arst' old_jti = refresh['jti'] old_exp = refresh['exp'] # Serializer validates ser = TokenRefreshSerializer(data={'refresh': str(refresh)}) now = aware_utcnow() - api_settings.ACCESS_TOKEN_LIFETIME / 2 with override_api_settings(ROTATE_REFRESH_TOKENS=True, BLACKLIST_AFTER_ROTATION=True): with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = now self.assertTrue(ser.is_valid()) access = AccessToken(ser.validated_data['access']) new_refresh = RefreshToken(ser.validated_data['refresh']) self.assertEqual(refresh['test_claim'], access['test_claim']) self.assertEqual(refresh['test_claim'], new_refresh['test_claim']) self.assertNotEqual(old_jti, new_refresh['jti']) self.assertNotEqual(old_exp, new_refresh['exp']) self.assertEqual(access['exp'], datetime_to_epoch(now + api_settings.ACCESS_TOKEN_LIFETIME)) self.assertEqual(new_refresh['exp'], datetime_to_epoch(now + api_settings.REFRESH_TOKEN_LIFETIME)) self.assertEqual(OutstandingToken.objects.count(), 1) self.assertEqual(BlacklistedToken.objects.count(), 1) # Assert old refresh token is blacklisted self.assertEqual(BlacklistedToken.objects.first().token.jti, old_jti) class TestTokenVerifySerializer(TestCase): def test_it_should_raise_token_error_if_token_invalid(self): token = RefreshToken() del token['exp'] s = TokenVerifySerializer(data={'token': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn("has no 'exp' claim", e.exception.args[0]) token.set_exp(lifetime=-timedelta(days=1)) s = TokenVerifySerializer(data={'token': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn('invalid or expired', e.exception.args[0]) def test_it_should_not_raise_token_error_if_token_has_wrong_type(self): token = RefreshToken() token[api_settings.TOKEN_TYPE_CLAIM] = 'wrong_type' s = TokenVerifySerializer(data={'token': str(token)}) self.assertTrue(s.is_valid()) def test_it_should_return_given_token_if_everything_ok(self): refresh = RefreshToken() refresh['test_claim'] = 'arst' # Serializer validates s = TokenVerifySerializer(data={'token': str(refresh)}) now = aware_utcnow() - api_settings.ACCESS_TOKEN_LIFETIME / 2 with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = now self.assertTrue(s.is_valid()) self.assertEqual(len(s.validated_data), 0) class TestTokenBlacklistSerializer(TestCase): def test_it_should_raise_token_error_if_token_invalid(self): token = RefreshToken() del token['exp'] s = TokenBlacklistSerializer(data={'refresh': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn("has no 'exp' claim", e.exception.args[0]) token.set_exp(lifetime=-timedelta(days=1)) s = TokenBlacklistSerializer(data={'refresh': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn('invalid or expired', e.exception.args[0]) def test_it_should_raise_token_error_if_token_has_wrong_type(self): token = RefreshToken() token[api_settings.TOKEN_TYPE_CLAIM] = 'wrong_type' s = TokenBlacklistSerializer(data={'refresh': str(token)}) with self.assertRaises(TokenError) as e: s.is_valid() self.assertIn("wrong type", e.exception.args[0]) def test_it_should_return_nothing_if_everything_ok(self): refresh = RefreshToken() refresh['test_claim'] = 'arst' # Serializer validates s = TokenBlacklistSerializer(data={'refresh': str(refresh)}) now = aware_utcnow() - api_settings.ACCESS_TOKEN_LIFETIME / 2 with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = now self.assertTrue(s.is_valid()) self.assertDictEqual(s.validated_data, {}) def test_it_should_blacklist_refresh_token_if_everything_ok(self): self.assertEqual(OutstandingToken.objects.count(), 0) self.assertEqual(BlacklistedToken.objects.count(), 0) refresh = RefreshToken() refresh['test_claim'] = 'arst' old_jti = refresh['jti'] # Serializer validates ser = TokenBlacklistSerializer(data={'refresh': str(refresh)}) now = aware_utcnow() - api_settings.ACCESS_TOKEN_LIFETIME / 2 with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = now self.assertTrue(ser.is_valid()) self.assertEqual(OutstandingToken.objects.count(), 1) self.assertEqual(BlacklistedToken.objects.count(), 1) # Assert old refresh token is blacklisted self.assertEqual(BlacklistedToken.objects.first().token.jti, old_jti) djangorestframework-simplejwt-5.0.0/tests/test_token_blacklist.py000066400000000000000000000214721413160305600255360ustar00rootroot00000000000000from unittest.mock import patch from django.contrib.auth.models import User from django.core.management import call_command from django.db.models import BigAutoField from django.test import TestCase from rest_framework_simplejwt.exceptions import TokenError from rest_framework_simplejwt.serializers import TokenVerifySerializer from rest_framework_simplejwt.settings import api_settings from rest_framework_simplejwt.token_blacklist.models import ( BlacklistedToken, OutstandingToken, ) from rest_framework_simplejwt.tokens import ( AccessToken, RefreshToken, SlidingToken, ) from rest_framework_simplejwt.utils import aware_utcnow, datetime_from_epoch from .utils import MigrationTestCase, override_api_settings class TestTokenBlacklist(TestCase): def setUp(self): self.user = User.objects.create( username='test_user', password='test_password', ) def test_sliding_tokens_are_added_to_outstanding_list(self): token = SlidingToken.for_user(self.user) qs = OutstandingToken.objects.all() outstanding_token = qs.first() self.assertEqual(qs.count(), 1) self.assertEqual(outstanding_token.user, self.user) self.assertEqual(outstanding_token.jti, token['jti']) self.assertEqual(outstanding_token.token, str(token)) self.assertEqual(outstanding_token.created_at, token.current_time) self.assertEqual(outstanding_token.expires_at, datetime_from_epoch(token['exp'])) def test_refresh_tokens_are_added_to_outstanding_list(self): token = RefreshToken.for_user(self.user) qs = OutstandingToken.objects.all() outstanding_token = qs.first() self.assertEqual(qs.count(), 1) self.assertEqual(outstanding_token.user, self.user) self.assertEqual(outstanding_token.jti, token['jti']) self.assertEqual(outstanding_token.token, str(token)) self.assertEqual(outstanding_token.created_at, token.current_time) self.assertEqual(outstanding_token.expires_at, datetime_from_epoch(token['exp'])) def test_access_tokens_are_not_added_to_outstanding_list(self): AccessToken.for_user(self.user) qs = OutstandingToken.objects.all() self.assertFalse(qs.exists()) def test_token_will_not_validate_if_blacklisted(self): token = RefreshToken.for_user(self.user) outstanding_token = OutstandingToken.objects.first() # Should raise no exception RefreshToken(str(token)) # Add token to blacklist BlacklistedToken.objects.create(token=outstanding_token) with self.assertRaises(TokenError) as e: # Should raise exception RefreshToken(str(token)) self.assertIn('blacklisted', e.exception.args[0]) def test_tokens_can_be_manually_blacklisted(self): token = RefreshToken.for_user(self.user) # Should raise no exception RefreshToken(str(token)) self.assertEqual(OutstandingToken.objects.count(), 1) # Add token to blacklist blacklisted_token, created = token.blacklist() # Should not add token to outstanding list if already present self.assertEqual(OutstandingToken.objects.count(), 1) # Should return blacklist record and boolean to indicate creation self.assertEqual(blacklisted_token.token.jti, token['jti']) self.assertTrue(created) with self.assertRaises(TokenError) as e: # Should raise exception RefreshToken(str(token)) self.assertIn('blacklisted', e.exception.args[0]) # If blacklisted token already exists, indicate no creation through # boolean blacklisted_token, created = token.blacklist() self.assertEqual(blacklisted_token.token.jti, token['jti']) self.assertFalse(created) # Should add token to outstanding list if not already present new_token = RefreshToken() blacklisted_token, created = new_token.blacklist() self.assertEqual(blacklisted_token.token.jti, new_token['jti']) self.assertTrue(created) self.assertEqual(OutstandingToken.objects.count(), 2) class TestTokenBlacklistFlushExpiredTokens(TestCase): def setUp(self): self.user = User.objects.create( username='test_user', password='test_password', ) def test_it_should_delete_any_expired_tokens(self): # Make some tokens that won't expire soon not_expired_1 = RefreshToken.for_user(self.user) not_expired_2 = RefreshToken.for_user(self.user) not_expired_3 = RefreshToken() # Blacklist fresh tokens not_expired_2.blacklist() not_expired_3.blacklist() # Make tokens with fake exp time that will expire soon fake_now = aware_utcnow() - api_settings.REFRESH_TOKEN_LIFETIME with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = fake_now expired_1 = RefreshToken.for_user(self.user) expired_2 = RefreshToken() # Blacklist expired tokens expired_1.blacklist() expired_2.blacklist() # Make another token that won't expire soon not_expired_4 = RefreshToken.for_user(self.user) # Should be certain number of outstanding tokens and blacklisted # tokens self.assertEqual(OutstandingToken.objects.count(), 6) self.assertEqual(BlacklistedToken.objects.count(), 4) call_command('flushexpiredtokens') # Expired outstanding *and* blacklisted tokens should be gone self.assertEqual(OutstandingToken.objects.count(), 4) self.assertEqual(BlacklistedToken.objects.count(), 2) self.assertEqual( [i.jti for i in OutstandingToken.objects.order_by('id')], [not_expired_1['jti'], not_expired_2['jti'], not_expired_3['jti'], not_expired_4['jti']], ) self.assertEqual( [i.token.jti for i in BlacklistedToken.objects.order_by('id')], [not_expired_2['jti'], not_expired_3['jti']], ) class TestPopulateJtiHexMigration(MigrationTestCase): migrate_from = ('token_blacklist', '0002_outstandingtoken_jti_hex') migrate_to = ('token_blacklist', '0003_auto_20171017_2007') def setUp(self): self.user = User.objects.create( username='test_user', password='test_password', ) super().setUp() def setUpBeforeMigration(self, apps): # Ensure some tokens are present in the outstanding list RefreshToken.for_user(self.user) RefreshToken.for_user(self.user) OutstandingToken = apps.get_model('token_blacklist', 'OutstandingToken') self.expected_hexes = [i.jti.hex for i in OutstandingToken.objects.all()] def test_jti_field_should_contain_uuid_hex_strings(self): OutstandingToken = self.apps.get_model('token_blacklist', 'OutstandingToken') actual_hexes = [i.jti_hex for i in OutstandingToken.objects.all()] self.assertEqual(actual_hexes, self.expected_hexes) class TokenVerifySerializerShouldHonourBlacklist(MigrationTestCase): migrate_from = ('token_blacklist', '0002_outstandingtoken_jti_hex') migrate_to = ('token_blacklist', '0003_auto_20171017_2007') def setUp(self): self.user = User.objects.create( username='test_user', password='test_password', ) super().setUp() def test_token_verify_serializer_should_honour_blacklist_if_blacklisting_enabled(self): with override_api_settings(BLACKLIST_AFTER_ROTATION=True): refresh_token = RefreshToken.for_user(self.user) refresh_token.blacklist() serializer = TokenVerifySerializer(data={"token": str(refresh_token)}) self.assertFalse(serializer.is_valid()) def test_token_verify_serializer_should_not_honour_blacklist_if_blacklisting_not_enabled(self): with override_api_settings(BLACKLIST_AFTER_ROTATION=False): refresh_token = RefreshToken.for_user(self.user) refresh_token.blacklist() serializer = TokenVerifySerializer(data={"token": str(refresh_token)}) self.assertTrue(serializer.is_valid()) class TestBigAutoFieldIDMigration(MigrationTestCase): migrate_from = ('token_blacklist', '0007_auto_20171017_2214') migrate_to = ('token_blacklist', '0008_migrate_to_bigautofield') def test_outstandingtoken_id_field_is_biagauto_field(self): OutstandingToken = self.apps.get_model('token_blacklist', 'OutstandingToken') assert isinstance(OutstandingToken._meta.get_field('id'), BigAutoField) def test_blacklistedtoken_id_field_is_biagauto_field(self): BlacklistedToken = self.apps.get_model('token_blacklist', 'BlacklistedToken') assert isinstance(BlacklistedToken._meta.get_field('id'), BigAutoField) djangorestframework-simplejwt-5.0.0/tests/test_tokens.py000066400000000000000000000314571413160305600236750ustar00rootroot00000000000000from datetime import datetime, timedelta from unittest.mock import patch from django.contrib.auth import get_user_model from django.test import TestCase from jose import jwt from rest_framework_simplejwt.exceptions import TokenError from rest_framework_simplejwt.settings import api_settings from rest_framework_simplejwt.state import token_backend from rest_framework_simplejwt.tokens import ( AccessToken, RefreshToken, SlidingToken, Token, UntypedToken, ) from rest_framework_simplejwt.utils import ( aware_utcnow, datetime_to_epoch, make_utc, ) from .utils import override_api_settings User = get_user_model() class MyToken(Token): token_type = 'test' lifetime = timedelta(days=1) class TestToken(TestCase): def setUp(self): self.token = MyToken() def test_init_no_token_type_or_lifetime(self): class MyTestToken(Token): pass with self.assertRaises(TokenError): MyTestToken() MyTestToken.token_type = 'test' with self.assertRaises(TokenError): MyTestToken() del MyTestToken.token_type MyTestToken.lifetime = timedelta(days=1) with self.assertRaises(TokenError): MyTestToken() MyTestToken.token_type = 'test' MyTestToken() def test_init_no_token_given(self): now = make_utc(datetime(year=2000, month=1, day=1)) with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = now t = MyToken() self.assertEqual(t.current_time, now) self.assertIsNone(t.token) self.assertEqual(len(t.payload), 4) self.assertEqual(t.payload['exp'], datetime_to_epoch(now + MyToken.lifetime)) self.assertEqual(t.payload['iat'], datetime_to_epoch(now)) self.assertIn('jti', t.payload) self.assertEqual(t.payload[api_settings.TOKEN_TYPE_CLAIM], MyToken.token_type) def test_init_token_given(self): # Test successful instantiation original_now = aware_utcnow() with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = original_now good_token = MyToken() good_token['some_value'] = 'arst' encoded_good_token = str(good_token) now = aware_utcnow() # Create new token from encoded token with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = now # Should raise no exception t = MyToken(encoded_good_token) # Should have expected properties self.assertEqual(t.current_time, now) self.assertEqual(t.token, encoded_good_token) self.assertEqual(len(t.payload), 5) self.assertEqual(t['some_value'], 'arst') self.assertEqual(t['exp'], datetime_to_epoch(original_now + MyToken.lifetime)) self.assertEqual(t['iat'], datetime_to_epoch(original_now)) self.assertEqual(t[api_settings.TOKEN_TYPE_CLAIM], MyToken.token_type) self.assertIn('jti', t.payload) def test_init_bad_sig_token_given(self): # Test backend rejects encoded token (expired or bad signature) payload = {'foo': 'bar'} payload['exp'] = aware_utcnow() + timedelta(days=1) token_1 = jwt.encode(payload, api_settings.SIGNING_KEY, algorithm='HS256') payload['foo'] = 'baz' token_2 = jwt.encode(payload, api_settings.SIGNING_KEY, algorithm='HS256') token_2_payload = token_2.rsplit('.', 1)[0] token_1_sig = token_1.rsplit('.', 1)[-1] invalid_token = token_2_payload + '.' + token_1_sig with self.assertRaises(TokenError): MyToken(invalid_token) def test_init_bad_sig_token_given_no_verify(self): # Test backend rejects encoded token (expired or bad signature) payload = {'foo': 'bar'} payload['exp'] = aware_utcnow() + timedelta(days=1) token_1 = jwt.encode(payload, api_settings.SIGNING_KEY, algorithm='HS256') payload['foo'] = 'baz' token_2 = jwt.encode(payload, api_settings.SIGNING_KEY, algorithm='HS256') token_2_payload = token_2.rsplit('.', 1)[0] token_1_sig = token_1.rsplit('.', 1)[-1] invalid_token = token_2_payload + '.' + token_1_sig t = MyToken(invalid_token, verify=False) self.assertEqual( t.payload, payload, ) def test_init_expired_token_given(self): t = MyToken() t.set_exp(lifetime=-timedelta(seconds=1)) with self.assertRaises(TokenError): MyToken(str(t)) def test_init_no_type_token_given(self): t = MyToken() del t[api_settings.TOKEN_TYPE_CLAIM] with self.assertRaises(TokenError): MyToken(str(t)) def test_init_wrong_type_token_given(self): t = MyToken() t[api_settings.TOKEN_TYPE_CLAIM] = 'wrong_type' with self.assertRaises(TokenError): MyToken(str(t)) def test_init_no_jti_token_given(self): t = MyToken() del t['jti'] with self.assertRaises(TokenError): MyToken(str(t)) def test_str(self): token = MyToken() token.set_exp( from_time=make_utc(datetime(year=2000, month=1, day=1)), lifetime=timedelta(seconds=0), ) # Delete all but one claim. We want our lives to be easy and for there # to only be a couple of possible encodings. We're only testing that a # payload is successfully encoded here, not that it has specific # content. del token[api_settings.TOKEN_TYPE_CLAIM] del token['jti'] del token['iat'] # Should encode the given token encoded_token = str(token) # Token could be one of two depending on header dict ordering self.assertIn( encoded_token, ( 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjk0NjY4NDgwMH0.VKoOnMgmETawjDZwxrQaHG0xHdo6xBodFy6FXJzTVxs', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk0NjY4NDgwMH0.iqxxOHV63sjeqNR1GDxX3LPvMymfVB76sOIDqTbjAgk', ), ) def test_repr(self): self.assertEqual(repr(self.token), repr(self.token.payload)) def test_getitem(self): self.assertEqual(self.token['exp'], self.token.payload['exp']) def test_setitem(self): self.token['test'] = 1234 self.assertEqual(self.token.payload['test'], 1234) def test_delitem(self): self.token['test'] = 1234 self.assertEqual(self.token.payload['test'], 1234) del self.token['test'] self.assertNotIn('test', self.token) def test_contains(self): self.assertIn('exp', self.token) def test_get(self): self.token['test'] = 1234 self.assertEqual(1234, self.token.get('test')) self.assertEqual(1234, self.token.get('test', 2345)) self.assertIsNone(self.token.get('does_not_exist')) self.assertEqual(1234, self.token.get('does_not_exist', 1234)) def test_set_jti(self): token = MyToken() old_jti = token['jti'] token.set_jti() self.assertIn('jti', token) self.assertNotEqual(old_jti, token['jti']) def test_set_exp(self): now = make_utc(datetime(year=2000, month=1, day=1)) token = MyToken() token.current_time = now # By default, should add 'exp' claim to token using `self.current_time` # and the TOKEN_LIFETIME setting token.set_exp() self.assertEqual(token['exp'], datetime_to_epoch(now + MyToken.lifetime)) # Should allow overriding of beginning time, lifetime, and claim name token.set_exp(claim='refresh_exp', from_time=now, lifetime=timedelta(days=1)) self.assertIn('refresh_exp', token) self.assertEqual(token['refresh_exp'], datetime_to_epoch(now + timedelta(days=1))) def test_set_iat(self): now = make_utc(datetime(year=2000, month=1, day=1)) token = MyToken() token.current_time = now # By default, should add 'iat' claim to token using `self.current_time` token.set_iat() self.assertEqual(token['iat'], datetime_to_epoch(now)) # Should allow overriding of time and claim name token.set_iat(claim='refresh_iat', at_time=now + timedelta(days=1)) self.assertIn('refresh_iat', token) self.assertEqual(token['refresh_iat'], datetime_to_epoch(now + timedelta(days=1))) def test_check_exp(self): token = MyToken() # Should raise an exception if no claim of given kind with self.assertRaises(TokenError): token.check_exp('non_existent_claim') current_time = token.current_time lifetime = timedelta(days=1) exp = token.current_time + lifetime token.set_exp(lifetime=lifetime) # By default, checks 'exp' claim against `self.current_time`. Should # raise an exception if claim has expired. token.current_time = exp with self.assertRaises(TokenError): token.check_exp() token.current_time = exp + timedelta(seconds=1) with self.assertRaises(TokenError): token.check_exp() # Otherwise, should raise no exception token.current_time = current_time token.check_exp() # Should allow specification of claim to be examined and timestamp to # compare against # Default claim with self.assertRaises(TokenError): token.check_exp(current_time=exp) token.set_exp('refresh_exp', lifetime=timedelta(days=1)) # Default timestamp token.check_exp('refresh_exp') # Given claim and timestamp with self.assertRaises(TokenError): token.check_exp('refresh_exp', current_time=current_time + timedelta(days=1)) with self.assertRaises(TokenError): token.check_exp('refresh_exp', current_time=current_time + timedelta(days=2)) def test_for_user(self): username = 'test_user' user = User.objects.create_user( username=username, password='test_password', ) token = MyToken.for_user(user) user_id = getattr(user, api_settings.USER_ID_FIELD) if not isinstance(user_id, int): user_id = str(user_id) self.assertEqual(token[api_settings.USER_ID_CLAIM], user_id) # Test with non-int user id with override_api_settings(USER_ID_FIELD='username'): token = MyToken.for_user(user) self.assertEqual(token[api_settings.USER_ID_CLAIM], username) def test_get_token_backend(self): token = MyToken() self.assertEqual(token.get_token_backend(), token_backend) class TestSlidingToken(TestCase): def test_init(self): # Should set sliding refresh claim and token type claim token = SlidingToken() self.assertEqual( token[api_settings.SLIDING_TOKEN_REFRESH_EXP_CLAIM], datetime_to_epoch(token.current_time + api_settings.SLIDING_TOKEN_REFRESH_LIFETIME), ) self.assertEqual(token[api_settings.TOKEN_TYPE_CLAIM], 'sliding') class TestAccessToken(TestCase): def test_init(self): # Should set token type claim token = AccessToken() self.assertEqual(token[api_settings.TOKEN_TYPE_CLAIM], 'access') class TestRefreshToken(TestCase): def test_init(self): # Should set token type claim token = RefreshToken() self.assertEqual(token[api_settings.TOKEN_TYPE_CLAIM], 'refresh') def test_access_token(self): # Should create an access token from a refresh token refresh = RefreshToken() refresh['test_claim'] = 'arst' access = refresh.access_token self.assertIsInstance(access, AccessToken) self.assertEqual(access[api_settings.TOKEN_TYPE_CLAIM], 'access') # Should keep all copyable claims from refresh token self.assertEqual(refresh['test_claim'], access['test_claim']) # Should not copy certain claims from refresh token for claim in RefreshToken.no_copy_claims: self.assertNotEqual(refresh[claim], access[claim]) class TestUntypedToken(TestCase): def test_it_should_accept_and_verify_any_type_of_token(self): access_token = AccessToken() refresh_token = RefreshToken() sliding_token = SlidingToken() for t in (access_token, refresh_token, sliding_token): untyped_token = UntypedToken(str(t)) self.assertEqual( t.payload, untyped_token.payload, ) def test_it_should_expire_immediately_if_made_from_scratch(self): t = UntypedToken() self.assertEqual(t[api_settings.TOKEN_TYPE_CLAIM], 'untyped') with self.assertRaises(TokenError): t.check_exp() djangorestframework-simplejwt-5.0.0/tests/test_utils.py000066400000000000000000000055551413160305600235320ustar00rootroot00000000000000from datetime import datetime, timedelta from unittest.mock import patch from django.test import TestCase from django.utils import timezone from rest_framework_simplejwt.utils import ( aware_utcnow, datetime_from_epoch, datetime_to_epoch, format_lazy, make_utc, ) class TestMakeUtc(TestCase): def test_it_should_return_the_correct_values(self): # It should make a naive datetime into an aware, utc datetime if django # is configured to use timezones and the datetime doesn't already have # a timezone # Naive datetime dt = datetime(year=1970, month=12, day=1) with self.settings(USE_TZ=False): dt = make_utc(dt) self.assertTrue(timezone.is_naive(dt)) with self.settings(USE_TZ=True): dt = make_utc(dt) self.assertTrue(timezone.is_aware(dt)) self.assertEqual(dt.utcoffset(), timedelta(seconds=0)) class TestAwareUtcnow(TestCase): def test_it_should_return_the_correct_value(self): now = datetime.utcnow() with patch('rest_framework_simplejwt.utils.datetime') as fake_datetime: fake_datetime.utcnow.return_value = now # Should return aware utcnow if USE_TZ == True with self.settings(USE_TZ=True): self.assertEqual(timezone.make_aware(now, timezone=timezone.utc), aware_utcnow()) # Should return naive utcnow if USE_TZ == False with self.settings(USE_TZ=False): self.assertEqual(now, aware_utcnow()) class TestDatetimeToEpoch(TestCase): def test_it_should_return_the_correct_values(self): self.assertEqual(datetime_to_epoch(datetime(year=1970, month=1, day=1)), 0) self.assertEqual(datetime_to_epoch(datetime(year=1970, month=1, day=1, second=1)), 1) self.assertEqual(datetime_to_epoch(datetime(year=2000, month=1, day=1)), 946684800) class TestDatetimeFromEpoch(TestCase): def test_it_should_return_the_correct_values(self): with self.settings(USE_TZ=False): self.assertEqual(datetime_from_epoch(0), datetime(year=1970, month=1, day=1)) self.assertEqual(datetime_from_epoch(1), datetime(year=1970, month=1, day=1, second=1)) self.assertEqual(datetime_from_epoch(946684800), datetime(year=2000, month=1, day=1), 946684800) with self.settings(USE_TZ=True): self.assertEqual(datetime_from_epoch(0), make_utc(datetime(year=1970, month=1, day=1))) self.assertEqual(datetime_from_epoch(1), make_utc(datetime(year=1970, month=1, day=1, second=1))) self.assertEqual(datetime_from_epoch(946684800), make_utc(datetime(year=2000, month=1, day=1))) class TestFormatLazy(TestCase): def test_it_should_work(self): obj = format_lazy('{} {}', 'arst', 'zxcv') self.assertNotIsInstance(obj, str) self.assertEqual(str(obj), 'arst zxcv') djangorestframework-simplejwt-5.0.0/tests/test_views.py000066400000000000000000000320451413160305600235210ustar00rootroot00000000000000from datetime import timedelta from importlib import reload from unittest.mock import patch from django.contrib.auth import get_user_model from django.utils import timezone from rest_framework_simplejwt import serializers from rest_framework_simplejwt.settings import api_settings from rest_framework_simplejwt.tokens import ( AccessToken, RefreshToken, SlidingToken, ) from rest_framework_simplejwt.utils import ( aware_utcnow, datetime_from_epoch, datetime_to_epoch, ) from .utils import APIViewTestCase, override_api_settings User = get_user_model() class TestTokenObtainPairView(APIViewTestCase): view_name = 'token_obtain_pair' def setUp(self): self.username = 'test_user' self.password = 'test_password' self.user = User.objects.create_user( username=self.username, password=self.password, ) def test_fields_missing(self): res = self.view_post(data={}) self.assertEqual(res.status_code, 400) self.assertIn(User.USERNAME_FIELD, res.data) self.assertIn('password', res.data) res = self.view_post(data={User.USERNAME_FIELD: self.username}) self.assertEqual(res.status_code, 400) self.assertIn('password', res.data) res = self.view_post(data={'password': self.password}) self.assertEqual(res.status_code, 400) self.assertIn(User.USERNAME_FIELD, res.data) def test_credentials_wrong(self): res = self.view_post(data={ User.USERNAME_FIELD: self.username, 'password': 'test_user', }) self.assertEqual(res.status_code, 401) self.assertIn('detail', res.data) def test_user_inactive(self): self.user.is_active = False self.user.save() res = self.view_post(data={ User.USERNAME_FIELD: self.username, 'password': self.password, }) self.assertEqual(res.status_code, 401) self.assertIn('detail', res.data) def test_success(self): res = self.view_post(data={ User.USERNAME_FIELD: self.username, 'password': self.password, }) self.assertEqual(res.status_code, 200) self.assertIn('access', res.data) self.assertIn('refresh', res.data) def test_update_last_login(self): self.view_post(data={ User.USERNAME_FIELD: self.username, 'password': self.password, }) # verify last_login is not updated user = User.objects.get(username=self.username) self.assertEqual(user.last_login, None) # verify last_login is updated with override_api_settings(UPDATE_LAST_LOGIN=True): reload(serializers) self.view_post(data={ User.USERNAME_FIELD: self.username, 'password': self.password, }) user = User.objects.get(username=self.username) self.assertIsNotNone(user.last_login) self.assertGreaterEqual(timezone.now(), user.last_login) reload(serializers) class TestTokenRefreshView(APIViewTestCase): view_name = 'token_refresh' def setUp(self): self.username = 'test_user' self.password = 'test_password' self.user = User.objects.create_user( username=self.username, password=self.password, ) def test_fields_missing(self): res = self.view_post(data={}) self.assertEqual(res.status_code, 400) self.assertIn('refresh', res.data) def test_it_should_return_401_if_token_invalid(self): token = RefreshToken() del token['exp'] res = self.view_post(data={'refresh': str(token)}) self.assertEqual(res.status_code, 401) self.assertEqual(res.data['code'], 'token_not_valid') token.set_exp(lifetime=-timedelta(seconds=1)) res = self.view_post(data={'refresh': str(token)}) self.assertEqual(res.status_code, 401) self.assertEqual(res.data['code'], 'token_not_valid') def test_it_should_return_access_token_if_everything_ok(self): refresh = RefreshToken() refresh['test_claim'] = 'arst' # View returns 200 now = aware_utcnow() - api_settings.ACCESS_TOKEN_LIFETIME / 2 with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = now res = self.view_post(data={'refresh': str(refresh)}) self.assertEqual(res.status_code, 200) access = AccessToken(res.data['access']) self.assertEqual(refresh['test_claim'], access['test_claim']) self.assertEqual(access['exp'], datetime_to_epoch(now + api_settings.ACCESS_TOKEN_LIFETIME)) class TestTokenObtainSlidingView(APIViewTestCase): view_name = 'token_obtain_sliding' def setUp(self): self.username = 'test_user' self.password = 'test_password' self.user = User.objects.create_user( username=self.username, password=self.password, ) def test_fields_missing(self): res = self.view_post(data={}) self.assertEqual(res.status_code, 400) self.assertIn(User.USERNAME_FIELD, res.data) self.assertIn('password', res.data) res = self.view_post(data={User.USERNAME_FIELD: self.username}) self.assertEqual(res.status_code, 400) self.assertIn('password', res.data) res = self.view_post(data={'password': self.password}) self.assertEqual(res.status_code, 400) self.assertIn(User.USERNAME_FIELD, res.data) def test_credentials_wrong(self): res = self.view_post(data={ User.USERNAME_FIELD: self.username, 'password': 'test_user', }) self.assertEqual(res.status_code, 401) self.assertIn('detail', res.data) def test_user_inactive(self): self.user.is_active = False self.user.save() res = self.view_post(data={ User.USERNAME_FIELD: self.username, 'password': self.password, }) self.assertEqual(res.status_code, 401) self.assertIn('detail', res.data) def test_success(self): res = self.view_post(data={ User.USERNAME_FIELD: self.username, 'password': self.password, }) self.assertEqual(res.status_code, 200) self.assertIn('token', res.data) def test_update_last_login(self): self.view_post(data={ User.USERNAME_FIELD: self.username, 'password': self.password, }) # verify last_login is not updated user = User.objects.get(username=self.username) self.assertEqual(user.last_login, None) # verify last_login is updated with override_api_settings(UPDATE_LAST_LOGIN=True): reload(serializers) self.view_post(data={ User.USERNAME_FIELD: self.username, 'password': self.password, }) user = User.objects.get(username=self.username) self.assertIsNotNone(user.last_login) self.assertGreaterEqual(timezone.now(), user.last_login) reload(serializers) class TestTokenRefreshSlidingView(APIViewTestCase): view_name = 'token_refresh_sliding' def setUp(self): self.username = 'test_user' self.password = 'test_password' self.user = User.objects.create_user( username=self.username, password=self.password, ) def test_fields_missing(self): res = self.view_post(data={}) self.assertEqual(res.status_code, 400) self.assertIn('token', res.data) def test_it_should_return_401_if_token_invalid(self): token = SlidingToken() del token['exp'] res = self.view_post(data={'token': str(token)}) self.assertEqual(res.status_code, 401) self.assertEqual(res.data['code'], 'token_not_valid') token.set_exp(lifetime=-timedelta(seconds=1)) res = self.view_post(data={'token': str(token)}) self.assertEqual(res.status_code, 401) self.assertEqual(res.data['code'], 'token_not_valid') def test_it_should_return_401_if_token_has_no_refresh_exp_claim(self): token = SlidingToken() del token[api_settings.SLIDING_TOKEN_REFRESH_EXP_CLAIM] res = self.view_post(data={'token': str(token)}) self.assertEqual(res.status_code, 401) self.assertEqual(res.data['code'], 'token_not_valid') def test_it_should_return_401_if_token_has_refresh_period_expired(self): token = SlidingToken() token.set_exp(api_settings.SLIDING_TOKEN_REFRESH_EXP_CLAIM, lifetime=-timedelta(seconds=1)) res = self.view_post(data={'token': str(token)}) self.assertEqual(res.status_code, 401) self.assertEqual(res.data['code'], 'token_not_valid') def test_it_should_update_token_exp_claim_if_everything_ok(self): now = aware_utcnow() token = SlidingToken() exp = now + api_settings.SLIDING_TOKEN_LIFETIME - timedelta(seconds=1) token.set_exp(from_time=now, lifetime=api_settings.SLIDING_TOKEN_LIFETIME - timedelta(seconds=1)) # View returns 200 res = self.view_post(data={'token': str(token)}) self.assertEqual(res.status_code, 200) # Expiration claim has moved into future new_token = SlidingToken(res.data['token']) new_exp = datetime_from_epoch(new_token['exp']) self.assertTrue(exp < new_exp) class TestTokenVerifyView(APIViewTestCase): view_name = 'token_verify' def setUp(self): self.username = 'test_user' self.password = 'test_password' self.user = User.objects.create_user( username=self.username, password=self.password, ) def test_fields_missing(self): res = self.view_post(data={}) self.assertEqual(res.status_code, 400) self.assertIn('token', res.data) def test_it_should_return_401_if_token_invalid(self): token = SlidingToken() del token['exp'] res = self.view_post(data={'token': str(token)}) self.assertEqual(res.status_code, 401) self.assertEqual(res.data['code'], 'token_not_valid') token.set_exp(lifetime=-timedelta(seconds=1)) res = self.view_post(data={'token': str(token)}) self.assertEqual(res.status_code, 401) self.assertEqual(res.data['code'], 'token_not_valid') def test_it_should_return_200_if_everything_okay(self): token = RefreshToken() res = self.view_post(data={'token': str(token)}) self.assertEqual(res.status_code, 200) self.assertEqual(len(res.data), 0) def test_it_should_ignore_token_type(self): token = RefreshToken() token[api_settings.TOKEN_TYPE_CLAIM] = 'fake_type' res = self.view_post(data={'token': str(token)}) self.assertEqual(res.status_code, 200) self.assertEqual(len(res.data), 0) class TestTokenBlacklistView(APIViewTestCase): view_name = 'token_blacklist' def setUp(self): self.username = 'test_user' self.password = 'test_password' self.user = User.objects.create_user( username=self.username, password=self.password, ) def test_fields_missing(self): res = self.view_post(data={}) self.assertEqual(res.status_code, 400) self.assertIn('refresh', res.data) def test_it_should_return_401_if_token_invalid(self): token = RefreshToken() del token['exp'] res = self.view_post(data={'refresh': str(token)}) self.assertEqual(res.status_code, 401) self.assertEqual(res.data['code'], 'token_not_valid') token.set_exp(lifetime=-timedelta(seconds=1)) res = self.view_post(data={'refresh': str(token)}) self.assertEqual(res.status_code, 401) self.assertEqual(res.data['code'], 'token_not_valid') def test_it_should_return_if_everything_ok(self): refresh = RefreshToken() refresh['test_claim'] = 'arst' # View returns 200 now = aware_utcnow() - api_settings.ACCESS_TOKEN_LIFETIME / 2 with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = now res = self.view_post(data={'refresh': str(refresh)}) self.assertEqual(res.status_code, 200) self.assertDictEqual(res.data, {}) def test_it_should_return_401_if_token_is_blacklisted(self): refresh = RefreshToken() refresh['test_claim'] = 'arst' # View returns 200 now = aware_utcnow() - api_settings.ACCESS_TOKEN_LIFETIME / 2 with patch('rest_framework_simplejwt.tokens.aware_utcnow') as fake_aware_utcnow: fake_aware_utcnow.return_value = now res = self.view_post(data={'refresh': str(refresh)}) self.assertEqual(res.status_code, 200) self.view_name = 'token_refresh' res = self.view_post(data={'refresh': str(refresh)}) # make sure other tests are not affected del self.view_name self.assertEqual(res.status_code, 401) djangorestframework-simplejwt-5.0.0/tests/urls.py000066400000000000000000000013321413160305600223050ustar00rootroot00000000000000from django.urls import re_path from rest_framework_simplejwt import views as jwt_views from . import views urlpatterns = [ re_path(r'^token/pair/$', jwt_views.token_obtain_pair, name='token_obtain_pair'), re_path(r'^token/refresh/$', jwt_views.token_refresh, name='token_refresh'), re_path(r'^token/sliding/$', jwt_views.token_obtain_sliding, name='token_obtain_sliding'), re_path(r'^token/sliding/refresh/$', jwt_views.token_refresh_sliding, name='token_refresh_sliding'), re_path(r'^token/verify/$', jwt_views.token_verify, name='token_verify'), re_path(r'^token/blacklist/$', jwt_views.token_blacklist, name='token_blacklist'), re_path(r'^test-view/$', views.test_view, name='test_view'), ] djangorestframework-simplejwt-5.0.0/tests/utils.py000066400000000000000000000055701413160305600224700ustar00rootroot00000000000000import contextlib from django.db import connection from django.db.migrations.executor import MigrationExecutor from django.test import TestCase, TransactionTestCase from rest_framework.test import APIClient from rest_framework_simplejwt.compat import reverse from rest_framework_simplejwt.settings import api_settings def client_action_wrapper(action): def wrapper_method(self, *args, **kwargs): if self.view_name is None: raise ValueError('Must give value for `view_name` property') reverse_args = kwargs.pop('reverse_args', tuple()) reverse_kwargs = kwargs.pop('reverse_kwargs', dict()) query_string = kwargs.pop('query_string', None) url = reverse(self.view_name, args=reverse_args, kwargs=reverse_kwargs) if query_string is not None: url = url + '?{0}'.format(query_string) return getattr(self.client, action)(url, *args, **kwargs) return wrapper_method class APIViewTestCase(TestCase): client_class = APIClient def authenticate_with_token(self, type, token): """ Authenticates requests with the given token. """ self.client.credentials(HTTP_AUTHORIZATION='{} {}'.format(type, token)) view_name = None view_post = client_action_wrapper('post') view_get = client_action_wrapper('get') @contextlib.contextmanager def override_api_settings(**settings): old_settings = {} for k, v in settings.items(): # Save settings try: old_settings[k] = api_settings.user_settings[k] except KeyError: pass # Install temporary settings api_settings.user_settings[k] = v # Delete any cached settings try: delattr(api_settings, k) except AttributeError: pass yield for k in settings.keys(): # Delete temporary settings api_settings.user_settings.pop(k) # Restore saved settings try: api_settings.user_settings[k] = old_settings[k] except KeyError: pass # Delete any cached settings try: delattr(api_settings, k) except AttributeError: pass class MigrationTestCase(TransactionTestCase): migrate_from = None migrate_to = None def setUp(self): self.migrate_from = [self.migrate_from] self.migrate_to = [self.migrate_to] # Reverse to the original migration executor = MigrationExecutor(connection) executor.migrate(self.migrate_from) old_apps = executor.loader.project_state(self.migrate_from).apps self.setUpBeforeMigration(old_apps) # Run the migration to test executor.loader.build_graph() executor.migrate(self.migrate_to) self.apps = executor.loader.project_state(self.migrate_to).apps def setUpBeforeMigration(self, apps): pass djangorestframework-simplejwt-5.0.0/tests/views.py000066400000000000000000000006541413160305600224630ustar00rootroot00000000000000from rest_framework import permissions from rest_framework.response import Response from rest_framework.views import APIView from rest_framework_simplejwt import authentication class TestView(APIView): permission_classes = (permissions.IsAuthenticated,) authentication_classes = (authentication.JWTAuthentication,) def get(self, request): return Response({'foo': 'bar'}) test_view = TestView.as_view() djangorestframework-simplejwt-5.0.0/tox.ini000066400000000000000000000021151413160305600211170ustar00rootroot00000000000000[tox] envlist= py{37,38,39}-dj22-drf310-tests py{37,38,39}-dj{22,31,32}-drf{311,312}-tests py39-djmain-drf312-tests docs lint [gh-actions] python= 3.7: py37 3.8: py38, docs, lint 3.9: py39 [gh-actions:env] DJANGO= 2.2: dj22 3.0: dj30 3.1: dj31 3.2: dj32 main: djmain DRF= 3.10: drf310 3.11: drf311 3.12: drf312 [testenv] usedevelop=True commands = pytest {posargs:tests} --cov-append --cov-report=xml --cov=rest_framework_simplejwt extras= test python-jose setenv= PYTHONDONTWRITEBYTECODE=1 deps= dj22: Django>=2.2,<2.3 dj31: Django>=3.1,<3.2 dj32: Django>=3.2,<3.3 drf310: djangorestframework>=3.10,<3.11 drf311: djangorestframework>=3.11,<3.12 drf312: djangorestframework>=3.12,<3.13 djmain: https://github.com/django/django/archive/main.tar.gz allowlist_externals=make [testenv:lint] extras = lint commands = flake8 ./rest_framework_simplejwt ./tests --ignore=E501 isort --check-only --diff ./rest_framework_simplejwt ./tests [testenv:docs] extras = doc commands = make build-docs