معرفی شرکت ها


djwto-0.0.4rc0


Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر

توضیحات

JWT based Auth layer built on top of Django.
ویژگی مقدار
سیستم عامل -
نام فایل djwto-0.0.4rc0
نام djwto
نسخه کتابخانه 0.0.4rc0
نگهدارنده []
ایمیل نگهدارنده []
نویسنده Willian Fuks
ایمیل نویسنده willian.fuks@gmail.com
آدرس صفحه اصلی -
آدرس اینترنتی https://pypi.org/project/djwto/
مجوز MIT License
[![Build Status](https://github.com/WillianFuks/djwto/actions/workflows/run-tests.yml/badge.svg)](https://github.com/WillianFuks/djwto/actions) [![Build Status](https://github.com/WillianFuks/djwto/actions/workflows/publish-to-pypi.yml/badge.svg)](https://github.com/WillianFuks/djwto/actions) [![Coverage Status](https://codecov.io/gh/WillianFuks/djwto/branch/master/graph/badge.svg)](https://codecov.io/gh/WillianFuks/djwto/branch/master/) [![PyPI version](https://badge.fury.io/py/djwto.svg)](https://badge.fury.io/py/djwto) [![GitHub license](https://img.shields.io/github/license/WillianFuks/djwto.svg)](https://github.com/WillianFuks/djwto/blob/master/LICENSE) [![Documentation Status](https://readthedocs.org/projects/djwto/badge/?version=latest)](https://djwto.readthedocs.io/en/latest/?badge=latest) [![Pyversions](https://img.shields.io/pypi/pyversions/djwto.svg)](https://pypi.python.org/pypi/djwto) ![](https://raw.githubusercontent.com/WillianFuks/djwto/master/logo.png) Welcome to **djwto**! djwto ("*jot two*") is an alternative library offering support for JWT based authentication on top of the Django framework. Its main features are: - Authentication either through a **Bearer** token or **Cookies**. - **Access** token can be divided into two parts where one part is not encoded and can be used by the client (hence the lib name). - **CSRF** protection by default. - **Customizable**. Add your own code when you see fit. - **Full Auth Layer**: protect your views by requiring the JWT tokens to be present in the income request. Also available for the permissions layer. ## Documentation Complete [documentation](https://djwto.readthedocs.io/en/latest/?) is also available at ReadTheDocs. ## Installation Install it through pip directly: ```sh pip install djwto ``` Then make it available in your `INSTALLED_APPS`: ```sh INSTALLED_APPS = [ 'django.contrib.auth', 'django.contrib.contenttypes', ... 'djwto' ] ``` And for using its defaults urls, add it to your `urls.py` project file: ```python from django.contrib import admin from django.urls import path, include urlpatterns = [ path('', include('djwto.urls')), ] ``` ## Requirements - Python (3.7, 3.8, 3.9) - Django 3+ ## Overview Contents: - [Json](#json) - [One-cookie](#one-cookie) - [Two-cookies](#two-cookies) - [Settings](#settings) - [Endpoints](#endpoints) - [Signals](#signals) - [Customization](#customization) - [Protecting Views](#protecting-views) - [Contributing and Bugs](#contributing-and-bugs) djwto offers 3 main ways to process the JWT tokens, which is defined by the settings `DJWTO_MODE`. Despite of the mode running, the tokens are always returned as `acccess` and `refresh`. The first is intended to be short-lived and used more often whereas the second lives longer and is only sent on a specific path as defined by the setting `DJWTO_REFRESH_COOKIE_PATH`. Its purpose, as the name implies, is to refresh and create a new *access* token. Here's an overview of each mode available: ### JSON In this setting the tokens are returned as a direct JSON response to the client. Here's an example using the `requests` library, running on a simple demo Django project: ```python import requests sess = requests.Session() r = sess.post('https://localhost:8001/login/', data={'username': 'alice', 'password': 'pass'}) r.json() {'refresh': 'eyJ0eXAiO.eyJpc3MiOiJ.QXq8sbgIEgT', 'access': 'eyJ0eXAiOi.eyJpc3MiOiJ.TtSnWdrhWuX'} ``` In order to make further requests to the backend the client would have to grab the tokens and add them to the ``AUTHORIZATION`` header with the **Bearer** pattern. ### ONE-COOKIE In this mode, the tokens are returned in Cookies. Here's an example: ```python sess = requests.Session() r = sess.post('https://localhost:8001/login/', data={'username': 'alice', 'password': 'pass'}) sess.cookies <RequestsCookieJar[Cookie(name='csrftoken', value='DB6kR7o'), Cookie(name='jwt_access', value='eyJ0.eyJpc.kJsR'), Cookie(name='jwt_refresh', value='eyJ0e.eyJ.wWr')]> ``` Notice we have: - `csrftoken` - `jwt_access` - `jwt_refresh` The first must be sent on a `header` on views protected by CSRF and the last two are the ones used for the auth layer. ### TWO-COOKIES Similar to `ONE-COOKIE` but this time the *access* token is divided in two pieces. Here's an example: ```python import base64 sess = requests.Session() r = sess.post('https://localhost:8001/login/', data={'username': 'alice', 'password': 'pass'}) sess.cookies <RequestsCookieJar[Cookie(name='csrftoken', value='N1vJ9D'), Cookie(name='jwt_access_payload', value='eyJhdWQiO.ZXJuYW1lIj.FsaWN'), Cookie(name='jwt_access_token', value='eyJ0eXAi.OiJKV1QiLC.JhbGciOiJIU'), Cookie(name='jwt_refresh', value='eyJ0eXA.iOiJKV1Qi.LCJhbGc')]> base64.b64decode(sess.cookies['jwt_access_payload']) b'{"aud": "aud", "exp": "2021-06-18T02:32:55.144", "iat": "2021-06-17T18:12:55.144", "iss": "iss", "jti": "0b2d199d-f233-4203-bdab-693c03bca505", "refresh_iat": 1623953575, "sub": "sub", "type": "access", "user": {"id": 1, "perms": [], "username": "alice"}}' ``` Now we have the following cookies: - `csrftoken` - `jwt_access_payload` - `jwt_access_token` - `jwt_refresh` Notice the access token now have two components: payload and the token itself. The payload is the original payload encoded in `base64` which can be used by the client receiving those cookies. The other part though is protected from javascript access and should only be used by the backend. ## Settings Here's an overview of all settings available for djwto: ```python DJWTO_ISS_CLAIM: Optional[str] = getattr(settings, 'DJWTO_ISS_CLAIM', None) DJWTO_SUB_CLAIM: Optional[str] = getattr(settings, 'DJWTO_SUB_CLAIM', None) DJWTO_AUD_CLAIM: Optional[Union[List[str], str]] = getattr(settings, 'DJWTO_AUD_CLAIM', None) DJWTO_IAT_CLAIM: bool = getattr(settings, 'DJWTO_IAT_CLAIM', True) DJWTO_JTI_CLAIM: bool = getattr(settings, 'DJWTO_JTI_CLAIM', True) DJWTO_ALLOW_REFRESH_UPDATE: bool = getattr(settings, 'DJWTO_ALLOW_REFRESH_UPDATE', True) DJWTO_ACCESS_TOKEN_LIFETIME = getattr(settings, 'DJWTO_ACCESS_TOKEN_LIFETIME', timedelta(minutes=5)) DJWTO_REFRESH_TOKEN_LIFETIME = getattr(settings, 'DJWTO_REFRESH_TOKEN_LIFETIME', timedelta(days=1)) DJWTO_NBF_LIFETIME: Optional[timedelta] = getattr(settings, 'DJWTO_NBF_LIFETIME', timedelta(minutes=0)) DJWTO_SIGNING_KEY: str = getattr(settings, 'DJWTO_SIGNING_KEY', os.environ['DJWTO_SIGNING_KEY']) # Only set if Algorithm uses asymetrical signing. DJWTO_VERIFYING_KEY: Optional[str] = getattr(settings, 'DJWTO_VERIFYING_KEY', None) DJWTO_ALGORITHM: str = getattr(settings, 'DJWTO_ALGORITHM', 'HS256') DJWTO_MODE: Literal['JSON', 'ONE-COOKIE', 'TWO-COOKIES'] = getattr(settings, 'DJWTO_MODE', 'JSON') DJWTO_REFRESH_COOKIE_PATH: str = getattr(settings, 'DJWTO_REFRESH_COOKIE_PATH', 'api/token/refresh') DJWTO_SAME_SITE: str = getattr(settings, 'DJWTO_SAME_SITE', 'Lax') DJWTO_DOMAIN: Optional[str] = getattr(settings, 'DJWTO_DOMAIN', None) DJWTO_CSRF: bool = getattr(settings, 'DJWTO_CSRF', True) ``` For a thorough view of each please refer to the official [docs](https://djwto.readthedocs.io/en/latest/?). ## Endpoints Here's an overview of each endpoint offered by djwto and how to use them. For each endpoint, if something goes wrong then a key `error` should be available in the response and its value should be an explanation for the event. ### `/login/` Simply POST to this endpoint with data containing the user `username` and their input `password`, the return response, if valid, should contain the newly created JWTs for the client. The return type depends on which mode djwto is running; here's an example on ``DJWTO_MODE=JSON`` mode making use of the [requests](https://docs.python-requests.org/en/master/>) library: ```python import requests sess = requests.Session() sess.verify = False # For testing locally r = sess.post('https://localhost:8001/login/', data={'username': 'alice', 'password': 'pass'}) r.json() {'refresh': 'eyJ0eXAiO.eyJpc3MiOiJ.QXq8sbgIEgT', 'access': 'eyJ0eXAiOi.eyJpc3MiOiJ.TtSnWdrhWuX'} ``` ### `/validate/` Sometimes the frontend may want to confirm whether a given token is still valid or not; that's the purpose of this endpoint. Supposing that the *login* process has already taken place, here's an example of *access* token validation on mode ``JSON``: ```python import requests sess = requests.Session() sess.verify = False # For testing locally sess.headers.update({'AUTHORIZATION': f'Bearer {r.json()["access"]}'}) r = sess.post('https://localhost:8001/validate_access/') print(r.json()) {'msg': 'Token is valid'} ``` Here's an example for `TWO-COOKIES:` ```python import requests sess = requests.Session() sess.verify = False # For testing locally sess.post('https://localhost:8001/login/', data={'username': 'alice', 'password': 'pass'}) sess.headers.update({'X-CSRFToken': sess.cookies['csrftoken']}) r = sess.post('https://localhost:8001/validate_access/', headers={'REFERER': 'https://localhost:8001'}) ``` Notice that the `CSRF` token is being sent in the header as `X-CSRFToken` and also there's a `REFERER` value indicating from where the client is coming (this is mandatory as by Django's built-in csrf techniques protection). In order to validate the *refresh*, here's an example: ```python import requests sess = requests.Session() sess.verify = False # For testing locally sess.post('https://localhost:8001/login/', data={'username': 'alice', 'password': 'pass'}) sess.headers.update({'X-CSRFToken': sess.cookies['csrftoken']}) r = sess.post('https://localhost:8001/api/token/refresh/validate_refresh/', headers={'REFERER': 'https://localhost:8001'}, data={'jwt_type': 'refresh'}) ``` Notice that the `POST` must send the data `jwt_type='refresh'` to specify for the backend which token to use. ### `/refresh_access/` The *access* token is designed to be short-lived, that is, it grants access for clients for a brief period of time before it goes expired. The reasoning is that after it expires the API has a chance to validate whether the client can continue receiving new tokens or not (so in case the client logged out or was blacklisted for some reason they'd lose access thereafter). When the token expires, a new one can be obtained by posting the refresh token to this endpoint. Here's an example for ``JSON`` mode: ```python import requests sess = requests.Session() sess.verify = False # For testing locally r = sess.post('https://localhost:8001/login/', data={'username': 'alice', 'password': 'pass'}) sess.headers.update({'AUTHORIZATION': f'Bearer {r.json()["refresh"]}'}) r = sess.post('https://localhost:8001/api/token/refresh/refresh_access/', headers={'REFERER': 'https://localhost:8001'}) print(r.json()) {"refresh": ..., "access": ...} ``` ### `/update_refresh/` At some scenarios it may be interesting for the JWT auth process to also be able to update the *refresh* token. This may occur for instance in an eCommerce environment when the customer is finishing the purchase process and may get blocked due expired token (which is highly undesirable). In order to allow this feature to be available, set ``settings.DJWTO_ALLOW_REFRESH_UPDATE`` to ``True``. Here's an example for ``JSON`` mode: ```python import requests sess = requests.Session() sess.verify = False # For testing locally r = sess.post('https://localhost:8001/login/', data={'username': 'alice', 'password': 'pass'}) sess.headers.update({'AUTHORIZATION': f'Bearer {r.json()["refresh"]}'}) r = sess.post('https://localhost:8001/api/token/refresh/update_refresh/') print(r.json()) {"refresh": '...', "access": '...'} ``` ### `/logout/` When logging a user out, if ``JTI`` is available then the tokens will be blacklisted. In either case, the tokens are deleted (both *access* and *refresh*). The request ``path`` must contain ``settings.DJWTO_REFRESH_COOKIE_PATH``. Here's an example for ``JSON`` mode: ```python import requests sess = requests.Session() sess.verify = False # For testing locally r = sess.post('https://localhost:8001/login/', data={'username': 'alice', 'password': 'pass'}) sess.headers.update({'AUTHORIZATION': f'Bearer {r.json()["refresh"]}'}) r = sess.post('https://localhost:8001/api/token/refresh/logout/') print(r.content) b'{"msg": "Token successfully blacklisted."}' ``` For either `ONE-COOKIE` or `TWO-COOKIES`: ```python import requests sess = requests.Session() sess.verify = False # For testing locally r = sess.post('https://localhost:8001/login/', data={'username': 'alice', 'password': 'pass'}) sess.headers.update({'X-CSRFToken': sess.cookies['csrftoken']}) r = sess.post('https://localhost:8001/api/token/refresh/logout/', headers={'REFERER': 'https://localhost:8001'}, data={'jwt_type': 'refresh'}) print(r.content) b'{"msg": "Token successfully blacklisted."}' r = sess.delete('https://localhost:8001/api/token/refresh/logout/', headers={'REFERER': 'https://localhost:8001'}, data={'jwt_type': 'refresh'}) print(r.content) b'{"msg": "Tokens successfully deleted."}' ``` Notice that the verb ``DELETE`` is also available with removes the cookies from the response. This option only works on for the cookie-based settings. If after blacklisting a token a request is sent for updating either *access* or *refresh*, the process should fail: ```python r = sess.post('https://localhost:8001/api/token/refresh/update_refresh/', headers={'REFERER':'https://localhost:8001'}, data={'jwt_type': 'refresh'}) print(r.content) b'{"error": "Can\'t update refresh token."}' r = sess.post('https://localhost:8001/api/token/refresh/refresh_access/', headers={'REFERER': 'https://localhost:8001'}, data={'jwt_type': 'refresh'}) print(r.content) b'{"error": "Can\'t update access token."}' ``` ## Signals djwto offers by default a set of signals that you can use in your projects for tracking down when certain events take place. In order to demonstrate how it works, consider a regular Django project created with an app called ``testapp``. Here's a list of all available signals offered by the package: ### `/jwt_logged_in/` The signal is triggered when a successfull loggin happens. Here's how to connect it: ```python def ready(self): import djwto.signals as signals def handler(sender, **kwargs): print('sender: ', sender) print('kwargs: ', kwargs) signals.jwt_logged_in.connect( handler, sender='GetTokensView' ) ``` On each successfull login the new function `handler` will process with appropriate input arguments. ### `/jwt_login_fail/` The signal is triggered when a loggin fails by any reason. Here's how to connect it: ```python def ready(self): import djwto.signals as signals def handler(sender, **kwargs): print('sender: ', sender) print('kwargs: ', kwargs) signals.jwt_login_fail.connect(handler, sender='GetTokensView') ``` ### `/jwt_blacklisted/` The signal is sent when a token is successfully blacklisted: ```python def ready(self): import djwto.signals as signals def handler(sender, **kwargs): print('sender: ', sender) print('kwargs: ', kwargs) signals.jwt_blacklisted.connect(handler, sender='BlackListTokenView') ``` ### `/jwt_token_validated/` The signal is sent each time a validation request is processed: ```python def ready(self): import djwto.signals as signals def handler(sender, **kwargs): print('sender: ', sender) print('kwargs: ', kwargs) signals.jwt_token_validated.connect(handler, sender='ValidateTokensView') ``` ### `/jwt_access_refreshed/` The signal is sent when the *access* token is successfully refreshed: ```python def ready(self): import djwto.signals as signals def handler(sender, **kwargs): print('sender: ', sender) print('kwargs: ', kwargs) signals.jwt_access_refreshed.connect(handler, sender='RefreshAccessView') ``` ### `/jwt_refresh_updated/` If the updating endpoint is available (as by the ``settings``) then when the updating of the refresh successfully happens this signal is sent. Example: ```python def ready(self): import djwto.signals as signals def handler(sender, **kwargs): print('sender: ', sender) print('kwargs: ', kwargs) signals.jwt_refresh_updated.connect(handler, sender='UpdateRefreshView') ``` ## Customization djwto offers the possibility for the client to customize how parts of the code should be processed, replacing the original logic. Let's suppose a regular Django project with an app called *testapp*. It's possible to specify customizations for djwto when the app is [ready](https://docs.djangoproject.com/en/3.2/ref/applications/#django.apps.AppConfig.ready>). For instance, if your project requires to also bring the customer's email when the JWT creation is running, here's one way of doing it: ```python from django.apps import AppConfig class TestappConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'testapp' def ready(self): import djwto.tokens as tokens def new_process_user(user): return { user.USERNAME_FIELD: user.get_username(), 'email': user.email, 'id': user.pk, 'perms': tokens.process_perms(user) } tokens.process_claims = new_process_user ``` Running the loggin process for ``TWO-COOKIES``, we get now: ```python import requests import base64 sess = requests.Session() sess.verify = False # For testing locally r = sess.post('https://localhost:8001/login/', data={'username': 'alice', 'password': 'pass'}) base64.b64decode(sess.cookies['jwt_access_payload']) b'{"aud": "aud", "exp": 1624259339, "iat": 1624229339, "iss": "iss", "jti": "900f4f1a-3e0f-4843-9997-9fd8d032684e", "refresh_iat": 1624229339, "sub": "sub", "type": "access", "user": {"email": "alice@djwto.com", "id": 1, "perms": [], "username": "alice"}}' ``` Now the `user` key retrieves the user email as well. Feel free to customize the code as you see fit. ## Protecting Views djwto offers decorators that can be used on views in order to add the authentication layer protection. There are two functions for doing so: ### `jwt_login_required` djwto offers the decorator ``jwt_loging_required`` for guaranteeing a view to only be processed if the required and valid JWT token was sent in the request. Here's an example. Suppose again a regular Django project with the usual *testapp* with a view defined as: ```python import djwto.authentication as auth # type: ignore from django.views import View from django.utils.decorators import method_decorator from django.http.response import HttpResponse class ProtectedView(View): def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) @method_decorator(auth.jwt_login_required) def get(self, request, *args, **kwargs): refresh_claims = request.payload print(refresh_claims) return HttpResponse('worked!') ``` Notice the decorator ``auth.jwt_login_required`` protecting the view. Now let's see what happens if we send a request without the JWT available: ```python r = sess.get('https://localhost:8001/testapp/protect/') r.content b'{"error": "Cookie \\"jwt_access_token\\" cannot be empty."}' ``` If we properly login: ```python r.content b'worked!' ``` ### `jwt_perm_required` djwto also offers the possibility of protecting views with permissions that should be available in the JWT token. Here's an example: decorate a view with the ``jwt_perm_required`` function like so: ```python class PermsProtectedView(View): def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) @method_decorator(auth.jwt_login_required) @method_decorator(auth.jwt_perm_required(['perm1'])) def get(self, request, *args, **kwargs): refresh_claims = request.payload print(refresh_claims) return HttpResponse('perms also worked!') ``` The function receives a list of permissions as input and only if the input JWT token contains those permissions is that the view will be processed. Now, sending the request with a regular JWT token returns: ```python r = sess.get('https://localhost:8001/testapp/perms_protect/') r.content b'{"error": "Invalid permissions for jwt token."}' ``` Suppose now that *Alice* has the permission ``perm1`` stored in the database. Here's the result then: ```python r.content b'perms also worked!' ``` If you run these examples with settings ``DJWTO_MODE=TWO-COOKIES``, you'll be able to see what's inside the returned cookie, like so: ```python base64.b64decode(sess.cookies['jwt_access_payload']) b'{"aud": "aud", "exp": 1624269024, "iat": 1624239024, "iss": "iss", "jti": "0e9bfcdc-d684-47b5-9677-0cb5e5e88893", "refresh_iat": 1624239024, "sub": "sub", "type": "access", "user": {"email": "alice@djwto.com", "id": 1, "perms": ["perm1"], "username": "alice"}}' ``` ## Contributing and Bugs Contributions are very welcome! If you want to send a PR please consider first discussing your implementation on an issue. Also, if you find bugs (this is still an alpha project!) please let us know by also opening an issue on the official repository.


نیازمندی

مقدار نام
- Django
- pyjwt[crypto]
- typing-extensions


زبان مورد نیاز

مقدار نام
>=3 Python


نحوه نصب


نصب پکیج whl djwto-0.0.4rc0:

    pip install djwto-0.0.4rc0.whl


نصب پکیج tar.gz djwto-0.0.4rc0:

    pip install djwto-0.0.4rc0.tar.gz