{ "info": { "author": "4teamwork AG", "author_email": "mailto:info@4teamwork.ch", "bugtrack_url": null, "classifiers": [ "Environment :: Web Environment", "Framework :: Plone", "Framework :: Plone :: 4.3", "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License (GPL)", "Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "ftw.tokenauth\n=============\n\nPAS plugin that facilitates **machine-to-machine authentication** by\nimplementing a two legged OAuth2 flow using service keys and short-lived\naccess tokens.\n\nInstallation\n============\n\n- Add ``ftw.tokenauth`` to your buildout configuration or as a dependency\n of your policy package:\n\n.. code:: ini\n\n [instance]\n eggs +=\n ftw.tokenauth\n\n- Install the generic setup profile of ``ftw.tokenauth``.\n\n\nConfiguration\n=============\n\nFor a user to be allowed to issue (or otherwise manage) service keys, they\nrequire the ``ftw.tokenauth: Manage own Service Keys`` permission. So\nintegration packages need to assign this permission to roles that should be\nallowed to use service keys.\n\n\nAuthentication flow\n===================\n\nThe authentication flow involves four steps:\n\n1. A logged in service user issues a service key in Plone, and stores the\n private key in a safe location accessible to the client application.\n\n2. The client application uses the private key to create and sign a JWT\n authorization grant.\n\n3. The client application exchanges the JWT authorization grant for a\n short-lived access token at the ``@@oauth2-token`` endpoint.\n\n4. The client then uses this access token to authenticate requests to\n protected resources.\n\n\nAssuming the client is in possession of a service key, the flow looks like this:\n\n.. image:: https://github.com/4teamwork/ftw.tokenauth/raw/master/docs/authentication-flow.png\n\n..\n Image Source: https://drive.google.com/open?id=1F8C4QB57ALF705vx9xkTDIX8AqMCJ30v\n\n\n\nBasic Usage\n===========\n\nIn order to set up machine-to-machine authentication for a client, the\nfollowing steps need to be performed:\n\n1. Issue Service Key\n--------------------\n\nA user that has already authenticated to Plone using regular means, and has\nthe ``ftw.tokenauth: Manage own Service Keys`` permission, can issue service\nkeys for their account via the ``@@manage-service-keys`` view\n(``Manage Service Keys`` action in personal tools menu).\n\n.. image:: https://github.com/4teamwork/ftw.tokenauth/raw/master/docs/manage-service-keys.png\n\nThey need to issue a service key that is then displayed **exactly once** for\ndownload, and store the private key in a safe location accessible to the\nclient that will use it.\n\n.. image:: https://github.com/4teamwork/ftw.tokenauth/raw/master/docs/issue-service-key.png\n\n`IP range restrictions`_ may also be defined when issuing a key.\n\nTODO: Document Key revocation.\n\n2. Create and sign JWT authorization grant using service key\n------------------------------------------------------------\n\nIn order to request an access token, the client application then uses the\nprivate service key to create and sign a JWT.\n\nThe JWT needs to contain the following claims:\n\n==== ========================================================================\nName Description\n==== ========================================================================\niss Issuer - must be ``client_id`` from service key\naud Audience - must be ``token_uri`` from service key\nsub Subject - must be ``user_id`` from service key or an arbitrary userid of\n an existing user if the service key user is allowed to impersonate other\n users.\niat The time the assertion was issued, specified as seconds since\n 00:00:00 UTC, January 1, 1970.\nexp The expiration time of the assertion, specified as seconds since\n 00:00:00 UTC, January 1, 1970. This value has a maximum of 1 hour after\n the issued time.\n==== ========================================================================\n\nThe JWT then needs to be signed with the private key. The only supported\nsignature algorithm is ``RS256``.\n\n\nPython Example:\n\n.. code:: python\n\n import json\n import jwt\n import time\n\n # Load saved key from filesystem\n service_key = json.load(open('my_saved_key.json', 'rb'))\n\n private_key = service_key['private_key'].encode('utf-8')\n\n claim_set = {\n \"iss\": service_key['client_id'],\n \"sub\": service_key['user_id'],\n \"aud\": service_key['token_uri'],\n \"iat\": int(time.time()),\n \"exp\": int(time.time() + (60 * 60)),\n }\n grant = jwt.encode(claim_set, private_key, algorithm='RS256')\n\n\n3. Token request (exchange JWT grant for an access token)\n---------------------------------------------------------\n\nThe client then makes a token request to the ``token_uri`` with the JWT grant\nit created.\n\nThis request needs to be a ``POST`` request with\n``Content-Type: application/x-www-form-urlencoded`` and a request body that\ncontains the form encoded parameters.\n\nTwo parameters are required:\n\n=========== =================================================================\nName Description\n=========== =================================================================\ngrant_type Must always be ``urn:ietf:params:oauth:grant-type:jwt-bearer``\nassertion The JWT authorization grant\n=========== =================================================================\n\nThe token endpoint will then respond with a token response containing the\naccess token:\n\n.. code:: json\n\n {\n \"access_token\": \"\",\n \"expires_in\": 3600,\n \"token_type\": \"Bearer\"\n }\n\nThe response will be of ``Content-Type: application/json`` and contain a JSON\nencoded body.\n\nPython Example:\n\n.. code:: python\n\n import requests\n\n GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:jwt-bearer'\n\n payload = {'grant_type': GRANT_TYPE, 'assertion': grant}\n response = requests.post(service_key['token_uri'], data=payload)\n token = response.json()['access_token']\n\n\nTODO: Document error responses for token requests\n\n\n4. Use access token to authenticate requests\n--------------------------------------------\n\nThe client can then use the access token to authenticate requests. The token\nneeds to be sent in the HTTP ``Authorization`` header as a ``Bearer`` token.\n\nOnce the token expires, the client must create a JWT authorization grant again,\nand request a new access token.\n\nPython Example:\n\n.. code:: python\n\n with requests.Session() as session:\n session.headers.update({'Authorization': 'Bearer %s' % token})\n response = session.get('http://localhost:8080/Plone/')\n # ...\n\nIf the token used by the client is expired, the server will respond with an\nerror response:\n\n.. code:: json\n\n {\n \"error\": \"invalid_token\",\n \"error_description\": \"Access token expired\"\n }\n\nThe client should then sign another JWT authentication grant, request a new\ntoken, and re-dispatch the failed request with the original parameters, and\nthe new token.\n\n\nRecommended Client Implementation\n=================================\n\nThe recommended logic to implement on a client to repeatedly authenticate and\nobtain new access tokens looks something like this:\n\n.. image:: https://github.com/4teamwork/ftw.tokenauth/raw/master/docs/client-flow.png\n\n..\n Image Source: https://drive.google.com/open?id=1wVua7R5VQUxJKGL8dq1kGV4AjLgjGSXZ\n\n\nThe client should, instead of trying to predict access token expiration, just\nanticipate the case that authentication using an existing token will fail\n(because the token expired), and then perform the necessary steps to obtain\na new token.\n\nTo accomplish this, it is recommended to delegate all the requests a client\napplication wants to make to a class that expects an ``Access token expired``\nresponse as described above, and obtains a new token if necessary. The failed\nrequest that lead to the error response then needs to be re-dispatched with\nits original parameters, but then new token in the ``Authorization`` header.\n\nCare needs to be taken to **not** include an expired token (or any\n``Authorization`` header for that matter) with the requests to the token\nendpoint when obtaining a new token.\n\nAn example implementation in Python can be found in\n`docs/client-example.py `_.\n\n\nAdvanced use\n============\n\nThis section covers some more advanced settings and functionality of\n``ftw.tokenauth``.\n\nIP range restrictions\n---------------------\n\nWhen issuing a key, IP range restrictions may be defined that limit from what\nsource IP address access tokens tied to this key may be used.\n\nChanges to IP range restrictions for a given key are effective immediately,\nand also affect already issued tokens tied to this key.\n\nIP ranges may be specified as a single IP address or as a network in\n`CIDR notation `_\nusing the slash-suffix.\n\nMultiple ranges may be provided in comma-separated form.\n\nExamples of valid IP range specifications:\n\n- ``192.168.1.1``\n- ``192.168.0.0/16``\n- ``192.168.1.1, 10.0.0.0/8``\n\nAuthentication attempts from an unauthorized source IP address are logged\nserver side, but not indicated to the client in any particular way -\nauthentication is simply not performed.\n\nImpersonation\n-------------\n\nImpersonation allows to authenticate as an arbitrary user instead of the user\nwho issued the service key. This is useful if e.g. an application needs to act\nin the context of different users.\n\nTo be able to impersonate another user the service key user needs the\npermission ``ftw.tokenauth: Impersonate user``. By default this permission is\ngranted to the ``Manager`` role only. Be aware that with this permission a user\nis allowed to impersonate users with higher privileges and thus in fact gets\nall the permissions of the highest privileged user in the system.\n\nTo impersonate a user pass his userid instead of the userid of the service\nkey user with the ``sub`` claim in the JWT token when requesting an access\ntoken.\n\nUsage logs\n----------\n\nIn the \"Manage Service Keys\" view, the last use of a key to issue access\ntokens is listed in the \"Last Used\" column. Clicking on this timestamp\ndisplays a detailed log of most recent uses of the key.\n\nBy default, these logs list the uses of the key in the last 7 days (the\nusage log retention period can be configured as a property on the PAS Plugin\nvia the ZMI).\n\nThe log entry with the most recent use of a key is always retained, while\nthe other log entries are cleaned out if they're expired (cleanup happens\nwhenever a any new access token is issued).\n\nThe logs don't show use of access tokens to authenticate, but instead they\nshow every instance where JWT authentication grants signed with this key\nwere used to obtain a new access token.\n\n\nLinks\n=====\n\n- Github: https://github.com/4teamwork/ftw.tokenauth\n- Issues: https://github.com/4teamwork/ftw.tokenauth/issues\n- Continuous integration: https://jenkins.4teamwork.ch/search?q=ftw.tokenauth\n\n\nCopyright\n=========\n\nThis package is copyright by `4teamwork `_.\n\n``ftw.tokenauth`` is licensed under GNU General Public License, version 2.\n\n\nChangelog\n=========\n\n\n1.1.0 (2018-07-12)\n------------------\n\n- Allow to impersonate another user.\n [buchi]\n\n\n1.0.1 (2018-04-16)\n------------------\n\n- Switch to `ipaddress` module instead of `py2-ipaddress` for IP range\n parsing, and fix unicode handling.\n [lgraf]\n\n\n1.0.0 (2018-04-04)\n------------------\n\n- Initial implementation\n [lgraf]", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/4teamwork/ftw.tokenauth", "keywords": "token authentication plone oauth2 jwt", "license": "GPL2", "maintainer": "", "maintainer_email": "", "name": "ftw.tokenauth", "package_url": "https://pypi.org/project/ftw.tokenauth/", "platform": "", "project_url": "https://pypi.org/project/ftw.tokenauth/", "project_urls": { "Homepage": "https://github.com/4teamwork/ftw.tokenauth" }, "release_url": "https://pypi.org/project/ftw.tokenauth/1.1.0/", "requires_dist": null, "requires_python": "", "summary": "Token Authentication for Plone", "version": "1.1.0" }, "last_serial": 5823473, "releases": { "1.1.0": [ { "comment_text": "", "digests": { "md5": "a05ac5114fab45c60bf21641da3ab4dd", "sha256": "380b2c706b7769582b87e419102b12ab8b421f0459e682955b3197dbda235328" }, "downloads": -1, "filename": "ftw.tokenauth-1.1.0.tar.gz", "has_sig": false, "md5_digest": "a05ac5114fab45c60bf21641da3ab4dd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 196994, "upload_time": "2018-07-12T08:56:41", "url": "https://files.pythonhosted.org/packages/80/3b/d9c3eb72825601a6d6ec42a60980dc4f3af39d06e3f61f32faf7ec6c6b2e/ftw.tokenauth-1.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "a05ac5114fab45c60bf21641da3ab4dd", "sha256": "380b2c706b7769582b87e419102b12ab8b421f0459e682955b3197dbda235328" }, "downloads": -1, "filename": "ftw.tokenauth-1.1.0.tar.gz", "has_sig": false, "md5_digest": "a05ac5114fab45c60bf21641da3ab4dd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 196994, "upload_time": "2018-07-12T08:56:41", "url": "https://files.pythonhosted.org/packages/80/3b/d9c3eb72825601a6d6ec42a60980dc4f3af39d06e3f61f32faf7ec6c6b2e/ftw.tokenauth-1.1.0.tar.gz" } ] }