From a96c525e5f5af9462d3965b7701512f6e79a6695 Mon Sep 17 00:00:00 2001 From: Ezeudoh Tochukwu Date: Sun, 30 Oct 2022 10:56:23 +0100 Subject: [PATCH] October-30-2022:Jazz master branch merge (#15) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix ES locale translation error (#499) * chore: test on Django 4.0 (#495) * chore: test on Django 4.0 * Remove Django 3.1 support from trove * Remove Django 3.1 from tox * Remove 3.1 reference in tox.ini Co-authored-by: Andrew Chen Wang <60190294+Andrew-Chen-Wang@users.noreply.github.com> * Stop deleting blacklist on user delete (#516) * OutstandingToken user on_delete should be null * Add test to verify that deleting a User doesn't remove tokens from the blacklist This is a rather unexpected default behavior. Deleting a User means that their blacklisted tokens become live again. * Add migration for cascading User deletion to SET_NULL instead of DELETE * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: Andrew Chen Wang <60190294+Andrew-Chen-Wang@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * [pre-commit.ci] pre-commit autoupdate (#498) updates: - [github.com/pre-commit/pre-commit-hooks: v4.0.1 → v4.1.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.0.1...v4.1.0) - [github.com/asottile/yesqa: v1.2.3 → v1.3.0](https://github.com/asottile/yesqa/compare/v1.2.3...v1.3.0) - [github.com/pycqa/isort: 5.9.3 → 5.10.1](https://github.com/pycqa/isort/compare/5.9.3...5.10.1) - [github.com/psf/black: 21.9b0 → 21.12b0](https://github.com/psf/black/compare/21.9b0...21.12b0) - [github.com/pre-commit/pre-commit-hooks: v4.0.1 → v4.1.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.0.1...v4.1.0) - [github.com/asottile/pyupgrade: v2.28.0 → v2.31.0](https://github.com/asottile/pyupgrade/compare/v2.28.0...v2.31.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Enable ECDSA algorithms supported by PyJWT (#520) * Parameterize some tests to reduce duplication and make it easy to add more algorithms This way new algorithms can be added to the basic test set simply by adding their backends to TestTokenBackend.backends. * Enable ECDSA algorithms supported by PyJWT Enable the algorithms and add basic tests for them. Also convert the ALLOWED_ALGORITHMS constant to a set for a minor style cleanup. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Simplify using custom token classes in serializers (#517) For most cases this could be done by overriding get_token, which is simple enough. The exception was TokenRefreshSerializer.validate where the entire method needed to be copy-pasted to allow using a custom replacement for RefreshToken. The other cases are changed the same way mainly for consistency. * [pre-commit.ci] pre-commit autoupdate (#524) updates: - [github.com/psf/black: 21.12b0 → 22.1.0](https://github.com/psf/black/compare/21.12b0...22.1.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Make the token serializer configurable (#521) * Update translation files (#526) * Add default __getattr__ behavior to models.TokenUser (#528) * Add default __getattr__ behavior to models.TokenUser to allow getting custom claims defined in serializers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Allow overriding access token class (#529) * Maintain compatibility with serializer_class overrides (#530) * Consider leeway when checking expiry (#458) * Add locale checker to CI (#456) * Add locale checker to CI * Just pip install Django * Add gettext package to OS * Add sudo to apt-get * Use @2ykwang 's updated script * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Only update on push to master * To avoid pain points of PRs and histories being split * Trying to use Andrew's username for pushing to see if that works * Use separate workflow file Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Update CHANGELOG.md for v5.1.0 (#527) * Update CHANGELOG.md for v5.0.1 * Update CHANGELOG.md * Remove looking for maintainers in README since Jazzband Co-authored-by: Andrew Chen Wang <60190294+Andrew-Chen-Wang@users.noreply.github.com> * Fix i18n CI (#538) * Open PR on i18n (#539) * fix small typo (#540) * Setup initial PyJWT 1.7.1 support (#536) * Fix release locale checker (#541) * Update locale files (#542) * [pre-commit.ci] pre-commit autoupdate (#545) updates: - [github.com/asottile/pyupgrade: v2.31.0 → v2.31.1](https://github.com/asottile/pyupgrade/compare/v2.31.0...v2.31.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Remove the JWTTokenUserAuthentication from the Experimental Features #546 (#547) * Change from git protocol to https protocol (#555) * [pre-commit.ci] pre-commit autoupdate (#551) updates: - [github.com/psf/black: 22.1.0 → 22.3.0](https://github.com/psf/black/compare/22.1.0...22.3.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Fix leeway type error (#554) * Fix lewway type error * Add test case * Update Korean translation * Add type hints * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix translation revert POT-Creation-Date * update translation Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * [pre-commit.ci] pre-commit autoupdate (#557) * Add info on TokenBlacklistView to the docs (#558) * chore(ci): add informational Codecov status checks (#559) * Update JWTStatelessUserAuthentication docs (#561) * Allow none jti claim token type claim (#567) * Allow customizing token JSON encoding (#568) * Allow specifying custom JSONEncoder for TokenBackend * Make TokenBackend JSONEncoder configurable * [pre-commit.ci] pre-commit autoupdate (#571) updates: - [github.com/asottile/pyupgrade: v2.32.0 → v2.32.1](https://github.com/asottile/pyupgrade/compare/v2.32.0...v2.32.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Update CHANGELOG to 5.2.0 (#573) * Locale update (#574) * update & correct supported versions in docs (#576) * update & correct supported versions in docs * Add DRF supported version Co-authored-by: Andrew Chen Wang <60190294+Andrew-Chen-Wang@users.noreply.github.com> * Add Swedish translations (#579) * Fixed issue #543 (#586) * Allow optional installation of the 'cryptography' package (#543) * Update docs (#543) * Update docs (#543) * Update docs/getting_started.rst Co-authored-by: Andrew Chen Wang <60190294+Andrew-Chen-Wang@users.noreply.github.com> * fix for code-block (#543) * another fix for code-block (#543) * fix: removed extra line (#543) Co-authored-by: Andrew Chen Wang <60190294+Andrew-Chen-Wang@users.noreply.github.com> * [pre-commit.ci] pre-commit autoupdate (#587) updates: - [github.com/pre-commit/pre-commit-hooks: v4.2.0 → v4.3.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.2.0...v4.3.0) - [github.com/pre-commit/pre-commit-hooks: v4.2.0 → v4.3.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.2.0...v4.3.0) - [github.com/asottile/pyupgrade: v2.32.1 → v2.34.0](https://github.com/asottile/pyupgrade/compare/v2.32.1...v2.34.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * [pre-commit.ci] pre-commit autoupdate (#589) updates: - [github.com/psf/black: 22.3.0 → 22.6.0](https://github.com/psf/black/compare/22.3.0...22.6.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * [pre-commit.ci] pre-commit autoupdate (#590) * [pre-commit.ci] pre-commit autoupdate (#594) updates: - [github.com/asottile/pyupgrade: v2.37.1 → v2.37.2](https://github.com/asottile/pyupgrade/compare/v2.37.1...v2.37.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * [pre-commit.ci] pre-commit autoupdate (#597) updates: - [github.com/asottile/pyupgrade: v2.37.2 → v2.37.3](https://github.com/asottile/pyupgrade/compare/v2.37.2...v2.37.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * [pre-commit.ci] pre-commit autoupdate (#601) updates: - [github.com/asottile/yesqa: v1.3.0 → v1.4.0](https://github.com/asottile/yesqa/compare/v1.3.0...v1.4.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Fix uncaught exception with JWK (#600) * Fix uncaught exception with JWK * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Allow tests to run on older JWT versions Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Test on Django 4.1 (#604) * [pre-commit.ci] pre-commit autoupdate (#609) * Add v5.2.1 changes (#611) * use non-deprecated UTC timezone (#606) RemovedInDjango50Warning * Added Romanian translations (#591) * Added Romanian translations * Changed some translations according to the grammatical rules of the Romanian language * Changed some translations according to the advices of: https://github.com/marcellefter https://github.com/uoxiu Co-authored-by: Daniel Cuznetov * allow verification skipping (#605) * allow verify skip verification if VERIFYING_KEY is not set * Update settings.py * Update authentication.py * Update settings.py * Update authentication.py * [pre-commit.ci] pre-commit autoupdate (#619) * [pre-commit.ci] pre-commit autoupdate (#620) * Update locale files (#624) * Revert 605 (#629) * [pre-commit.ci] pre-commit autoupdate (#630) * [Docs] Fix typo in blacklist_app.rst (#593) * Fix typo in blacklist_app.rst `TokenBlackListView` -> `TokenBlacklistView` * Append CHANGELOG Co-authored-by: Andrew-Chen-Wang Co-authored-by: Marc Salat Co-authored-by: Christofer Bertonha Co-authored-by: Andrew Chen Wang <60190294+Andrew-Chen-Wang@users.noreply.github.com> Co-authored-by: vainu-arto <70135394+vainu-arto@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: yeongkwang Co-authored-by: Oscar Y Chen Co-authored-by: totycro Co-authored-by: Byron Motoche <37427699+byrpatrick@users.noreply.github.com> Co-authored-by: Vladimir <44180334+inti7ary@users.noreply.github.com> Co-authored-by: Tom Hu <88201630+thomasrockhu-codecov@users.noreply.github.com> Co-authored-by: Dennis Dinwiddie <51653728+denniskeends@users.noreply.github.com> Co-authored-by: abdurrahman <32853027+asaah18@users.noreply.github.com> Co-authored-by: Pasindu Prabhashitha <63661350+PasinduPrabhashitha@users.noreply.github.com> Co-authored-by: Armenak Baburyan <1723973+armenak-baburyan@users.noreply.github.com> Co-authored-by: Jeremy Mayeres <1524722+jerr0328@users.noreply.github.com> Co-authored-by: Benedikt S. Vogler Co-authored-by: Daniel Cuzneţov Co-authored-by: Daniel Cuznetov Co-authored-by: Domenico Co-authored-by: Boseong Choi <31615733+cbscsm@users.noreply.github.com> Co-authored-by: Andrew-Chen-Wang --- CHANGELOG.md | 14 + docs/experimental_features.md | 23 -- docs/getting_started.md | 16 ++ docs/settings.md | 274 ++++++++++--------- ninja_jwt/authentication.py | 9 +- ninja_jwt/backends.py | 15 +- ninja_jwt/locale/es/LC_MESSAGES/django.po | 10 +- ninja_jwt/locale/es_AR/LC_MESSAGES/django.po | 9 +- ninja_jwt/locale/es_CL/LC_MESSAGES/django.po | 10 +- ninja_jwt/locale/fa_IR/LC_MESSAGES/django.po | 9 +- ninja_jwt/locale/fr/LC_MESSAGES/django.po | 10 +- ninja_jwt/locale/id_ID/LC_MESSAGES/django.po | 10 +- ninja_jwt/locale/it_IT/LC_MESSAGES/django.po | 10 +- ninja_jwt/locale/ko_KR/LC_MESSAGES/django.po | 10 +- ninja_jwt/locale/nl_NL/LC_MESSAGES/django.po | 9 +- ninja_jwt/locale/pl_PL/LC_MESSAGES/django.po | 9 +- ninja_jwt/locale/pt_BR/LC_MESSAGES/django.po | 10 +- ninja_jwt/locale/ro/LC_MESSAGES/django.mo | Bin 0 -> 2548 bytes ninja_jwt/locale/ro/LC_MESSAGES/django.po | 117 ++++++++ ninja_jwt/locale/ru_RU/LC_MESSAGES/django.po | 10 +- ninja_jwt/locale/sv/LC_MESSAGES/django.mo | Bin 2281 -> 2281 bytes ninja_jwt/locale/sv/LC_MESSAGES/django.po | 16 +- ninja_jwt/settings.py | 4 +- pyproject.toml | 6 + tests/test_backends.py | 60 ++-- tests/test_token_blacklist.py | 26 +- tests/test_tokens.py | 26 +- tests/test_views.py | 1 - 28 files changed, 452 insertions(+), 271 deletions(-) delete mode 100644 docs/experimental_features.md create mode 100644 ninja_jwt/locale/ro/LC_MESSAGES/django.mo create mode 100644 ninja_jwt/locale/ro/LC_MESSAGES/django.po diff --git a/CHANGELOG.md b/CHANGELOG.md index 09ebb87cd..3f1f6816c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ ## Unreleased +## Version 5.2.2 + +Major security release + +* Revert #605 in https://github.com/jazzband/djangorestframework-simplejwt/pull/629 +* Fix typo in blacklist_app.rst by @cbscsm in https://github.com/jazzband/djangorestframework-simplejwt/pull/593 + +## Version 5.2.1 + +* Add Swedish translations by @PasinduPrabhashitha in https://github.com/jazzband/djangorestframework-simplejwt/pull/579 +* Fixed issue #543 by @armenak-baburyan in https://github.com/jazzband/djangorestframework-simplejwt/pull/586 +* Fix uncaught exception with JWK by @jerr0328 in https://github.com/jazzband/djangorestframework-simplejwt/pull/600 +* Test on Django 4.1 by @2ykwang in https://github.com/jazzband/djangorestframework-simplejwt/pull/604 + ## Version 5.2.0 * Remove the JWTTokenUserAuthentication from the Experimental Features #546 by @byrpatrick in https://github.com/jazzband/djangorestframework-simplejwt/pull/547 diff --git a/docs/experimental_features.md b/docs/experimental_features.md deleted file mode 100644 index 937d6de28..000000000 --- a/docs/experimental_features.md +++ /dev/null @@ -1,23 +0,0 @@ - -The `JWTTokenUserAuth` backend\'s `authenticate` method does -not perform a database lookup to obtain a user instance. Instead, it -returns a `ninja_jwt.models.TokenUser` instance which acts as a -stateless user object backed only by a validated token instead of a -record in a database. This can facilitate developing single sign-on -functionality between separately hosted Django apps which all share the -same token secret key. To use this feature, add the -`ninja_jwt.authentication.JWTTokenUserAuth` backend (instead -of the default `JWTAuth` backend) to the Django Ninja Extra route definition - -```python -from ninja_extra import api_controller, route -from ninja_jwt.authentication import JWTTokenUserAuth - - -@api_controller -class MyController: - @route.get('/some-endpoint', auth=JWTTokenUserAuth()) - def some_endpoint(self): - pass - -``` diff --git a/docs/getting_started.md b/docs/getting_started.md index 10b2a64ef..8abacfb71 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -97,3 +97,19 @@ curl \ ... {"access":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiY29sZF9zdHVmZiI6IuKYgyIsImV4cCI6MTIzNTY3LCJqdGkiOiJjNzE4ZTVkNjgzZWQ0NTQyYTU0NWJkM2VmMGI0ZGQ0ZSJ9.ekxRxgb9OKmHkfy-zs1Ro_xs1eMLXiR17dIDBVxeT-w"} ``` + +Cryptographic Dependencies (Optional) +------------------------------------- + +If you are planning on encoding or decoding tokens using certain digital +signature algorithms (i.e. RSA and ECDSA; visit PyJWT for other algorithms), you will need to install the +cryptography_ library. This can be installed explicitly, or as a required +extra in the `django-ninja-jwt` requirement: + + pip install django-ninja-jwt[crypto] + + +The `django-ninja-jwt[crypto]` format is recommended in requirements +files in projects using `Ninja JWT`, as a separate `cryptography` requirement +line may later be mistaken for an unused requirement and removed. +[cryptography](https://cryptography.io) \ No newline at end of file diff --git a/docs/settings.md b/docs/settings.md index 657b6b790..436ad2f26 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -57,192 +57,194 @@ 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` -======================= +``BLACKLIST_AFTER_ROTATION`` +---------------------------- -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 `'ninja_jwt.token_blacklist',` to your `INSTALLED_APPS` in -the settings file to use this setting. +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 ``'ninja_jwt.token_blacklist',`` to your +``INSTALLED_APPS`` in the settings file to use this setting. Learn more about `/blacklist_app`{.interpreted-text role="doc"}. `UPDATE_LAST_LOGIN` =================== -When set to `True`, last_login field in the auth_user table is updated -upon login (TokenObtainPairController). +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. + 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/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 Ninja 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 Ninja 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 +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. +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. +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. +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 ''. When -set to `None`, this field is excluded from the token backend and is not -used during validation. +``JWK_URL`` +---------- -`LEEWAY` -======== +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 is used to give some margin to the expiration time. This can be -an integer for seconds or a `datetime.timedelta`. Please reference - +``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. -`USER_ID_FIELD` -=============== +``AUTH_HEADER_TYPES`` +--------------------- -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 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. -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. +``AUTH_HEADER_NAME`` +---------------------------- -`USER_AUTHENTICATION_RULE` -========================== +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. -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. +``USER_ID_FIELD`` +----------------- -`AUTH_TOKEN_CLASSES` -==================== +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. -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. +``USER_ID_CLAIM`` +----------------- -`TOKEN_TYPE_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. -The claim name that is used to store a token's type. More about this in -the `Token types` section below. +``USER_AUTHENTICATION_RULE`` +---------------------------- -`JTI_CLAIM` -=========== +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. -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. +``AUTH_TOKEN_CLASSES`` +---------------------- -`TOKEN_USER_CLASS` -================== +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. -A stateless user object which is backed by a validated token. Used only -for the experimental JWTTokenUserAuthentication authentication backend. -The value is a dotted path to your subclass of -`rest_framework_simplejwt.models.TokenUser`, which also is the default. +``TOKEN_TYPE_CLAIM`` +-------------------- -`SLIDING_TOKEN_LIFETIME` -======================== +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. + +``TOKEN_USER_CLASS`` +-------------------- + +A stateless user object which is backed by a validated token. Used only for +the JWTStatelessUserAuthentication authentication backend. The value +is a dotted path to your subclass of ``rest_framework_simplejwt.models.TokenUser``, +which also is the default. + +``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. -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`` +---------------------------------- -`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. -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`` +----------------------------------- -`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. -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. diff --git a/ninja_jwt/authentication.py b/ninja_jwt/authentication.py index c257ed00a..b07fe66e8 100644 --- a/ninja_jwt/authentication.py +++ b/ninja_jwt/authentication.py @@ -75,10 +75,10 @@ def authenticate(self, request: HttpRequest, token: str) -> Any: return self.jwt_authenticate(request, token) -class JWTTokenUserAuth(JWTBaseAuthentication, HttpBearer): +class JWTStatelessUserAuthentication(JWTBaseAuthentication, HttpBearer): """ - Experimental features - JWTTokenUserAuth backend + An authentication plugin that authenticates requests through a JSON web + token provided in a request header without performing a database lookup to obtain a user instance. """ def authenticate(self, request: HttpRequest, token: str) -> Any: @@ -97,6 +97,9 @@ def get_user(self, validated_token: Any) -> Type[AbstractUser]: return api_settings.TOKEN_USER_CLASS(validated_token) +JWTTokenUserAuth = JWTStatelessUserAuthentication + + def default_user_authentication_rule(user) -> bool: # Prior to Django 1.10, inactive users could be authenticated with the # default `ModelBackend`. As of Django 1.10, the `ModelBackend` diff --git a/ninja_jwt/backends.py b/ninja_jwt/backends.py index 691c32d61..1a6cd1b52 100644 --- a/ninja_jwt/backends.py +++ b/ninja_jwt/backends.py @@ -10,13 +10,13 @@ from .utils import format_lazy try: - from jwt import PyJWKClient + from jwt import PyJWKClient, PyJWKClientError JWK_CLIENT_AVAILABLE = True except ImportError: JWK_CLIENT_AVAILABLE = False -ALLOWED_ALGORITHMS = ( +ALLOWED_ALGORITHMS = { "HS256", "HS384", "HS512", @@ -26,7 +26,7 @@ "ES256", "ES384", "ES512", -) +} class TokenBackend: @@ -95,7 +95,10 @@ def get_verifying_key(self, token) -> bytes: return self.signing_key if self.jwks_client: - return self.jwks_client.get_signing_key_from_jwt(token).key + try: + return self.jwks_client.get_signing_key_from_jwt(token).key + except PyJWKClientError as ex: + raise TokenBackendError(_("Token is invalid or expired")) from ex return self.verifying_key @@ -144,5 +147,5 @@ def decode(self, token, verify=True) -> Dict[str, Any]: ) except InvalidAlgorithmError as ex: raise TokenBackendError(_("Invalid algorithm specified")) from ex - except InvalidTokenError: - raise TokenBackendError(_("Token is invalid or expired")) + except InvalidTokenError as ex: + raise TokenBackendError(_("Token is invalid or expired")) from ex diff --git a/ninja_jwt/locale/es/LC_MESSAGES/django.po b/ninja_jwt/locale/es/LC_MESSAGES/django.po index e8ef0214c..f9c328a04 100644 --- a/ninja_jwt/locale/es/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/es/LC_MESSAGES/django.po @@ -46,14 +46,14 @@ msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" -msgstr "Algoritmo especificado no válido" - -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "El token es inválido o ha expirado" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "Algoritmo especificado no válido" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "La combinación de credenciales no tiene una cuenta activa" diff --git a/ninja_jwt/locale/es_AR/LC_MESSAGES/django.po b/ninja_jwt/locale/es_AR/LC_MESSAGES/django.po index 266da6940..6bc063fb1 100644 --- a/ninja_jwt/locale/es_AR/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/es_AR/LC_MESSAGES/django.po @@ -49,16 +49,17 @@ msgstr "" #: backends.py:88 msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." -msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" msgstr "" -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "El token es inválido o ha expirado" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "" diff --git a/ninja_jwt/locale/es_CL/LC_MESSAGES/django.po b/ninja_jwt/locale/es_CL/LC_MESSAGES/django.po index ef08e9bb5..bf79a738b 100644 --- a/ninja_jwt/locale/es_CL/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/es_CL/LC_MESSAGES/django.po @@ -46,14 +46,14 @@ msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" -msgstr "" - -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Token inválido o expirado" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "" diff --git a/ninja_jwt/locale/fa_IR/LC_MESSAGES/django.po b/ninja_jwt/locale/fa_IR/LC_MESSAGES/django.po index 82406b0f0..f580d224d 100644 --- a/ninja_jwt/locale/fa_IR/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/fa_IR/LC_MESSAGES/django.po @@ -42,16 +42,17 @@ msgstr "" #: backends.py:88 msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." -msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" msgstr "" -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "توکن نامعتبر است یا منقضی شده است" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "هیچ اکانت فعالی برای اطلاعات داده شده یافت نشد" diff --git a/ninja_jwt/locale/fr/LC_MESSAGES/django.po b/ninja_jwt/locale/fr/LC_MESSAGES/django.po index 9f18fe37a..2205714da 100644 --- a/ninja_jwt/locale/fr/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/fr/LC_MESSAGES/django.po @@ -46,14 +46,14 @@ msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" -msgstr "L'algorithme spécifié est invalide" - -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Le jeton est invalide ou expiré" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "L'algorithme spécifié est invalide" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "Aucun compte actif n'a été trouvé avec les identifiants fournis" diff --git a/ninja_jwt/locale/id_ID/LC_MESSAGES/django.po b/ninja_jwt/locale/id_ID/LC_MESSAGES/django.po index 1bf40310e..bdc61cc82 100644 --- a/ninja_jwt/locale/id_ID/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/id_ID/LC_MESSAGES/django.po @@ -45,14 +45,14 @@ msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" -msgstr "Algoritma yang ditentukan tidak valid" - -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Token tidak valid atau kedaluwarsa" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "Algoritma yang ditentukan tidak valid" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "Tidak ada akun aktif yang ditemukan dengan kredensial yang diberikan" diff --git a/ninja_jwt/locale/it_IT/LC_MESSAGES/django.po b/ninja_jwt/locale/it_IT/LC_MESSAGES/django.po index 7cca6dff3..327ac04db 100644 --- a/ninja_jwt/locale/it_IT/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/it_IT/LC_MESSAGES/django.po @@ -49,14 +49,14 @@ msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" -msgstr "L'algoritmo specificato non è valido" - -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Il token non è valido o è scaduto" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "L'algoritmo specificato non è valido" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "Nessun account attivo trovato con queste credenziali" diff --git a/ninja_jwt/locale/ko_KR/LC_MESSAGES/django.po b/ninja_jwt/locale/ko_KR/LC_MESSAGES/django.po index 49704c66a..0b6df170c 100644 --- a/ninja_jwt/locale/ko_KR/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/ko_KR/LC_MESSAGES/django.po @@ -47,14 +47,14 @@ msgstr "" "알 수 없는 타입 '{}', 'leeway' 값은 반드시 int, float 또는 timedelta 타입이어" "야 합니다." -#: backends.py:147 -msgid "Invalid algorithm specified" -msgstr "잘못된 알고리즘이 지정되었습니다" - -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "유효하지 않거나 만료된 토큰" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "잘못된 알고리즘이 지정되었습니다" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "지정된 자격 증명에 해당하는 활성화된 사용자를 찾을 수 없습니다" diff --git a/ninja_jwt/locale/nl_NL/LC_MESSAGES/django.po b/ninja_jwt/locale/nl_NL/LC_MESSAGES/django.po index 47b91aa51..f07db53ed 100644 --- a/ninja_jwt/locale/nl_NL/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/nl_NL/LC_MESSAGES/django.po @@ -43,16 +43,17 @@ msgstr "" #: backends.py:88 msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." -msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" msgstr "" -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Token is niet geldig of verlopen" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "Geen actief account gevonden voor deze gegevens" diff --git a/ninja_jwt/locale/pl_PL/LC_MESSAGES/django.po b/ninja_jwt/locale/pl_PL/LC_MESSAGES/django.po index b1f6160b5..fa295200c 100644 --- a/ninja_jwt/locale/pl_PL/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/pl_PL/LC_MESSAGES/django.po @@ -42,16 +42,17 @@ msgstr "" #: backends.py:88 msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." -msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" msgstr "" -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Token jest niepoprawny lub wygasł" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "Nie znaleziono aktywnego konta dla podanych danych uwierzytelniających" diff --git a/ninja_jwt/locale/pt_BR/LC_MESSAGES/django.po b/ninja_jwt/locale/pt_BR/LC_MESSAGES/django.po index 1b6303241..0da814688 100644 --- a/ninja_jwt/locale/pt_BR/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/pt_BR/LC_MESSAGES/django.po @@ -46,14 +46,14 @@ msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" -msgstr "Algoritmo inválido especificado" - -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "O token é inválido ou expirado" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "Algoritmo inválido especificado" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "Usuário e/ou senha incorreto(s)" diff --git a/ninja_jwt/locale/ro/LC_MESSAGES/django.mo b/ninja_jwt/locale/ro/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..d87cddd1107d20cbbe80c221d8da0991d01ab1ca GIT binary patch literal 2548 zcmZ{lyN?_-9LEiWM;1sRykEj7M3)npO#&&xUIKZLz)4An!X6@`K;9Yet}h;Y0>1_q!5_g7K(;;0T?Aji(1UM+pMelz--G1OPayg6TRZ+8q?9~~ zck*Cr`cNz>j`Td7!5#yVmh?Q0U$T#4h)^WmbRInq;-#1(6v-EW!OfH}`hEy6#qtRn z;7Re$p9p9SW9!rv5tKE2EJY<9ucHsVv?d5;cv!dGH=>k9CAF$mkQHAOI!ez@3S%rJ zofJXxU@u9-*HsvEV|mzUB)5)hHIzZslD)0gu$+Zx7j&qK4LXTis z7}q#nmTIVE#m-qS$^cD3X`=~H5KWa&4P(hi=@eL0NfVUNp7k-iaP`^(_c8=!Mtm$h zA4u5ZWNp`E)#c~46kcLHg!92>@nqSWP+APMoNV@8)=zFGjinZPTs27j4YS#sVoX-9{7a$cbjWfh|4=tzjY(hA{H0 z*SaiMBCHN9d*kVIH7G`bX6KzJSx7&t5so8XVQnE6SZ8o?$SqI)s2>` z>!EW&Hr9pWtRJbWI1xv_=-VFeoDn`0eJ709B3ResZwaHM<|pG7BZFPzN2)YYw&Meb zj*V)RQn$4A8`y0|QH*4dJKH(CeD>7LnuFZ~os+nDDBhxv%h2ONUJm;k6q;G$EiHAj_g3?7N<;UD^z%oA0DXCIj^G=x;SlJ?R(rbY3Pq0T;loj=z*mUdneua zBI>;Yr-rB(d4 z(w$DN13`yoTA#F+*$kpRRYDRNV{zod==x5B-IVdXRa}k1@oP0E_~Umwnwp*`4tM+ ud}*=%+;u_EqRPmfDph&uRPJCyR6tuP(^Snm7>X(PFVq1(Q~#Qt%KibnKly?H literal 0 HcmV?d00001 diff --git a/ninja_jwt/locale/ro/LC_MESSAGES/django.po b/ninja_jwt/locale/ro/LC_MESSAGES/django.po new file mode 100644 index 000000000..3a8d5d036 --- /dev/null +++ b/ninja_jwt/locale/ro/LC_MESSAGES/django.po @@ -0,0 +1,117 @@ +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR Daniel Cuznetov , 2022. +msgid "" +msgstr "" +"Project-Id-Version: djangorestframework_simplejwt\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-07-13 10:45+0100\n" +"Last-Translator: Daniel Cuznetov \n" +"Language: ro\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:78 +msgid "Authorization header must contain two space-delimited values" +msgstr "" +"Header-ul(antetul) de autorizare trebuie să conțină două valori separate " +"prin spațiu" + +#: authentication.py:104 +msgid "Given token not valid for any token type" +msgstr "Tokenul dat nu este valid pentru niciun tip de token" + +#: authentication.py:116 authentication.py:143 +msgid "Token contained no recognizable user identification" +msgstr "Tokenul nu conține date de identificare a utilizatorului" + +#: authentication.py:121 +msgid "User not found" +msgstr "Utilizatorul nu a fost găsit" + +#: authentication.py:124 +msgid "User is inactive" +msgstr "Utilizatorul este inactiv" + +#: backends.py:67 +msgid "Unrecognized algorithm type '{}'" +msgstr "Tipul de algoritm '{}' nu este recunoscut" + +#: backends.py:73 +msgid "You must have cryptography installed to use {}." +msgstr "Trebuie să aveți instalată criptografia pentru a utiliza {}." + +#: backends.py:88 +msgid "" +"Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." +msgstr "" +"Tipul '{}' nu este recunoscut, 'leeway' trebuie să fie de tip int, float sau " +"timedelta." + +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 +msgid "Token is invalid or expired" +msgstr "Token nu este valid sau a expirat" + +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "Algoritm nevalid specificat" + +#: serializers.py:30 +msgid "No active account found with the given credentials" +msgstr "Nu a fost găsit cont activ cu aceste date de autentificare" + +#: settings.py:70 +msgid "" +"The '{}' setting has been removed. Please refer to '{}' for available " +"settings." +msgstr "" +"Setarea '{}' a fost ștearsă. Referați la '{}' pentru setări disponibile." + +#: token_blacklist/admin.py:68 +msgid "jti" +msgstr "jti" + +#: token_blacklist/admin.py:74 +msgid "user" +msgstr "utilizator" + +#: token_blacklist/admin.py:80 +msgid "created at" +msgstr "creat la" + +#: token_blacklist/admin.py:86 +msgid "expires at" +msgstr "expiră la" + +#: token_blacklist/apps.py:7 +msgid "Token Blacklist" +msgstr "Listă de token-uri blocate" + +#: tokens.py:30 +msgid "Cannot create token with no type or lifetime" +msgstr "Nu se poate crea token fără tip sau durată de viață" + +#: tokens.py:102 +msgid "Token has no id" +msgstr "Tokenul nu are id" + +#: tokens.py:115 +msgid "Token has no type" +msgstr "Tokenul nu are tip" + +#: tokens.py:118 +msgid "Token has wrong type" +msgstr "Tokenul are tipul greșit" + +#: tokens.py:170 +msgid "Token has no '{}' claim" +msgstr "Tokenul nu are reclamația '{}'" + +#: tokens.py:175 +msgid "Token '{}' claim has expired" +msgstr "Reclamația tokenului '{}' a expirat" + +#: tokens.py:230 +msgid "Token is blacklisted" +msgstr "Tokenul este în listă de tokenuri blocate" diff --git a/ninja_jwt/locale/ru_RU/LC_MESSAGES/django.po b/ninja_jwt/locale/ru_RU/LC_MESSAGES/django.po index e7f5df55e..554b33ed0 100644 --- a/ninja_jwt/locale/ru_RU/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/ru_RU/LC_MESSAGES/django.po @@ -50,14 +50,14 @@ msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" -msgstr "" - -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Токен недействителен или просрочен" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "Не найдено активной учетной записи с указанными данными" diff --git a/ninja_jwt/locale/sv/LC_MESSAGES/django.mo b/ninja_jwt/locale/sv/LC_MESSAGES/django.mo index 5370a8829e94f24ea2060ed3e2746f06d8d1336b..5c75a354dc1c100459f1402076e23b3178887f86 100644 GIT binary patch delta 15 WcmaDU_)>5~JS$Ui+2%yn4rTx{)&-aV delta 15 WcmaDU_)>5~JS$Ub@#aL<4rTx{Vg-i) diff --git a/ninja_jwt/locale/sv/LC_MESSAGES/django.po b/ninja_jwt/locale/sv/LC_MESSAGES/django.po index d9e754e92..6e12ed405 100644 --- a/ninja_jwt/locale/sv/LC_MESSAGES/django.po +++ b/ninja_jwt/locale/sv/LC_MESSAGES/django.po @@ -14,8 +14,7 @@ msgstr "" #: authentication.py:78 msgid "Authorization header must contain two space-delimited values" -msgstr "" -"Auktoriseringshuvudet måste innehålla två mellanslagsavgränsade värden" +msgstr "Auktoriseringshuvudet måste innehålla två mellanslagsavgränsade värden" #: authentication.py:104 msgid "Given token not valid for any token type" @@ -46,14 +45,14 @@ msgid "" "Unrecognized type '{}', 'leeway' must be of type int, float or timedelta." msgstr "" -#: backends.py:147 -msgid "Invalid algorithm specified" -msgstr "Ogiltig algoritm har angetts" - -#: backends.py:149 exceptions.py:38 tokens.py:44 +#: backends.py:102 backends.py:152 exceptions.py:38 tokens.py:44 msgid "Token is invalid or expired" msgstr "Token är ogiltig eller har löpt ut" +#: backends.py:150 +msgid "Invalid algorithm specified" +msgstr "Ogiltig algoritm har angetts" + #: serializers.py:30 msgid "No active account found with the given credentials" msgstr "Inget aktivt konto hittades med de angivna användaruppgifterna" @@ -63,8 +62,7 @@ msgid "" "The '{}' setting has been removed. Please refer to '{}' for available " "settings." msgstr "" -"Inställningen '{}' har tagits bort. Se '{}' för tillgängliga " -"inställningar" +"Inställningen '{}' har tagits bort. Se '{}' för tillgängliga inställningar" #: token_blacklist/admin.py:68 msgid "jti" diff --git a/ninja_jwt/settings.py b/ninja_jwt/settings.py index bf091514e..3a9cd7b56 100644 --- a/ninja_jwt/settings.py +++ b/ninja_jwt/settings.py @@ -58,8 +58,8 @@ class Config: TOKEN_USER_CLASS: Any = Field("ninja_jwt.models.TokenUser") AUTH_TOKEN_CLASSES: List[Any] = Field(["ninja_jwt.tokens.AccessToken"]) JSON_ENCODER: Optional[Any] = Field(None) - TOKEN_TYPE_CLAIM: str = Field("token_type") - JTI_CLAIM: str = Field("jti") + TOKEN_TYPE_CLAIM: Optional[str] = Field("token_type") + JTI_CLAIM: Optional[str] = Field("jti") SLIDING_TOKEN_REFRESH_EXP_CLAIM: str = Field("refresh_exp") SLIDING_TOKEN_LIFETIME: timedelta = Field(timedelta(minutes=5)) SLIDING_TOKEN_REFRESH_LIFETIME: timedelta = Field(timedelta(days=1)) diff --git a/pyproject.toml b/pyproject.toml index b01f91032..7e6901ff2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,7 @@ Source = "https://github.com/eadwinCode/django-ninja-jwt" [project.optional-dependencies] test = [ + "cryptography", "pytest", "pytest-cov", "pytest-django", @@ -74,6 +75,11 @@ test = [ "python-jose==3.3.0", "click==8.0.4" ] + +crypto = [ + "cryptography>=3.3.1", +] + doc = [ "mkdocs", "mkdocs-material", diff --git a/tests/test_backends.py b/tests/test_backends.py index 98783876b..b70aa6757 100644 --- a/tests/test_backends.py +++ b/tests/test_backends.py @@ -42,13 +42,7 @@ def default(self, obj): class TestTokenBackend: - backends = ( - TokenBackend("HS256", SECRET), - TokenBackend("RS256", PRIVATE_KEY, PUBLIC_KEY), - TokenBackend("ES256", ES256_PRIVATE_KEY, ES256_PUBLIC_KEY), - TokenBackend("ES384", ES256_PRIVATE_KEY, ES256_PUBLIC_KEY), - TokenBackend("ES512", ES256_PRIVATE_KEY, ES256_PUBLIC_KEY), - ) + backends = () @pytest.fixture(autouse=True) def setUp(self): @@ -59,6 +53,13 @@ def setUp(self): "RS256", PRIVATE_KEY, PUBLIC_KEY, AUDIENCE, ISSUER ) self.payload = {"foo": "bar"} + self.backends = ( + self.hmac_token_backend, + self.rsa_token_backend, + TokenBackend("ES256", ES256_PRIVATE_KEY, ES256_PUBLIC_KEY), + TokenBackend("ES384", ES256_PRIVATE_KEY, ES256_PUBLIC_KEY), + TokenBackend("ES512", ES256_PRIVATE_KEY, ES256_PUBLIC_KEY), + ) def test_init(self): # Should reject unknown algorithms @@ -252,18 +253,6 @@ def test_decode_success(self, title, backend): assert backend.decode(token) == 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") - - assert ( - self.hmac_token_backend.decode(no_exp_token, verify=False) == self.payload - ) - def test_decode_aud_iss_success(self): self.payload["exp"] = aware_utcnow() + timedelta(days=1) self.payload["foo"] = "baz" @@ -313,6 +302,39 @@ def test_decode_rsa_aud_iss_jwk_success(self): assert jwk_token_backend.decode(token) == self.payload + @pytest.mark.skipif( + not JWK_CLIENT_AVAILABLE, + reason="PyJWT 1.7.1 doesn't have JWK client", + ) + def test_decode_jwk_missing_key_raises_tokenbackenderror(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"}, + ) + + with patch("ninja_jwt.backends.PyJWKClient") as mock_jwk_module: + mock_jwk_client = mock.MagicMock() + + mock_jwk_module.return_value = mock_jwk_client + mock_jwk_client.get_signing_key_from_jwt.side_effect = jwt.PyJWKClientError( + "Unable to find a signing key that matches" + ) + + # Note the PRIV,PUB care is intentially the original pairing + jwk_token_backend = TokenBackend( + "RS256", PRIVATE_KEY, PUBLIC_KEY, AUDIENCE, ISSUER, JWK_URL + ) + + with pytest.raises(TokenBackendError, match="Token is invalid or expired"): + jwk_token_backend.decode(token) + def test_decode_when_algorithm_not_available(self): token = jwt.encode(self.payload, PRIVATE_KEY, algorithm="RS256") if IS_OLD_JWT: diff --git a/tests/test_token_blacklist.py b/tests/test_token_blacklist.py index 649c37b63..07fb8aafc 100644 --- a/tests/test_token_blacklist.py +++ b/tests/test_token_blacklist.py @@ -4,7 +4,6 @@ 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 ninja_jwt.exceptions import TokenError from ninja_jwt.schema import TokenVerifySerializer @@ -13,7 +12,7 @@ from ninja_jwt.tokens import AccessToken, RefreshToken, SlidingToken from ninja_jwt.utils import aware_utcnow, datetime_from_epoch -from .utils import MigrationTestCase, override_api_settings +from .utils import MigrationTestCase @pytest.mark.django_db @@ -176,6 +175,29 @@ def test_it_should_delete_any_expired_tokens(self): not_expired_3["jti"], ] + def test_token_blacklist_will_not_be_removed_on_User_delete(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 pytest.raises(TokenError) as e: + # Should raise exception + RefreshToken(str(token)) + assert "blacklisted" in e.exception.args[0] + + # Delete the User and try again + self.user.delete() + + with pytest.raises(TokenError) as e: + # Should raise exception + RefreshToken(str(token)) + assert "blacklisted" in e.exception.args[0] + @pytest.mark.django_db class TestTokenVerifySerializerShouldHonourBlacklist(MigrationTestCase): diff --git a/tests/test_tokens.py b/tests/test_tokens.py index 5142d15ac..04c97f136 100644 --- a/tests/test_tokens.py +++ b/tests/test_tokens.py @@ -218,6 +218,18 @@ def test_set_jti(self): assert "jti" in token assert old_jti != token["jti"] + def test_optional_jti(self, monkeypatch): + with monkeypatch.context() as m: + m.setattr(api_settings, "JTI_CLAIM", None) + token = MyToken() + assert "jti" not in token + + def test_optional_type_token(self, monkeypatch): + with monkeypatch.context() as m: + m.setattr(api_settings, "TOKEN_TYPE_CLAIM", None) + token = MyToken() + assert "type" not in token + def test_set_exp(self): now = make_utc(datetime(year=2000, month=1, day=1)) @@ -333,20 +345,6 @@ def test_check_exp(self): "refresh_exp", current_time=current_time + timedelta(days=2) ) - def test_check_token_not_expired_if_in_leeway(self): - token = MyToken() - token.set_exp("refresh_exp", lifetime=timedelta(days=1)) - - datetime_in_leeway = token.current_time + timedelta(days=1) - - with pytest.raises(TokenError): - token.check_exp("refresh_exp", current_time=datetime_in_leeway) - - # a token 1 day expired is valid if leeway is 2 days - token.token_backend.leeway = timedelta(days=2).total_seconds() - token.check_exp("refresh_exp", current_time=datetime_in_leeway) - token.token_backend.leeway = 0 - @pytest.mark.django_db def test_for_user(self, monkeypatch): username = "test_user" diff --git a/tests/test_views.py b/tests/test_views.py index 96a43bc29..28f70138c 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -490,6 +490,5 @@ def test_it_should_return_401_if_token_is_blacklisted(self): ) # make sure other tests are not affected del self.view_name - assert res.status_code == 401 assert res.data["code"] == "token_not_valid"