Openstack ^^^^^^^^^ Openstack leverages Token Authentication and we can use hooks to drastically simplify this as compared to the previous :ref:`v103 Openstack Example `. The following are sample request/response payloads from an Openstack server. We will use this information to build out the hooks needed. The following shows an example of making a request to the Openstack token server and the response: .. code-block:: shell curl -i \ -H "Content-Type: application/json" \ -d ' { "auth": { "identity": { "methods": ["password"], "password": { "user": { "name": "admin", "domain": { "id": "default" }, "password": "adminpwd" } } } } }' \ "http://localhost:5000/v3/auth/tokens" ; echo Example response: .. code-block:: http HTTP/1.1 201 Created X-Subject-Token: MIIFvgY... Vary: X-Auth-Token Content-Type: application/json Content-Length: 312 Date: Fri, 11 May 2018 03:15:01 GMT { "token": { "issued_at": "2018-05-11T03:15:01.000000Z", "audit_ids": [ "0PKh_BDKTWqqaFONE-Sxbg" ], "methods": [ "password" ], "expires_at": "2018-05-11T04:15:01.000000Z", "user": { "password_expires_at": null, "domain": { "id": "default", "name": "Default" }, "id": "9a7e43333cc44ef4b988f05fc3d3a49d", "name": "admin" } } } The payload is significantly different then what the out of the box Token Authenticator supports so based on this, the first hook needed will be the ``get_token_request_args`` since it can be used to format the payload as above. This function can modify ANY parameter in ``requests.Session.request`` and in this case the payload needs to be set as above. Since the payload is json the parameter we will need to modify is the ``json`` parameter. It should be noted that setting the ``json`` parameter causes the request to automatically insert the ``Content-Type: application/json`` header which is needed as shown above. Thus, all that is needed is to return the payload that to requests. In this case only the credential parameter is required as the username and password from the credential are required (Refer to :ref:`Credential Fields` for all the available fields). Any other parameters defined in `requests.Session.request `_ can be modified. For example ``payload={“method”: “post”,”headers” : { “Content-Type”: “Application json”}, “json”: {…}}`` can be used to set the method and include additional headers if they were needed. The next problem is that the token response is NOT in the payload but rather in the header. Additionally, the expiry information is a fixed date and thus that will need to be converted into seconds. Therefore, the ``modify_response`` hook will be needed to retrieve the token from the header and to calculate the token expiry. There are two input parameters needed for this case the response and the ``refresh_info``. The response is a `requests.response `_ as described in the link. The ``refresh_info`` includes the type of refresh and the value for the time to live between token refreshes. That is ``refresh_info.type`` indicates either “tokenAuthRefreshStatic” or “tokenAuthRefreshDynamic”. This info comes from the credential. The ``refresh_info.info.ttl`` is also from the credential if static refresh was selected. Otherwise it will default to 3600. The code puts the expiry timer handling in a try block such that even if an error occurs the token refresh will be set to the value in the ``refresh_info.info.ttl`` which has a default value of 3600 seconds. Finally, the token itself is received in the ``X-Subject-Token`` header and thus needs to be retrieved from the ``response.headers`` as shown in the code. The system then continues and since the default behavior for the Token Authenticator is that the token received is then used to make subsequent calls with the token being included in a header whose definition is defined in the credential. Since this behaviour is needed for openstack as long as the ``Authorization Header`` field is set to ``X-Auth-Token`` then no further hooks are required. **Complete Snippet Code** .. code-block:: python from silo.apps.errors import error_manager with error_manager(self): from silo.low_code import * from silo.apps.collection import create_collections, save_collections # ===================================== # =========== User Editable =========== # ===================================== # List any custom substitutions that need to occur within the snippet arguments custom_substitution = {} from silo.auth import create_token_authenticator from silo.auth.base_token import * from datetime import datetime, timezone def get_token_request_args(credential): payload={ "method" : "post", "json": { "auth": { "identity": { "methods": ["password"], "password": { "user": { "name": credential.fields["username"], "domain": { "id": "default" }, "password": credential.fields["password"] } } } } } } return(payload) def process_open_stack_response(response,refresh_info): try: if refresh_info.type=="tokenAuthRefreshDynamic": exp_time=response.json()['token']['expires_at'] exp_time = exp_time.replace('Z', '') exp_time = datetime.strptime(exp_time, '%Y-%m-%dT%H:%M:%S.%f') exp_time = exp_time.replace(tzinfo=timezone.utc) current_time = datetime.now(timezone.utc) seconds= int((exp_time-current_time).total_seconds()) else: seconds=refresh_info.info.ttl except: seconds=refresh_info.info.ttl return token_response_fields( expires_in=seconds, token=response.headers["X-Subject-Token"], ) Openstack = create_token_authenticator( name="Openstack", description="Openstack Authenticator.", get_token_request_args= get_token_request_args, process_response=process_open_stack_response ) # ===================================== # ========= End User Editable ========= # ===================================== collections = create_collections(self) snippet_framework(collections, custom_substitution, snippet_id, app=self) save_collections(collections, self) **Credential used for Openstack** .. image:: ../../../../_static/authentication/images/openstack-cred.png