{
"info": {
"author": "Wichert Akkerman",
"author_email": "wichert@wiggy.net",
"bugtrack_url": null,
"classifiers": [
"Intended Audience :: Developers",
"License :: DFSG approved",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Topic :: Software Development :: Libraries :: Python Modules"
],
"description": "JWT authentication for Pyramid\n==============================\n\nThis package implements an authentication policy for Pyramid that using `JSON\nWeb Tokens `_. This standard (`RFC 7519\n`_) is often used to secure backens APIs.\nThe excellent `PyJWT `_ library is\nused for the JWT encoding / decoding logic.\n\nEnabling JWT support in a Pyramid application is very simple:\n\n.. code-block:: python\n\n from pyramid.config import Configurator\n from pyramid.authorization import ACLAuthorizationPolicy\n\n def main():\n config = Configurator()\n # Pyramid requires an authorization policy to be active.\n config.set_authorization_policy(ACLAuthorizationPolicy())\n # Enable JWT authentication.\n config.include('pyramid_jwt')\n config.set_jwt_authentication_policy('secret')\n\nThis will set a JWT authentication policy using the `Authorization` HTTP header\nwith a `JWT` scheme to retrieve tokens. Using another HTTP header is trivial:\n\n.. code-block:: python\n\n config.set_jwt_authentication_policy('secret', http_header='X-My-Header')\n\nIf your application needs to decode tokens which contain an `Audience `_ claim you can extend this with:\n\n.. code-block:: python\n\n config.set_jwt_authentication_policy('secret', \n auth_type='Bearer',\n callback=add_role_principals,\n audience=\"example.org\")\n\n\nTo make creating valid tokens easier a new ``create_jwt_token`` method is\nadded to the request. You can use this in your view to create tokens. A simple\nauthentication view for a REST backend could look something like this:\n\n.. code-block:: python\n\n @view_config('login', request_method='POST', renderer='json')\n def login(request):\n login = request.POST['login']\n password = request.POST['password']\n user_id = authenticate(login, password) # You will need to implement this.\n if user_id:\n return {\n 'result': 'ok',\n 'token': request.create_jwt_token(user_id)\n }\n else:\n return {\n 'result': 'error'\n }\n\nSince JWT is typically used via HTTP headers and does not use cookies the\nstandard ``remember()`` and ``forget()`` functions from Pyramid are not useful.\nTrying to use them while JWT authentication is enabled will result in a warning.\n\n\nExtra claims\n------------\n\nNormally pyramid_jwt only makes a single JWT claim: the *subject* (or\n``sub`` claim) is set to the principal. You can also add extra claims to the\ntoken by passing keyword parameters to the ``create_jwt_token`` method.\n\n.. code-block:: python\n\n token = request.create_jwt_token(user.id,\n name=user.name,\n admin=(user.role == 'admin'))\n\n\nAll claims found in a JWT token can be accessed through the ``jwt_claims``\ndictionary property on a request. For the above example you can retrieve the\nname and admin-status for the user directly from the request:\n\n.. code-block:: python\n\n print('User id: %d' % request.authenticated_userid)\n print('Users name: %s', request.jwt_claims['name'])\n if request.jwt_claims['admin']:\n print('This user is an admin!')\n\nKeep in mind that data ``jwt_claims`` only reflects the claims from a JWT\ntoken and do not check if the user is valid: the callback configured for the\nauthentication policy is *not* checked. For this reason you should always use\n``request.authenticated_userid`` instead of ``request.jwt_claims['sub']``.\n\nYou can also use extra claims to manage extra principals for users. For example\nyou could claims to represent add group membership or roles for a user. This\nrequires two steps: first add the extra claims to the JWT token as shown above,\nand then use the authentication policy's callback hook to turn the extra claim\ninto principals. Here is a quick example:\n\n.. code-block:: python\n\n def add_role_principals(userid, request):\n return ['role:%s' % role for role in request.jwt_claims.get('roles', [])]\n\n config.set_jwt_authentication_policy(callback=add_role_principals)\n\n\nYou can then use the role principals in an ACL:\n\n.. code-block:: python\n\n class MyView:\n __acl__ = [\n (Allow, Everyone, ['read']),\n (Allow, 'role:admin', ['create', 'update']),\n ]\n\nValidation Example\n------------------\n\nAfter creating and returning the token through your API with\n``create_jwt_token`` you can test by issuing an HTTP authorization header type\nfor JWT.\n\n.. code-block:: text\n\n GET /resource HTTP/1.1\n Host: server.example.com\n Authorization: JWT eyJhbGciOiJIUzI1NiIXVCJ9...TJVA95OrM7E20RMHrHDcEfxjoYZgeFONFh7HgQ\n\nWe can test using curl.\n\n.. code-block:: bash\n\n curl --header 'Authorization: JWT TOKEN' server.example.com/ROUTE_PATH\n\n.. code-block:: python\n\n config.add_route('example', '/ROUTE_PATH')\n @view_config(route_name=example)\n def some_action(request):\n if request.authenticated_userid:\n # Do something\n\n\nSettings\n--------\n\nThere are a number of flags that specify how tokens are created and verified.\nYou can either set this in your .ini-file, or pass/override them directly to the\n``config.set_jwt_authentication_policy()`` function.\n\n+--------------+-----------------+---------------+--------------------------------------------+\n| Parameter | ini-file entry | Default | Description |\n+==============+=================+===============+============================================+\n| private_key | jwt.private_key | | Key used to hash or sign tokens. |\n+--------------+-----------------+---------------+--------------------------------------------+\n| public_key | jwt.public_key | | Key used to verify token signatures. Only |\n| | | | used with assymetric algorithms. |\n+--------------+-----------------+---------------+--------------------------------------------+\n| algorithm | jwt.algorithm | HS512 | Hash or encryption algorithm |\n+--------------+-----------------+---------------+--------------------------------------------+\n| expiration | jwt.expiration | | Number of seconds (or a datetime.timedelta |\n| | | | instance) before a token expires. |\n+--------------+-----------------+---------------+--------------------------------------------+\n| audience | jwt.audience | | Proposed audience for the token |\n+--------------+-----------------+---------------+--------------------------------------------+\n| leeway | jwt.leeway | 0 | Number of seconds a token is allowed to be |\n| | | | expired before it is rejected. |\n+--------------+-----------------+---------------+--------------------------------------------+\n| http_header | jwt.http_header | Authorization | HTTP header used for tokens |\n+--------------+-----------------+---------------+--------------------------------------------+\n| auth_type | jwt.auth_type | JWT | Authentication type used in Authorization |\n| | | | header. Unused for other HTTP headers. |\n+--------------+-----------------+---------------+--------------------------------------------+\n| json_encoder | | None | A subclass of JSONEncoder to be used |\n| | | | to encode principal and claims infos. |\n+--------------+-----------------+---------------+--------------------------------------------+\n\nPyramid JWT example use cases\n=============================\n\nThis is a basic guide (that will assume for all following statements that you\nhave followed the Readme for this project) that will explain how (and why) to\nuse JWT to secure/restrict access to a pyramid REST style backend API, this\nguide will explain a basic overview on:\n\n- Creating JWT's\n- Decoding JWT's\n- Restricting access to certain pyramid views via JWT's\n\n\nCreating JWT's\n--------------\n\nFirst off, lets start with the first view in our pyramid project, this would\nnormally be say a login view, this view has no permissions associated with it,\nany user can access and post login credentials to it, for example:\n\n.. code-block:: python\n\n def authenticate_user(login, password):\n # Note the below will not work, its just an example of returning a user\n # object back to the JWT creation.\n login_query = session.query(User).\\\n filter(User.login == login).\\\n filter(User.password == password).first()\n\n if login_query:\n user_dict = {\n 'userid': login_query.id, \n 'user_name': login_query.user_name,\n 'roles': login_query.roles\n }\n # An example of login_query.roles would be a list\n # print(login_query.roles)\n # ['admin', 'reports']\n return user_dict\n else:\n # If we end up here, no logins have been found\n return None\n\n @view_config('login', request_method='POST', renderer='json')\n def login(request):\n '''Create a login view\n '''\n login = request.POST['login']\n password = request.POST['password']\n user = authenticate(login, password)\n if user:\n return {\n 'result': 'ok',\n 'token': request.create_jwt_token(\n user['userid'], \n roles=user['roles'],\n userName=user['user_name']\n )\n }\n else:\n return {\n 'result': 'error',\n 'token': None\n }\n\nNow what this does is return your JWT back to whatever front end application\nyou may have, with the user details, along with their permissions, this will\nreturn a decoded token such as:\n\n.. code-block::\n\n eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyTmFtZSI6Imx1a2UiLCJyb2xlcyI6WyJhZG1pbiIsInJlcG9ydHMiXSwic3ViIjo0LCJpYXQiOjE1MTkwNDQyNzB9.__KjyW1U-tpAEvTbSJsasS-8CaFyXH784joUPONH6hQ\n\nNow I would suggest heading over to `JWT.io `_, copy this data\ninto their page, and you will see the decoded token:\n\n.. code-block:: json\n\n {\n \"userName\": \"luke\",\n \"roles\": [\n \"admin\",\n \"reports\"\n ],\n \"sub\": 4,\n \"iat\": 1519044270\n }\n\nNote, at the bottom of jwt.io's webpage, that the signature shows verified, if\nyou change the \"secret\" at the bottom, it will say \"NOT Verified\" this is\nbecause in order for any JWT process to be verified, the valid \"secret\" or\n\"private key\" must be used. It is important to note that any data sent in a JWT\nis accessible and readable by anyone.\n\nDecoding JWT\n------------\n\nThe following section would also work if pyramid did not create the JWT, all it\nneeds to know to decode a JWT is the \"secret\" or \"private key\" used to\ncreate/sign the original JWT.By their nature JWT's aren't secure, but they can\nbe used \"to secure\". In our example above, we returned the \"roles\" array in our\nJWT, this had two properties \"admin\" and \"reports\" so we could then in our\npyramid application, setup an ACL to map JWT permissions to pyramid based\nsecurity, for example in our projects __init__.py we could add:\n\n.. code-block:: python\n\n from pyramid.security import ALL_PERMISSIONS\n\n class RootACL(object):\n __acl__ = [\n (Allow, 'admin', ALL_PERMISSIONS),\n (Allow, 'reports', ['reports'])\n ]\n\n def __init__(self, request):\n pass\n\nWhat this ACL will do is allow anyone with the \"admin\" role in their JWT access\nto all views protected via a permission, where as users with \"reports\" in their\nJWT will only have access to views protected via the \"reports\" permission.\n\nNow this ACL in itself is not enough to map the JWT permission to pyramids\nsecurity backend, we need to also add the following to __init__.py:\n\n.. code-block:: python\n\n from pyramid.authorization import ACLAuthorizationPolicy\n\n\n def add_role_principals(userid, request):\n return request.jwt_claims.get('roles', [])\n\n def main(global_config, **settings):\n \"\"\" This function returns a Pyramid WSGI application.\n \"\"\"\n config = Configurator(settings=settings)\n ...\n # Enable JWT - JSON Web Token based authentication\n config.set_root_factory(RootACL)\n config.set_authorization_policy(ACLAuthorizationPolicy())\n config.include('pyramid_jwt')\n config.set_jwt_authentication_policy('myJWTsecretKeepThisSafe', \n auth_type='Bearer',\n callback=add_role_principals)\n\nThis code will map any properties of the \"roles\" attribute of the JWT, run them\nthrough the ACL and then tie them into pyramids security framework.\n\nHow is this secure?\n-------------------\n\nFor example, a JWT could easily be manipulated, anyone could hijack the token,\nchange the values of the \"roles\" array to gain access to a view they do not\nactually have access to. WRONG! pyramid_jwt checks the signature of all JWT\ntokens as part of the decode process, if it notices that the signature of the\ntoken is not as expected, it means either the application has been setup\ncorrectly with the wrong private key, OR an attacker has tried to manipulate\nthe token.\n\nSecuring views with JWT's\n-------------------------\n\nIn the example posted above we creating an \"admin\" role that we gave\nALL_PERMISSIONS access in our ACL, so any user with this role could access any\nview e.g.:\n\n.. code-block:: python\n\n @view_config(route_name='view_a', request_method='GET', \n permission=\"admin\", renderer='json')\n def view_a(request):\n return\n\n @view_config(route_name='view_b', request_method='GET', \n permission=\"cpanel\", renderer='json')\n def view_b(request):\n return\n\nThis user would be able to access both of these views, however any user with\nthe \"reports\" permission would not be able to access any of these views, they\ncould only access permissions with \"reports\". Obviously in our use case, one\nuser had both \"admin\" and \"reports\" permissions, so they would be able to\naccess any view regardless.\n\n\nChangelog\n=========\n\n1.4.1 - August 10, 2018\n-----------------------\n\n- `Pull request #23 `_:\n Allow specifying the audience in the app configuration, from `John Stevens II\n `_.\n\n\n1.4 - August 9, 2018\n--------------------\n\n- `Pull request #21 `_:\n add support for JWT aud claims, from `Luke Crooks\n `_.\n\n1.3 - March 20, 2018\n---------------------\n\n- `Issue #20 `_:\n Fix handling of public keys.\n- `Pull request #17 `_:\n a lot of documentation improvements from `Luke Crooks\n `_.\n\n\n1.2 - May 25, 2017\n------------------\n\n- Fix a `log.warn` deprecation warning on Python 3.6.\n\n- Documentation improvements, courtesy of `\u00c9ric Araujo `_\n and `Guillermo Cruz `_.\n\n- `Pull request #10 `_\n Allow use of a custom JSON encoder.\n Submitted by `Julien Meyer `_.\n\n\n1.1 - May 4, 2016\n-----------------\n\n- `Issue #2 `_:\n Support setting and reading extra claims in a JWT token.\n\n- `Pull request #4 `_:\n Fix parsing of expiration and leeway settings from a configuration value.\n Submitted by `Daniel Kraus `_.\n\n- `Pull request #3 `_:\n Allow overriding the expiration timestamp for a token when creating a new\n token. Submitted by `Daniel Kraus`_.\n\n\n1.0 - December 17, 2015\n-----------------------\n\n- First release\n\n\n",
"description_content_type": "",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://github.com/wichert/pyramid_jwt",
"keywords": "Pyramid JWT authentication security",
"license": "BSD",
"maintainer": "",
"maintainer_email": "",
"name": "pyramid_jwt",
"package_url": "https://pypi.org/project/pyramid_jwt/",
"platform": "",
"project_url": "https://pypi.org/project/pyramid_jwt/",
"project_urls": {
"Homepage": "https://github.com/wichert/pyramid_jwt"
},
"release_url": "https://pypi.org/project/pyramid_jwt/1.4.1/",
"requires_dist": [
"pyramid",
"PyJWT",
"pytest; extra == 'tests'",
"WebTest; extra == 'tests'"
],
"requires_python": "",
"summary": "JWT authentication policy for Pyramid",
"version": "1.4.1"
},
"last_serial": 4155803,
"releases": {
"1.0": [
{
"comment_text": "",
"digests": {
"md5": "8e1dafb07a6a7f8e24cbd63742115f77",
"sha256": "c071a9320e13e44c5cfa47a64032402877e95e7c36c048b158662b63d7382b4b"
},
"downloads": -1,
"filename": "pyramid_jwt-1.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "8e1dafb07a6a7f8e24cbd63742115f77",
"packagetype": "bdist_wheel",
"python_version": "3.5",
"requires_python": null,
"size": 7332,
"upload_time": "2015-12-17T09:36:10",
"url": "https://files.pythonhosted.org/packages/d6/80/878f9521ea9fda5a82320ef84f5c4e79c612780f1a947435b2ca9aee1265/pyramid_jwt-1.0-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "e80e834ac46c7cf547dc5ea5aa0322eb",
"sha256": "1e93e9660ae53ed1d700a06dab55872c64760ba7358b422ef99d9fbbf48650cc"
},
"downloads": -1,
"filename": "pyramid_jwt-1.0.tar.gz",
"has_sig": false,
"md5_digest": "e80e834ac46c7cf547dc5ea5aa0322eb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 5653,
"upload_time": "2015-12-17T09:35:59",
"url": "https://files.pythonhosted.org/packages/6a/2b/095d1559b2ac400df19073853e64a9e276ac0946c9bf207b9eba3d330994/pyramid_jwt-1.0.tar.gz"
}
],
"1.1": [
{
"comment_text": "",
"digests": {
"md5": "6d4a809016ff2786d6acad4ac1fc7b9e",
"sha256": "7172aa97258aadd828e4b04eb76977fd4d7384a743be34b01d2806e92c083fa3"
},
"downloads": -1,
"filename": "pyramid_jwt-1.1-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "6d4a809016ff2786d6acad4ac1fc7b9e",
"packagetype": "bdist_wheel",
"python_version": "3.5",
"requires_python": null,
"size": 8738,
"upload_time": "2016-05-04T10:01:37",
"url": "https://files.pythonhosted.org/packages/d0/e1/4d0e3d7836eace4c5f34b8aa14f1003e4c24ab913da537848ac12d731d0f/pyramid_jwt-1.1-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "d93eb0513f05cd52f59bacdca7a5efa3",
"sha256": "d0e2e4651a7f7f4ad093679613bc43baafd3e752a63dac3990003dd0e8f5a03d"
},
"downloads": -1,
"filename": "pyramid_jwt-1.1.tar.gz",
"has_sig": false,
"md5_digest": "d93eb0513f05cd52f59bacdca7a5efa3",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 6676,
"upload_time": "2016-05-04T10:01:30",
"url": "https://files.pythonhosted.org/packages/46/24/8585b7d6ccb12f22f26bc27b4ec09feba078752ffb9b1214b40f45e850cf/pyramid_jwt-1.1.tar.gz"
}
],
"1.2": [
{
"comment_text": "",
"digests": {
"md5": "43a678e2a83c0743e9c5e5c173ebf6f5",
"sha256": "6d6531e03b4bf1bb985240c9928ad9db1585c4e053ec1fde2ce6f282aebc8064"
},
"downloads": -1,
"filename": "pyramid_jwt-1.2-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "43a678e2a83c0743e9c5e5c173ebf6f5",
"packagetype": "bdist_wheel",
"python_version": "2.7",
"requires_python": null,
"size": 10101,
"upload_time": "2017-05-25T11:16:29",
"url": "https://files.pythonhosted.org/packages/89/62/b1d96aaca028a3135ae44dff31929d5e80d9594330f9c482e2de418bfb5a/pyramid_jwt-1.2-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "74af040fdc1d41b1ea3aa2d340b7a06d",
"sha256": "2a364257dd2785ca8d1ed10adc0aff66c864c366e8dc40a47f0d58c2a5b56e63"
},
"downloads": -1,
"filename": "pyramid_jwt-1.2.tar.gz",
"has_sig": false,
"md5_digest": "74af040fdc1d41b1ea3aa2d340b7a06d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 8859,
"upload_time": "2017-05-25T11:16:32",
"url": "https://files.pythonhosted.org/packages/89/93/b4f28123276af85cd1acfe8f0e7b40ab116e38e627ae4427e91775f840a6/pyramid_jwt-1.2.tar.gz"
}
],
"1.3": [
{
"comment_text": "",
"digests": {
"md5": "0d190865359ef1a799ebbef3568fb531",
"sha256": "e3b807f7e81819b7261781d0138dd8479e6f4812439343d596c7d0e2c90764b9"
},
"downloads": -1,
"filename": "pyramid_jwt-1.3-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "0d190865359ef1a799ebbef3568fb531",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 14755,
"upload_time": "2018-03-20T19:39:10",
"url": "https://files.pythonhosted.org/packages/36/f9/3d0eb513322e714d153cbfc3f627ac63a4f7fcbde6175c7393325290c172/pyramid_jwt-1.3-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "b410aa312a24d502e4337b6adad38082",
"sha256": "43c69a57770e92ecbe495488cab22bb699d20eb74678c8b183091c2601ad8849"
},
"downloads": -1,
"filename": "pyramid_jwt-1.3.tar.gz",
"has_sig": false,
"md5_digest": "b410aa312a24d502e4337b6adad38082",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 14453,
"upload_time": "2018-03-20T19:39:12",
"url": "https://files.pythonhosted.org/packages/e4/be/bd00dc1ae6e15de1d5de036f9e246fd268940ede6584e8aa61a1fcb5bda2/pyramid_jwt-1.3.tar.gz"
}
],
"1.4": [
{
"comment_text": "",
"digests": {
"md5": "631bc7d9f72cce9156ebce0e9a4511f5",
"sha256": "fd9eac1589e218235a05aa75779dd2d10f90af0e177abccf27e3abb33188a0f6"
},
"downloads": -1,
"filename": "pyramid_jwt-1.4-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "631bc7d9f72cce9156ebce0e9a4511f5",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 8857,
"upload_time": "2018-08-09T06:18:10",
"url": "https://files.pythonhosted.org/packages/30/91/da9b1f47595b028817aa6c4d3ac06bf6019c7d15c1365fe4bdb8717559e9/pyramid_jwt-1.4-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "04e5b9a5adac6e457a4a8676691f447b",
"sha256": "04beee99c600a58b6c5b8d04d9d74868375f3515ad3b9090edea44c89f44424c"
},
"downloads": -1,
"filename": "pyramid_jwt-1.4.tar.gz",
"has_sig": false,
"md5_digest": "04e5b9a5adac6e457a4a8676691f447b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 14138,
"upload_time": "2018-08-09T06:18:12",
"url": "https://files.pythonhosted.org/packages/5d/d5/9e98a2beeeaec89328cf0326d4e06f913a41df651356f050a2c497838ce1/pyramid_jwt-1.4.tar.gz"
}
],
"1.4.1": [
{
"comment_text": "",
"digests": {
"md5": "91cd138d3ea88a7a11a6b08b02556503",
"sha256": "260dfb72912485347af2b5b3b22fabd3b0b2c0e7f610ead660ab6a76f030cf5f"
},
"downloads": -1,
"filename": "pyramid_jwt-1.4.1-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "91cd138d3ea88a7a11a6b08b02556503",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 8940,
"upload_time": "2018-08-10T08:30:25",
"url": "https://files.pythonhosted.org/packages/91/10/363520d94b8265a95e27ff30b6ed254a86f421a7fb5520f6032cb45a83c4/pyramid_jwt-1.4.1-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "70bac5570281153ae3f82f685c2dcbc8",
"sha256": "ee21d0c4bc0cc9af2875930abb55c41615d473fee7bd83023bcb61aa36691612"
},
"downloads": -1,
"filename": "pyramid_jwt-1.4.1.tar.gz",
"has_sig": false,
"md5_digest": "70bac5570281153ae3f82f685c2dcbc8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 14299,
"upload_time": "2018-08-10T08:30:27",
"url": "https://files.pythonhosted.org/packages/c9/66/ef0e9a6fe510b28edd762ad15e89a05f2273b6d3624a952b97a3a86e27f4/pyramid_jwt-1.4.1.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "91cd138d3ea88a7a11a6b08b02556503",
"sha256": "260dfb72912485347af2b5b3b22fabd3b0b2c0e7f610ead660ab6a76f030cf5f"
},
"downloads": -1,
"filename": "pyramid_jwt-1.4.1-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "91cd138d3ea88a7a11a6b08b02556503",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 8940,
"upload_time": "2018-08-10T08:30:25",
"url": "https://files.pythonhosted.org/packages/91/10/363520d94b8265a95e27ff30b6ed254a86f421a7fb5520f6032cb45a83c4/pyramid_jwt-1.4.1-py2.py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "70bac5570281153ae3f82f685c2dcbc8",
"sha256": "ee21d0c4bc0cc9af2875930abb55c41615d473fee7bd83023bcb61aa36691612"
},
"downloads": -1,
"filename": "pyramid_jwt-1.4.1.tar.gz",
"has_sig": false,
"md5_digest": "70bac5570281153ae3f82f685c2dcbc8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 14299,
"upload_time": "2018-08-10T08:30:27",
"url": "https://files.pythonhosted.org/packages/c9/66/ef0e9a6fe510b28edd762ad15e89a05f2273b6d3624a952b97a3a86e27f4/pyramid_jwt-1.4.1.tar.gz"
}
]
}