A Django app that allows you to generate SIMPLE_JWT with two different lifetimes plus more options.
Project description
simplejwt_multisessions is a Django app based on djangorestframework-simplejwt that allows you to generate SIMPLE_JWT with two different lifetimes and session features.
Introduction
simplejwt_multisessions is an MIT licensed Django app that allows Django devs to generate refresh tokens with two different lifetimes i.e., “SHORT” and “LONG”. simplejwt_multisessions is designed to manage tokens and sessions together. This app also can assign different policies to each type of lifetime category.
Why use simplejwt_multisessions?
Generates “LONG” and “SHORT” simpleJWT lifetime.
Sets information of the session on the initialization. e.g., “Safari 15.6.1, iOS 15.6.1, iPhone”
- Extendable option for each session type with different policies.
Extension of the session after each request by generating a new refresh token.
Extension of the session once after each specific timedelta by generating a new refresh token.
- Limitable option for the available number of sessions. i.e., a User can log in as a “LONG” session
with only 3 different Endpoints/Devices or the User can log in as a “SHORT” session at the same time with only 2 different Endpoints/Devices. 1. Sets max number of available sessions for each session type: “SHORT”, “LONG”. 2. Sets destroying oldest active sessions. i.e., if the user reaches the max number of active sessions then by logging a new session the oldest session will be destroyed.
Returns list of active i.e., not expired sessions assigned to requested user with its session_id.
Destroys sessions by their session_id.
Destroys all other active/not expired sessions except the one making the request.
Differentiable URLs for different APIs e.g., “login”, “refresh”, “list”, “destroy”, “destroyAllOther” and “logout”.
- Most of API’s permission_classes are set to AllowAny and need to receive secret_key in the body of the request
which is defined in settings of simplejwt_multisessions in settings.py and the rest are set to IsAuthenticated and need nothing else.
Requirements
Python 3.8+
Django 3.2+
djangorestframework 3.12+
User guide
Installation
Install with pip:
$ python -m pip install simplejwt_multisessions
Clone the repo into your Django Project
settings.py configuration
To start using simplejwt_multisessions, after installation of requirements first you should add rest_framework_simplejwt.token_blacklist to INSTALLED_APPS in settings.py and then add simplejwt_multisessions to it.
# Django Project settings.py
INSTALLED_APPS = [
# ...
"rest_framework_simplejwt.token_blacklist",
"simplejwt_multisessions",
# ...
]
According to djangorestframework-simplejwt configuration instruction: https://django-rest-framework-simplejwt.readthedocs.io/en/latest/getting_started.html#project-configuration
# Django Project settings.py
REST_FRAMEWORK = {
# ...
'DEFAULT_AUTHENTICATION_CLASSES': (
# ...
'rest_framework_simplejwt.authentication.JWTAuthentication'
)
# ...
}
You also need to add djangorestframework-simplejwt settings to settings.py https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html#settings
# Django project settings.py
from datetime import timedelta
#...
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=30),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': False,
'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': 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',
'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
After adding SEIMPLE_JWT to your Django’s settings.py, Now you are good to add simplejwt_multisessions’s settings to settings.py
# Django project settings.py
JWT_MULTISESSIONS = {
'SECRET': SECRET_KEY,
'LONG_SESSION':{ # This section is the "LONG_SESSION" configuration.
'REFRESH_TOKEN_LIFETIME': SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'], # or you can set the timedelta what you wish e.g., timedelta(days = 29)
'ROTATE_REFRESH_TOKENS': False, # If set this flag true then after each token refresh request a new refresh token will be generated and the old one will be blacklisted
'UPDATE_LAST_LOGIN': False, # By settings this flag to true after each request the session `lastUpdateDate` updates.
'BLACKLIST_AFTER_ROTATION': False,
'EXTEND_SESSION': True, # This flag specifies whether should extend the refresh lifetime after each refresh request or shouldn't
'EXTEND_SESSION_EVERY_TIME': False, # If `EXTEND_SESSION` is set to true this flag specifies to extend refresh lifetime with the exact amount of `REFRESH_TOKEN_LIFETIME`
'EXTEND_SESSION_ONCE_AFTER_EACH': timedelta(days= 15), # If `EXTEND_SESSION` is set to true but `EXTEND_SESSION_EVERY_TIME` is set to False, then the refresh token will be extended as much as this property specifies.
'LIMIT_NUMBER_OF_AVAIL_SESSIONS': True, # This feature decides to restrict the number of available sessions.
'MAX_NUMBER_ACTIVE_SESSIONS': 5, # If `LIMIT_NUMBER_OF_AVAIL_SESSIONS` is set to true then this property defines the max number of active sessions and prevents requests to exceed more than its value.
'DESTROY_OLDEST_ACTIVE_SESSION': True, # If `LIMIT_NUMBER_OF_AVAIL_SESSIONS` is set to true by setting this flag to true and the max number of requests exceeds `MAX_NUMBER_ACTIVE_SESSIONS` then the newly arrived request will destroy the oldest session and the new session will be initialized.
},
'SHORT_SESSION': { # This section is the "SHORT_SESSION" configuration. The features are as same as what is mentioned in the `LONG_SESSION` section.
'REFRESH_TOKEN_LIFETIME': timedelta(days= 1),
'ROTATE_REFRESH_TOKENS': False,
'UPDATE_LAST_LOGIN': True,
'BLACKLIST_AFTER_ROTATION': True,
'EXTEND_SESSION': False,
'EXTEND_SESSION_EVERY_TIME': False,
'EXTEND_SESSION_ONCE_AFTER_EACH': timedelta(hours= 12),
'LIMIT_NUMBER_OF_AVAIL_SESSIONS': True,
'MAX_NUMBER_ACTIVE_SESSIONS': 2,
'DESTROY_OLDEST_ACTIVE_SESSION': True,
}
}
URLs
If you are intended to use simplejwt_multisessions you should instead of using URLs related to rest_framework_simplejwt use the URLs mentioned below:
# <root_of_your_django_project>/urls.py
#...
from simplejwt_multisessions.api.views import (
initializeSession,
refreshSession,
listOfActiveSessions,
destroySessionById,
destroyAllOtherSessions,
logout,
)
#...
urlpatterns = [
...
path('api/session/login/', initializeSession, name='initialize_session'),
path('api/session/refresh/', refreshSession, name='refresh_session'),
path('api/session/list/', listOfActiveSessions, name='list_sessions'),
path('api/session/destroy/', destroySessionById, name='destroy_session_by_id'),
path('api/session/destroyAllOther/', destroyAllOtherSessions, name="destroy_all_other"),
path('api/session/logout/', logout, name='logout'),
#...
]
Usage
Login and Refresh
- For logging in or initializing a new session you need five variables to mention in your request’s body:
username: The username_field of the Django user model
password: The password of the Django user model
secret_key: The secret_key mentioned in JWT_MULTISESSIONS settings in settings.py
session: The session type of the request options: [“LONG”, “SHORT”]
info: This field can store arbitrary information about the session. for instance Device id or browser version or IP address e.g., “Safari 15.6.1, iOS 15.6.1, iPhone”. NOTICE: This field is not changeable after initialization.
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"username": "benyamin", "password": "somepassword",
"secret_key": "the_Secret_Key", "session": "LONG",
"info": "sample info"}' \
http://localhost:8000/api/session/login/
...
{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY2NTc1OTU2MywiaWF0IjoxNjYzMTY3NTYzLCJqdGkiOiJkOWFiYTMzYWNmMDg0ODU2YjJjMjA4OWJiYTFjZDc0YSIsInVzZXJfaWQiOjF9.zGwSDoJzoCEhIqwg3D4bRAwrnJ0DKRKSGOzdyyw__ho",
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYzMTY4MTYzLCJpYXQiOjE2NjMxNjc1NjMsImp0aSI6IjBlZGZlMmY3MzhiMzRhOWFhYzQ4ZDhjYzAxOTVmZjEzIiwidXNlcl9pZCI6MX0.QCEqyFmXk5-yHZ4dYKnhNx80o9mgAYRdgFfUtgV1lQQ",
"session_id": "ozdyyw__ho-10"
}
Same as classic simpleJWT You can use the returned access token to prove authentication for a protected view:
curl \
-H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYzMTY4MTYzLCJpYXQiOjE2NjMxNjc1NjMsImp0aSI6IjBlZGZlMmY3MzhiMzRhOWFhYzQ4ZDhjYzAxOTVmZjEzIiwidXNlcl9pZCI6MX0.QCEqyFmXk5-yHZ4dYKnhNx80o9mgAYRdgFfUtgV1lQQ" \
http://localhost:8000/api/some-protected-view/
When this access token expires, you can use the refresh token to obtain another access token: NOTICE: 1. Since refresh API’s permission_classes is AllowAny you need to enter the secret_key that is assigned in simplejwt_multisessions’s settings in settings.py. 2. You should enter “session” in your request’s body. This “session” must be the same as the one you used in the login request otherwise the session will be destroyed.
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"refresh":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY2NTc1OTU2MywiaWF0IjoxNjYzMTY3NTYzLCJqdGkiOiJkOWFiYTMzYWNmMDg0ODU2YjJjMjA4OWJiYTFjZDc0YSIsInVzZXJfaWQiOjF9.zGwSDoJzoCEhIqwg3D4bRAwrnJ0DKRKSGOzdyyw__ho",
"secret_key": "the_Secret_Key", "session": "LONG"}' \
http://localhost:8000/api/session/refresh/
...
{"access":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYzMTcwNTA5LCJpYXQiOjE2NjMxNjc1NjMsImp0aSI6IjQxNWMxNjcwMmVkNTRkYWM5MDk0OTkyMmExMjcyMTdjIiwidXNlcl9pZCI6MX0.gdSQUmoSt_-ir87xngbC7YIvwNsAXJaAy0l4IRfuT1I"}
List of all sessions
To see the list of user’s all active sessions you can use the list’s API. You have to enter “session” in the body of your request. “session” can be one of these options: [“SHORT”, “LONG”, “ALL”] 1. If you ask for “SHORT” sessions, in return you will just see the list of all short types of sessions and related info. 2. If you ask for “LONG” sessions, in return you will just see the list of all long types of sessions and related info. 3. If you ask for “ALL” sessions, in return you will see the list of all types of sessions and related info, and by “ALL”, it means “SHORT” and “LONG” sessions in total. NOTICE: Since this API’s permission_classes is set to IsAuthenticated the proof of authentication is mandatory.
“session”: “SHORT”
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYzMTY4MTYzLCJpYXQiOjE2NjMxNjc1NjMsImp0aSI6IjBlZGZlMmY3MzhiMzRhOWFhYzQ4ZDhjYzAxOTVmZjEzIiwidXNlcl9pZCI6MX0.QCEqyFmXk5-yHZ4dYKnhNx80o9mgAYRdgFfUtgV1lQQ" \
-d '{"session":"SHORT"}' \
http://localhost:8000/api/session/list/
{
"Short":
{
"Available Sessions":"0",
"Sessions":[
{
"session_id":"8e_ikeorsa-8",
"created_at":"2022-09-11T11:16:26Z",
"last_update":"2022-09-14T14:41:26Z",
"expires_at":"2022-09-15T14:41:26Z",
"session_info":"Safari 15.6.1, iOS 15.6.1, iPhone"
},
{
"session_id":"oetqjgyp1o-9",
"created_at":"2022-09-14T08:23:14Z",
"last_update":"2022-09-14T14:41:20Z",
"expires_at":"2022-09-15T14:41:20Z",
"session_info":"Chrome 105.0.0.0, OS X 10.15.7 64-bit"
}
]
}
}
“session”: “LONG”
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYzMTY4MTYzLCJpYXQiOjE2NjMxNjc1NjMsImp0aSI6IjBlZGZlMmY3MzhiMzRhOWFhYzQ4ZDhjYzAxOTVmZjEzIiwidXNlcl9pZCI6MX0.QCEqyFmXk5-yHZ4dYKnhNx80o9mgAYRdgFfUtgV1lQQ" \
-d '{"session":"LONG"}' \
http://localhost:8000/api/session/list/
{
"Long":
{
"Available Sessions":"2",
"Sessions":[
{
"session_id": "8oeptkbkk0-3",
"created_at": "2022-09-08T19:14:33Z",
"last_update": "2022-09-08T19:14:33.108983Z",
"expires_at": "2022-10-08T19:14:33Z",
"session_info": "iOS native app, V1.0.2"
},
{
"session_id": "sppfiktswq-4",
"created_at": "2022-09-08T19:14:59Z",
"last_update": "2022-09-08T19:14:59.905518Z",
"expires_at": "2022-10-08T19:14:59Z",
"session_info": "android native app, V1.4"
},
{
"session_id": "rvu6qdlcdc-5",
"created_at": "2022-09-08T19:15:22Z",
"last_update": "2022-09-08T19:15:22.407307Z",
"expires_at": "2022-10-08T19:15:22Z",
"session_info": "react native app, V2.5"
},
]
}
}
“session”: “ALL”
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYzMTY4MTYzLCJpYXQiOjE2NjMxNjc1NjMsImp0aSI6IjBlZGZlMmY3MzhiMzRhOWFhYzQ4ZDhjYzAxOTVmZjEzIiwidXNlcl9pZCI6MX0.QCEqyFmXk5-yHZ4dYKnhNx80o9mgAYRdgFfUtgV1lQQ" \
-d '{"session":"ALL"}' \
http://localhost:8000/api/session/list/
{
"Short":
{
"Available Sessions":"0",
"Sessions":[
{
"session_id":"8e_ikeorsa-8",
"created_at":"2022-09-11T11:16:26Z",
"last_update":"2022-09-14T14:41:26Z",
"expires_at":"2022-09-15T14:41:26Z",
"session_info":"Safari 15.6.1, iOS 15.6.1, iPhone"
},
{
"session_id":"oetqjgyp1o-9",
"created_at":"2022-09-14T08:23:14Z",
"last_update":"2022-09-14T14:41:20Z",
"expires_at":"2022-09-15T14:41:20Z",
"session_info":"Chrome 105.0.0.0, OS X 10.15.7 64-bit"
}
]
},
"Long":
{
"Available Sessions":"2",
"Sessions":[
{
"session_id": "8oeptkbkk0-3",
"created_at": "2022-09-08T19:14:33Z",
"last_update": "2022-09-08T19:14:33.108983Z",
"expires_at": "2022-10-08T19:14:33Z",
"session_info": "iOS native app, V1.0.2"
},
{
"session_id": "sppfiktswq-4",
"created_at": "2022-09-08T19:14:59Z",
"last_update": "2022-09-08T19:14:59.905518Z",
"expires_at": "2022-10-08T19:14:59Z",
"session_info": "android native app, V1.4"
},
{
"session_id": "rvu6qdlcdc-5",
"created_at": "2022-09-08T19:15:22Z",
"last_update": "2022-09-08T19:15:22.407307Z",
"expires_at": "2022-10-08T19:15:22Z",
"session_info": "react native app, V2.5"
},
]
}
}
Destroy session by their id
Sometimes End-User wants to terminate one of its active sessions. This action can be done by knowing the “session_id” and using ‘destroy’’s API. NOTICE: Since this API’s permission_classes is set to IsAuthenticated the proof of authentication is mandatory.
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYzMTY4MTYzLCJpYXQiOjE2NjMxNjc1NjMsImp0aSI6IjBlZGZlMmY3MzhiMzRhOWFhYzQ4ZDhjYzAxOTVmZjEzIiwidXNlcl9pZCI6MX0.QCEqyFmXk5-yHZ4dYKnhNx80o9mgAYRdgFfUtgV1lQQ" \
-d '{"session_id":"rvu6qdlcdc-5"}' \
http://localhost:8000/api/session/destroy/
{}
Destroy all other sessions
This API allows the End-User to destroy all other active sessions except the one that makes the request. Necessary inputs: [“session”, “refresh”]. “session” specifies the session type to be destroyed. “refresh” specifies the considered “refresh” token assigned to the current request session and is excluded from being destroyed. NOTICE: Since this API’s permission_classes is set to IsAuthenticated the proof of authentication is mandatory.
“session”: “SHORT”
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYzMTY4MTYzLCJpYXQiOjE2NjMxNjc1NjMsImp0aSI6IjBlZGZlMmY3MzhiMzRhOWFhYzQ4ZDhjYzAxOTVmZjEzIiwidXNlcl9pZCI6MX0.QCEqyFmXk5-yHZ4dYKnhNx80o9mgAYRdgFfUtgV1lQQ" \
-d '{"session":"SHORT",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY2NTc1OTU2MywiaWF0IjoxNjYzMTY3NTYzLCJqdGkiOiJkOWFiYTMzYWNmMDg0ODU2YjJjMjA4OWJiYTFjZDc0YSIsInVzZXJfaWQiOjF9.zGwSDoJzoCEhIqwg3D4bRAwrnJ0DKRKSGOzdyyw__ho"}' \
http://localhost:8000/api/session/destroyAllOther/
{}
“session”: “LONG”
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYzMTY4MTYzLCJpYXQiOjE2NjMxNjc1NjMsImp0aSI6IjBlZGZlMmY3MzhiMzRhOWFhYzQ4ZDhjYzAxOTVmZjEzIiwidXNlcl9pZCI6MX0.QCEqyFmXk5-yHZ4dYKnhNx80o9mgAYRdgFfUtgV1lQQ" \
-d '{"session":"LONG",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY2NTc1OTU2MywiaWF0IjoxNjYzMTY3NTYzLCJqdGkiOiJkOWFiYTMzYWNmMDg0ODU2YjJjMjA4OWJiYTFjZDc0YSIsInVzZXJfaWQiOjF9.zGwSDoJzoCEhIqwg3D4bRAwrnJ0DKRKSGOzdyyw__ho"}' \
http://localhost:8000/api/session/destroyAllOther/
{}
“session”: “ALL”
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjYzMTY4MTYzLCJpYXQiOjE2NjMxNjc1NjMsImp0aSI6IjBlZGZlMmY3MzhiMzRhOWFhYzQ4ZDhjYzAxOTVmZjEzIiwidXNlcl9pZCI6MX0.QCEqyFmXk5-yHZ4dYKnhNx80o9mgAYRdgFfUtgV1lQQ" \
-d '{"session":"ALL",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY2NTc1OTU2MywiaWF0IjoxNjYzMTY3NTYzLCJqdGkiOiJkOWFiYTMzYWNmMDg0ODU2YjJjMjA4OWJiYTFjZDc0YSIsInVzZXJfaWQiOjF9.zGwSDoJzoCEhIqwg3D4bRAwrnJ0DKRKSGOzdyyw__ho"}' \
http://localhost:8000/api/session/destroyAllOther/
{}
Logout
This API facilitates logging out the session. Necessary inputs: [“secret_key”, “refresh”]. The secret_key mentioned in JWT_MULTISESSIONS settings in settings.py. “refresh” must be the refresh token the one that makes the request and is about to log out.
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"secret_key": "the_Secret_Key",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY2NTc1OTU2MywiaWF0IjoxNjYzMTY3NTYzLCJqdGkiOiJkOWFiYTMzYWNmMDg0ODU2YjJjMjA4OWJiYTFjZDc0YSIsInVzZXJfaWQiOjF9.zGwSDoJzoCEhIqwg3D4bRAwrnJ0DKRKSGOzdyyw__ho"}' \
http://localhost:8000/api/session/logout/
{}
License
The licenses of djangorestframework and djangorestframework-simplejwt projects have been included in this repository in the “licenses” directory.
MIT License
Copyright (c) 2022 Benyamin Agha Ebrahimi
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file simplejwt_multisessions-1.4.tar.gz.
File metadata
- Download URL: simplejwt_multisessions-1.4.tar.gz
- Upload date:
- Size: 24.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.10.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d3bf965205a924176dad91cd2d597bc9b2c5cd987a12a1cb0d88d6746026212
|
|
| MD5 |
73e79aa0a1b06631b4317dc799661bb8
|
|
| BLAKE2b-256 |
cb7eb2bcf3da66764fb50b9f3fb5efc04756b6815e8917d57145b14dc3fdf1b1
|
File details
Details for the file simplejwt_multisessions-1.4-py3-none-any.whl.
File metadata
- Download URL: simplejwt_multisessions-1.4-py3-none-any.whl
- Upload date:
- Size: 20.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.10.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8cf2a997377d3cde963ece1f8d111c5fc69a3330cf583cf56242faec2979f81
|
|
| MD5 |
a8db4c97c1800533d2956c2bb3a2f567
|
|
| BLAKE2b-256 |
b6c8fccb2f0b01cae3ac8c239d7d34a9c57a882ad50856922d4d8dc353515ef2
|