ITENTIAL ADAPTERS TECHNICAL RESOURCE
Authentication
Authentication Overview
The main purpose of an adapter is to communicate with an external system to perform actions and then return the results of those actions back to Itential Automation Platform (IAP). In order to communicate with these external systems, adapters need to be able to authenticate with the external system. While there are some common ways to authenticate there is not one way, and systems can undergo changes to the norms (e.g., want the token in a different place).
Prior to configuring an adapter to “talk” to an external system, determine the following:
- Is a username and password required?
- Is it required on every call or just to retrieve a token?
- Are you authenticating based on system credentials?
- Do you need to dynamically login based on different user credentials?
- Is a token required to make calls?
- Are you able to use a static token that is always good?
- If the token expires, how long is the token valid for?
- If you are requesting a token, what is required (headers, body, etc.)?
- Do you need to dynamically login to environments/domains based on some criteria?
Common Authentication Configurations
Basic Authentication Method
Attributes of basic authentication are:
- A username and password are provided in every request.
- The username and password are then concatenated with a ”:” in the middle.
- This method will then base 64 encode the format and place it into a new string prefixed with ”Basic ”.
- The string is then placed into the Authorization header in the request.
Some variations of basic authentication include:
- The credentials can be provided in a different header field or even in a different place.
- The format of the authentication data may be different.
- You may have to use dynamic credentials instead of a system level credential.
How to Setup Basic Authentication in the Adapter
Basic authentication is handled entirely within the adapter properties in the authentication section.
- The auth_method should be set to ”basic user_password”.
- The actual username and password should be provided in their respective properties.
- The auth_field should be set to where the authentication information should be in the request.
- The auth_field_format is the format of the data that will be sent in the request. The adapter library will replace the variables it knows about:
- {username}
- {password}
- {b64}….{/b64} tells it to b64 encode everything between the tags.
SAMPLE PROPERTIES
"authentication": {
"auth_method": "basic user_password",
"username": "systemuser",
"password": "systempassword",
"token": "",
"token_timeout": 180000,
"token_cache": "local",
"invalid_token_error": 401,
"auth_field": "header.headers.Authorization",
"auth_field_format": "Basic {b64}{username}:{password}{/b64}"
},
Static Token
Static token is not very common but can be used, and there is a way for the adapter to support it.
Common Static Token
- A token is provided to login.
- The token does not expire, or it expires in long periods (e.g. every 3 months).
- The token can be placed into some part of the request.
- The token can be provided alone or as part of a string.
Variations of Common
- The token can be provided in a different header field or even in a different place.
- The format of the authentication data may be different.
- May have to use different static tokens instead of a system level token.
Static Token in Adapter
Static Token is handled entirely within the adapter properties in the authentication section.
- The auth_method should be set to ”static_token”.
- The actual token should be provided in the token properties.
- The auth_field should be set to where the authentication information should be in the request.
- The auth_field_format is the format of the data that will be sent in the request.
- The adapter library will replace the variables it knows about: {token}
SAMPLE PROPERTIES
"authentication": {
"auth_method": "static_token",
"username": "",
"password": "",
"token": "adsfhjdhsaflhljafhasdjlfh",
"token_timeout": 0,
"token_cache": "local",
"invalid_token_error": 499,
"auth_field": "header.headers.AUTH",
"auth_field_format": "Token {token}"
},
Other Options for Static Token
- Token in a different header field:
- Change the value of auth_field to header.headers.MyAuthField, etc.
- Token in a different location:
- To locate in the url, set auth_field to “url”.
- To locate in the body, set auth_field to “body.field”.
- Different format of auth data:
- Want to remove all text and just send the token – ”{token}”.
- Can also set the auth_field_format to “My encoded token {b64}{token}{/b64}”.
- Using different static tokens:
- Set the callProperties on each request in adapter.js
SAMPLE PROPERTIES
"authentication": {
"auth_method": "static_token",
"username": "",
"password": "",
"token": "adsfhjdhsaflhljafhasdjlfh",
"token_timeout": 0,
"token_cache": "local",
"invalid_token_error": 499,
"auth_field": "header.headers.MyAuthField",
"auth_field_format": "My encoded token {b64}{token}{/b64}"
},
ADAPTER.JS
const reqObj = {
callProperties: {
authentication: {
token:<dynamicToken>
}
}
};
Two Step Token
The two step token process is one of the more common methods for API authentication and has many different varieties, which are discussed below.
Common Uses in Two Step Token Authentication
The typical configuration for the Two Step Token authentication method is:
- A username and password are provided in the body of an initial token request.
- If the user is authenticated, the system will return a token in the response body.
- The token will then be used in subsequent requests to the system by placing it into the X-AUTH-TOKEN header in the request.
Variations in Using a Two Step Token
The token can be provided in a different header field or even in a different place.
- The format of the authentication data may be different.
- You may have to use dynamic credentials instead of a system level credential
- The token will timeout at different times.
How to Use a Two Step Token in the Adapter
Two Step Token is partly handled in both the adapter properties and the .system entity.
- The auth_method should be set to ”request_token”.
- Since username and password is used on the first request to get a token, the actual username and password should be provided in their respective properties.
- The auth_field should be set to where the token will be placed in future request. This is not where info is placed on the initial token request.
- The auth_field_format is the format of the data that will be sent in future requests. The adapter library will replace the variables it knows about:
- {token}
- You can specify how long the token is valid in milliseconds in the token_timeout field. This helps to prevent having to use a two-step process on every call.
- You can specify where to store the token_cache, but only use local for now.
- Invalid_token_error tells the adapter the error to expect when the token is not valid. When this happens, the adapter attempts to pull a new token and will then reattempt the request.
SAMPLE PROPERTIES
"authentication": {
"auth_method": "request_token",
"username": "systemuser",
"password": "systempassword",
"token": "",
"token_timeout": 180000,
"token_cache": "local",
"invalid_token_error": 401,
"auth_field": "header.headers.X-AUTH-TOKEN",
"auth_field_format": "{token}"
},
Two Step Token in the .system entity
Here are some other key pieces of information to know when the two step token is handled in the .system entity:
- Action File – The action file tells the Adapter Library how it is going to get the token. The key information here is:
- entitypath – The path to use for the call to get the token.
- requestSchema – The schema to use to prepare the data for the external system.
- responseSchema – The schema to use on the response to extract the token.
ACTION.JSON
{
"name": "getToken",
"protocol": "REST",
"method": "POST",
"entitypath": "/api/{version}/authentication/signin",
"requestSchema": "schemaTokenReq.json",
"responseSchema": "schemaTokenResp.json",
"timeout": 0,
"sendEmpty": false,
"requestDataType": "JSON",
"responseDataType": "JSON",
"headers": {},
"responseObjects": [
{
"type": "default",
"key": "",
"mockFile": "mockdatafiles/gettoken.json"
}
]
},
Two Step Token in the .system entity
Here are some other key pieces of information to know when the two step token is handled in the .system entity:
- Action File – The action file tells the Adapter Library how it is going to get the token. The key information here is:
- entitypath – The path to use for the call to get the token.
- requestSchema – The schema to use to prepare the data for the external system.
- responseSchema – The schema to use on the response to extract the token.
SAMPLE REQUEST SCHEMA
{
"$id": "reqTokenSchema.json",
"type": "object",
"schema": "http://json-schema.org/draft-07/schema#",
"translate": true,
"dynamicfields": true,
"properties": {
"ph_request_type": {
"type": "string",
"description": "type of request(internal to adapter)",
"default": "getToken",
"enum": [
"getToken",
"healthcheck"
],
"external_name": "ph_request_type"
},
"username": {
"type": "string",
"description": "username to login with",
"external_name": "user_name"
},
"password": {
"type": "string",
"decription": "password to login with",
"external_name": "passwd"
}
},
"required": ["username","password"],
"definitions": {}
}
Sample Request
{
"user_name": "systemuser",
"passwd": "systempassword"
}
- Response Schema:
- Need to tell the adapter library where in the response it will find the token.
SAMPLE RESPONSE SCHEMA
{
"$id": "respTokenSchema.json",
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"translate": true,
"properties": {
"ph_request_type": {
"type": "string",
"description": "type of request(internal to adapter)",
"default": "getToken",
"enum": [
"getToken"
],
"external_name": "ph_request_type"
},
"token": {
"type": "string",
"description": "the token returned from system",
"external_name": "access_token"
}
},
"definitions": {}
}
Sample Response
{
"access_token": "slfadhslfdjshfkashfljhsadf"
}
Other Two Step Token Options
Some other options for using a two step token include:
- Token in a different header field:
- Change the value of auth_field: header.headers.MyAuthField, etc.
- Token in a different location:
- Set auth_field to “url” to locate the token in the url.
- Set auth_field to “body.field” to locate the token in the body.
- Different format for authenticated data:
- Want to remove all text and just send the token, use ”{token}”
- Can also set the auth_field_format to “My encoded token {b64}{token}{/b64}”
- To use a dynamic username and password:
- Set the callProperties on each request in adapter.js
- To have the token time out after 10m, use (600s -> 600000ms)
SAMPLE PROPERTIES
"authentication": {
"auth_method": "request_token",
"username": "request_token",
"password": "systempassword",
"token": "",
"token_timeout": 60000,
"token_cache": "local",
"invalid_token_error": 401,
"auth_field": "header.headers.MyAuthField",
"auth_field_format": "My encoded token {b64}{token}{/b64}"
},
ADAPTER.JS
const reqObj = {
callProperties: {
authentication: {
username: <dynamicUser>,
password: <dynamicPassword>
}
}
};
More Variations When Using a Two Step Token
- Can change the fields where the username and password are in the request by changing the request schema.
- Instead of user_name the value of the username property will be in a field called user.
- Instead of passwd the value of the password property will be in a field called pwd.
- Can always change the field where the token can be found in the response
- Instead of access_token, the token is in a field called Token.
SAMPLE RESPONSE SCHEMA
{
"$id": "respTokenSchema.json",
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"translate": true,
"properties": {
"ph_request_type": {
"type": "string",
"description": "type of request(internal to adapter)",
"default": "getToken",
"enum": [
"getToken"
],
"external_name": "ph_request_type"
},
"token": {
"type": "string",
"description": "the token returned from system",
"external_name": "Token"
}
},
"definitions": {}
}
Sample Response
{
"Token": "slfadhslfdjshfkashfljhsadf"
}
SAMPLE REQUEST SCHEMA
{
"$id": "reqTokenSchema.json",
"type": "object",
"schema": "http://json-schema.org/draft-07/schema#",
"translate": true,
"dynamicfields": true,
"properties": {
"ph_request_type": {
"type": "string",
"description": "type of request(internal to adapter)",
"default": "getToken",
"enum": [
"getToken",
"healthcheck"
],
"external_name": "ph_request_type"
},
"username": {
"type": "string",
"description": "username to login with",
"external_name": "user"
},
"password": {
"type": "string",
"decription": "password to login with",
"external_name": "pwd"
}
},
"required": ["username","password"],
"definitions": {}
}
Sample Request
{
"user": "systemuser",
"pwd": "systempassword"
}
More Authentication Examples
There are different types of authentication, many of them can be handled by the adapter by changing:
- authentication properties
- .system entity – action.json
- .system entity – schemas
- callProperties in the reqObj within adapter.js
Below are some more complex examples of authentication that still use a two-step authentication where a token is received and used in subsequent steps. Some of these may be more common than others but we are providing them to show how you can make modifications to support different ways to authenticate.
Of note, there are still some forms of authentication that are not currently supported. As these methods are implemented, changes are made to support them. In the meantime, if you experience any problems after making changes to the above areas, don’t hesitate to contact the Itential Adapters Team – we’re here to help.
Basic Authentication to Get Token
This system has a two-step authentication but instead of using a normal body to request a token, the adapter needs to use basic authentication to get the token.
- Changes that need to be made to the action.json file:
- Notice the added header. Since this header is on the token request, the adapter library will still replace the variables and encode it (this is not normally done on header data).
- There are no other changes needed beyond the normal two-step token request.
entity/.system/action.json
{
"name": "getToken",
"protocol": "REST",
"method": "POST",
"entitypath": "{base_path}/{version}/authentication/login",
"requestSchema": "tokenReqSchema.json",
"responseSchema": "tokenRespSchema.json",
"timeout": 0,
"sendEmpty": false,
"requestDatatype": "JSON",
"responseDatatype": "JSON",
"headers": {
"Authorization": "Basic {b64}{username}:{password}{/b64}"
},
"responseObjects": [
{
"type": "default",
"key": "",
"mockFile": "mockdatafiles/getToken-default.json"
}
]
},
OAUTH Authentication
This system has a two-step OAUTH authentication process. It expects a bearer token to be returned. Some other changes are that it wanted the body of the token request to be urlencoded. Also that body needs to include some additional fields (grant_type, client_secret and client_id).
- Changes to the action.json file:
- Notice the different datatype on the request. Setting it to urlencode means the adapter library will urlencode the body prior to sending it.
- The urlencode on the response means that the adapter will decode and then parse the response.
- Changes to the adapter properties:
- The auth_field did not need to be changed as the token needed to be in the Authorization header.
- The auth_field_format has been changed so the format is what is expected of a Bearer token.
entity/.system/action.json
{
"name": "getToken",
"protocol": "REST",
"method": "POST",
"entitypath": "/oauth_token.do",
"requestSchema": "oAuthTokenRequest.json",
"responseSchema": "oAuthTokenResponse.json",
"timeout": 0,
"sendEmpty": false,
"requestDatatype": "urlencode",
"responseDatatype": "urlencode",
"headers": {},
"responseObjects": [
{
"type": "default",
"key": "",
"mockFile": "mockdatafiles/getToken-default.json"
}
]
},
Adapter Authentication Properties
"authentication": {
"auth_method": "request_token",
"username": "username",
"password": "password",
"token": "",
"invalid_token_error": 401,
"token_timeout": 180000,
"token_cache": "local",
"auth_field": "header.headers.Authorization",
"auth_field_format": "Bearer {token}"
},
For this OAUTH example, there are no changes to the response schema to support this as the token is in the body of the response.
- Changes to the token request schema file:
- This is where you define the new data that is part of the body.
- grant_type
- client_secret
- client_id
- This is where you define the new data that is part of the body.
- Since the data is static on all token requests, you can utilize the default field of the schema to put in the values.
- If the data was not static or if you prefer to put these fields into the code, you could utilize the authData object within the reqObj in the adapter.js to add these fields.
SAMPLE REQUEST SCHEMA
{
"$id": "oAuthTokenRequest.json",
"type": "object",
"schema": "http://json-schema.org/draft-07/schema#",
"translate": true,
"dynamicfields": true,
"properties": {
"ph_request_type": {
"type": "string",
"description": "type of request(internal to adapter)",
"default": "getToken",
"enum": [
"getToken"
],
"external_name": "ph_request_type"
},
"grant_type": {
"type": "string",
"description": "type of auth",
"default": "password",
"external_name": "grant_type"
},
"client_secret": {
"type": "string",
"description": "secret used during login",
"default": "111111",
"external_name": "client_secret"
},
"client_id": {
"type": "string",
"description": "client appreciation id",
"default": "agsfgsgdsgfdgsdfgfdsgsdfgsdfgdf",
"external_name": "client_id"
},
"username": {
"type": "string",
"description": "username to login with",
"external_name": "username"
},
"password": {
"type": "string",
"description": "password to login with",
"external_name": "password"
}
},
"definitions": {}
}
Authentication to a Different Host
- This system has a two-step authentication but the request to retrieve a token is to a different host than the actual system we are integrating with. Think of this as a separate single sign-on system that returns a token that can be used with any other system.
- Changes to the action.json file:
- Notice the sso object. This object allows you to define a protocol, host and port to retrieve the token from. It overrides any information from the adapter properties or the callProperties.
- The protocol and port can be omitted if they are the same as the adapter or call properties.
entity/.system/action.json
{
"name": "getToken",
"protocol": "REST",
"method": "POST",
"entitypath": "{base_path/{version}/authenication/login",
"requestSchema": "tokenReqSchema.json",
"responseSchema": "tokenRespSchema.json",
"timeout": 0,
"sendEmpty": false,
"requestDatatype": "JSON",
"responseDatatype": "PLAIN",
"headers": {},
"sso": {
"protocol": "https",
"host": "tokenhost",
"port": 443
},
"responseObjects": [
{
"type": "default",
"key": "",
"mockFile": "mockdatafiles/getToken-default.json"
}
]
},
Dynamic Token Per Domain
- This system has authentication on a per domain basis. Meaning you must authenticate within the domain you are retrieving/updating information in.
- Since this information is Dynamic per request, the information needs to be provided through the reqObj. This is done by adding the authData object with the contents you wish to add to the body of the authentication request.
ADAPTER.JS
const reqObj = {
payload: { garbage: 'need since post' },
uriPathVars: [groupId, deviceId],
uriQuery: { name: 'anyname' },
uriOptions: { page: 2 },
addlHeaders: { audit: 'turnOn' },
authData: {
domain: 'abc'
},
callProperties: {
stub: true,
request: {
attempt_timeout: 60000
}
},
filter: '[*name=doggie]'
};
Dynamic User Per Request
- This system has authentication based on the person making the request (it does not use a system user).
- The user credentials must be passed into the adapter method (in this case there are two variables that were passed in – dynuser and dynpass)
- Set the callProperties object in the ReqObj.
- Be sure to set username to the user variable (dynuser) and password to the password variable (dynpass) in the authentication section.
- The adapter library will use these credentials instead of the credentials defined in the adapter properties. However, you should still input values into the adapter properties, or the adapter may not startup.
ADAPTER.JS
const reqObj = {
payload: { garbage: 'need since post' },
uriPathVars: [groupId, deviceId],
uriQuery: { name: 'anyname' },
uriOptions: { page: 2 },
addlHeaders: { audit: 'turnOn' },
callProperties: {
authentication: {
username: dynuser,
password: dynpass
}
},
filter: '[*name=doggie]'
};
Encoding Request Values
This system requires encoding data in the body of the token request. To show that you can do both encoding and encrypting, we have altered the schema slightly.
- Notice in the schema, we are encoding the username by turning the encode flag to true.
- This does not encode the key just the data.
- Notice in the schema, we are encrypting the password using AES encryption with the provided key. Encryption will only happen when the key is provided.
- Similar with username, the key will not be encrypted, just the data.
SAMPLE REQUEST SCHEMA
{
"$id": "oAuthTokenRequest.json",
"type": "object",
"schema": "http://json-schema.org/draft-07/schema#",
"translate": true,
"dynamicfields": true,
"properties": {
"ph_request_type": {
"type": "string",
"description": "type of request(internal to adapter)",
"default": "getToken",
"enum": [
"getToken"
],
"external_name": "ph_request_type"
},
"username": {
"type": "string",
"description": "username to login with",
"encode": true,
"external_name": "username"
},
"password": {
"type": "string",
"description": "password to login with",
"encrypt": {
"type": "AES",
"key": "myspecialkey"
},
"external_name": "password"
}
},
"definitions": {}
}
Flexible Authentication
Authentication is very flexible!
- Remember, there are a variety of modifications you can make:
- Alter the getToken action.
- Alter the token schemas.
- Alter the adapter properties.
- Use authData or callProperties from adapter.js.
- You can also add other things into the adapter.js:
- For example, we had a system that took in a secret but wanted to pass it through SHA-1 with HMAC.
- Using adapter properties, Itential was able to achieve this with additional headers on calls and by adding SHA1/HMAC calls to the adapter.js.
Adapter Support
If you experience any problems or can’t figure out how to make a change, don’t hesitate to contact us. The Itential Adapters Team is here to help.
- For help with any adapter changes:
- Create a ticket (ISD, IPSO or ADAPT).
- You can work the ticket or allow the Itential Adapter Team to verify if the new authentication capability can be supported.
- Once all changes are made, update the adapter-utils dependency in your adapter or run the adapterMigrator.
- To update your adapter-utils dependency:
- Change the version in the package.json dependencies.
- rm –rf node modules
- rm package-lock.json
- npm install
Get Started with Itential
Start a 30 day free trial, or contact us to discuss your goals and how we can help.