Authenticators
Unfortunately, there is no one standard that defines how authentication nor do vendors fully implement the current standards for authentication. Therefore the Snippet Framework supports the ability to customize your authentication flow. As previously mentioned there are five out-of-the-box authenticators that can be leveraged. These authenticators are described under the REST Authentication section.
When building a custom authenticator it is recommended to start with one of the above authenticators and modify the code accordingly.
Authenticator ABC
All the authenticators must inherit from the same ABC (Abstract Base Class) and thus
all have similar structure. Below is an implementation of an Authenticator
class that
provides no authentication at all. This shows all the required methods that must be defined
in a brand new authenticator.
1from silo.auth import add_auth, Authenticator
2
3@add_auth
4class AuthNone(Authenticator):
5 """AuthNone encapsulates the authentication lifecycle for no authentication.
6 :param Authenticator Authenticator: Parent ABC Class
7 :param silo.apps.collection.credentials.Credential credentials: credentials
8 retrieved from em7/UCF
9 """
10
11 def __init__(self, credentials, **kwargs):
12 """Constructor method"""
13 Authenticator.__init__(self, credentials)
14
15 def check_if_authenticated(self):
16 """
17 Required for ABC implementation. Since there is nothing to authenticate against
18 we will always return True.
19 :return: True
20 :rtype: boolean
21 """
22 return True
23
24 def check_result(self, result):
25 """
26 Provides a check on the returned response from the REST request that was made.
27 :param response result: Response object after making a REST call
28 :return: True
29 :rtype: boolean
30 """
31 return True
32
33 def authenticate(self):
34 """
35 Authenticate is a no-op for NoneAuth
36 :return: True
37 :rtype: boolean
38 """
39 return True
40
41 def modify_request(self, res):
42 """Function to apply authorization implementation for request session
43 :param session session: Request to be updated with credentials
44 :return: True
45 :rtype: boolean
46 """
47 res.auth = None
48 return True
49
50 @staticmethod
51 def build(credentials, **kwargs):
52 """Build method to be called by get_authmethod in low_code"""
53 return AuthNone(credentials, **kwargs)
54
55 @staticmethod
56 def get_name():
57 return "AuthNone"
58
59 @staticmethod
60 def get_desc():
61 return "No Authentication for HTTP"
The most important functions to understand are as follows:
authenticate()
– This function is called before making any API request. Its job is to make sure that authentication has occurred. It is present in all authenticators and doesn’t really do anything in AuthBasic though becomes very important for the token based authentication schemes. In those cases its function is to make the API request to the token server and then stores that token for later use bymodify_request()
.modify_request()
- This function is called afterauthenticate()
has been performed and its job is to modify the session with the authentication information. This function has access to the session and thus can add the token to the headers e.g. in AuthToken. However, it can be easily modified e.g. to place the token in a query parameter.get_name()
- This function helps define the look up name for the authenticator. The returned value in this function will be the same as the credential’sAuthenticator Override
value.
Note
The out-of-the-box http step uses the Requests library. Request sessions
must be understood as the parameters of a session is what normally needs
to be modifed by modify_request()
. See Request Sessions
Logging
The Authenticator provides a logger with which to debug authentication issues. This logger provides credential masking if used in conjunction with the authenticator’s credential filter object.
from silo.auth import auth_logger
In order for these debug logs to be visible, the aligned credential must have its
Logging Debug
value set to True
.
Warning
Turning on debug runs the risk of recording a secret to the logs.
Credential Masking/Filtering
Credentials can be masked or filter from the auth_logger
output. Suppose
we had a AuthBasic
whose password was lowcodepassword. When logging is
enabled then our logs that would have had lowcodepassword is replaced with
*****
. Again, the secret is replaced with five asterix.
from silo.auth import add_auth, Authenticator
@add_auth
class AuthExampleCredFilter(Authenticator):
def __init__(self, credentials, **kwargs):
"""Constructor method"""
Authenticator.__init__(self, credentials)
self.cred_filter.update_secret("secret", "lowcodepassword")
def update_secret(self, key, secret):
"""Function to add/modify an additional secret for log removal
This function will transform the credentials into unicode
and store its entries as secret items in self._secrets.
:param str key: key for secret into dict
:param str secret: actual secret text to remove from log
"""
Note
The key parameter is used for subsequent updates to a secret that must be masked. For example, a token that will be refreshed will need to be updated and the previous secret will no longer need to be masked.
Authenticator Override
Before we dive into developing new or augmenting an existing authenticator, we need to understand how to reuse the Low-code tools: rest v101 credential type in order to point to a different authenticator while stilling being able to re-use existing credential fields.
Below is an image of the Low-code tools: rest v100 credential type. On the
bottom right of the image, is a field called Authenticator Override
.
The contents of this field must match the output of the get_name()
function. Below is a shortened example of an authenticator. Its name is
AuthCustom
. If we wanted to use this new authenticator then we would
need to update the credential field, Authenticator Override
, to be
AuthCustom
.
@add_auth
class AuthCustom(Authenticator):
# There are more methods not shown
@staticmethod
def get_name():
return "AuthCustom"
In the following examples, after we create our authenticator we will need to
update the Authenticator Override
field to match our get_name()
value.
Credential Fields
When using Authenticator Override, your custom authenticator has access to the following fields from the credentials.fields dictionary. When creating a custom authenticator, we will be reusing these fields to implement new authentication flows.
Note
Not all fields are view-able/visible from the Credential UI. In the table below, Visibility (AuthType) denotes when a field will be visible given an Authentication Type.
Display Name |
credentials.fields Name |
Visibility (AuthType) |
Type |
Default Value |
Description |
---|---|---|---|---|---|
Authentication Type |
|
All |
string |
|
Fixed string defining the authenticator to use i.e.
|
URL |
|
All |
string |
“” |
URL that should be used to retrieve device data [http(s)://Host:Port/Path] |
Token Retrieval Endpoint |
|
|
string |
“” |
Fields indicates that the token should be retrieved from a different API |
Authorization Header |
|
|
string |
|
Where the authentication server expects the key/token from the request header. |
Token Key |
|
|
string |
|
Key value in the JSON device response that contains the token
value to extract. Example: |
Bearer Token Format |
|
|
string |
|
Formulate how the bearer token should be configured and sent.
The typical format is |
Username |
|
|
string |
“” |
Username for authentication |
Password |
|
|
string |
“” |
Password for authentication |
Proxy Settings |
|
All |
boolean |
|
Toggle-able that allow users to input proxy settings |
Proxy Hostname/IP |
|
All |
string |
“” |
hostname/IP of your proxy server (optional) |
Proxy Port |
|
All |
number |
“” |
Port used by your proxy server |
Proxy User |
|
All |
string |
“” |
Proxy user (optional) |
Proxy Password |
|
All |
string |
“” |
Proxy password (optional) |
HTTP Header 1 |
|
|
string |
“” |
Optional HTTP header (e.g. |
HTTP Header 2 |
|
|
string |
“” |
Optional HTTP header (e.g. |
HTTP Headers (JSON input) |
|
All |
string |
“” |
Accepts additional header values in the form of JSON.
Example: |
SSL Peer Verify |
|
All |
boolean |
True |
This option determines whether the authenticity of the peer’s certificate should be verified. False means to have an insecure connection. |
CA Path |
|
All |
string |
“” |
This option specifies the Certificate Authority certificate path to use when making HTTPS requests. |
Authentication Failure Retry Time (seconds) |
|
|
number |
60 |
Number of seconds before retrying a failed authentication request. |
Additional Options |
|
|
string |
“” |
Additional Options (switch that creates additional options field that accepts JSON). |
Logging Debug |
|
All |
boolean |
|
Enables the logging authentication debug output |
Token Refresh Implementation |
|
|
string |
|
Refresh algorithm choice. Options |
Token TTL (seconds) |
|
|
number |
3600 |
TTL of the token (in seconds) before auto-refreshing
for |
Expiry Time Key |
|
|
string |
|
The key for the expiry time within the JSON response |
Client ID |
|
|
string |
“” |
Your application’s Client ID |
Client Secret |
|
|
string |
“” |
Your application’s Client Secret |
Access Token URL |
|
|
string |
“” |
URL for generating the Oauth2 token |
Request Header |
|
|
string |
|
Header utilized when making requests with the token |
Token Format |
|
|
string |
|
Formulate how the bearer token should be configured
and sent. The typical format is |
Response Token Key |
|
|
string |
|
The key that contains the token within the JSON response |
Oauth2 Grant Type |
|
|
string |
|
Supported authentication methods for Oauth2 i.e. |
Client auth method |
|
|
string |
|
How to send ClientID and ClientSecret i.e. |
Token Scope |
|
|
string |
“” |
Specify scope for client credential grant type |
Resource Owner Username |
|
|
string |
“” |
Resource owner username for requesting the token |
Resource Owner Password |
|
|
string |
“” |
Resource owner password for requesting the token |
Token Scopes |
|
|
string |
“” |
Specify all required scopes for the request. Refer to your application when specifying multiple scopes. |
Additional body Parameters |
|
|
string |
“” |
Any non-standard body parameters that need to be
specified for the request. You must enter the params
in JSON format as a dictionary.
Example: |
Prefix |
|
|
string |
“” |
Prefix to add to the API Key |
Suffix |
|
|
string |
“” |
Suffix to add to the API Key |
API Key |
|
|
string |
“” |
API Key to use for authentication |
Authenticator Override |
|
All |
string |
“” |
This is only used when a custom authenticator is leveraging this credential type. Enter the name of the custom authenticator. |