How to: Get started with the Alta Aware API

Last modified: Friday November 18, 2022.

The Alta Aware API allows open access to the functionality of Aware. Other applications or scripts can be developed against this API to carry out a wide range of tasks, including automation of operations or integrations with other systems.

All requests are made to API endpoints under https://<serverAddress>/api/v1.

Authentication

Authentication occurs via a HTTP POST to https://<serverAddress>/api/v1/dologin.

https://test-deployment.eu1.aware.avasecurity.com/api/v1/dologin

The username and password are provided as a JSON body of the format: {"username":"apiUsername","password":"password"}

If you have a user called apiuser1 with password ^hgY&8hfthsdfYU^, the JSON body would be:

{"username":"apiuser1","password":"^hgY&8hfthsdfYU^"}

The response to this is a cookie that is then used in all further requests.

Best practice is to not hardcode any usernames or passwords within your application or script.

Also, consider using a separate account for each API consumer.

The Aware API uses the same rich permissions scheme as the rest of the Aware system. Hence the visibility of information, and the ability to carry out actions will depend on the permissions of the user that you use for the API. This allows you to create API users with limited permissions specific for each integration or task.

Using two factor authentication (2FA) with the API

It is possible to use two factor authentication when using the API. This is done via a HTTP POST to https://<serverAddress>/api/v1/totp with a JSON body of the format {"code":totp,"name":name}

If the current TOTP code is 029842, and the name is not set (which is the default) then the JSON body would be {"code":"029842","name":""}

Generating Two-Factor TOTP codes programmatically

It is possible to use the Setup Key created during the configuration of 2FA to generate the TOTP codes.

To generate TOTP from within your application, you can either manually configure 2FA, or do this from within your application.

To manually configure 2FA:

  1. Log in to the Aware user interface as the API user using the existing credentials.
  2. Enable 2FA for the account, and find the Setup Key from the configuration page.
  3. SETUP KEY

    VNxxxxxxxxxxxxxxxxxxxxxxxxxxxxZG

  4. Securely store the Setup Key.
  5. Use the Setup Key to generate the current TOTP value.
  6. Enter this into the configuration dialog.

To automatically setup the TOTP

  1. Send an HTTP POST to https://<server-name>/api/v1/me/totp.
  2. Extract Setup Key value from secret parameter within the uri parameter in the response.
  3. Securely store this Setup Key.
  4. Use the Setup Key to generate TOTP code.
  5. Send an HTTP PUT to https://<server-name>/api/v1/me/totp with a JSON body of the format {"name":"", "uri": <Copied from response to POST>, "verification_code": <Generated code>}

After this, each time the API user attempts to sign in, the current TOTP code will need to be generated and provided in the call to totp.

Risk of loss of data!

You must store the password and Setup Key in such a way to prevent unauthenticated access to them. If these both become known by a user, then that user can impersonate the API user.

API endpoints

All other requests use the cookie from the dologin request for authorization.

The HTTP status codes return whether the request has succeeded or not.

In cases where data is returned, a JSON data structure is used. The details of URIs, parameters, and JSON format is held within the product in the API Explorer. This also provides a method for testing API requests as the signed in user.

Examples

Example Python code to return lists of users and devices. This supports setup and use of 2FA:

#!/usr/bin/env python

# Simple example of using Alta Aware API
# Allow for 2FA secret generation, and 2FA entry via TOTP code or secret.
# Throws exceptions on errors, but no handling of these.
# Hostname, username, password and TOTP/secret come from user entry.
# Password and TOTP/secret via getpass function to try to stop echo to terminal.

import requests
import json
import getpass
import base64
import hmac
import struct
import time

def awareTOTP(secret):
	#Generate the OTP from the secret.
	secret = base64.b32decode(secret + '=' * ((8 - len(secret)) % 8), casefold=True)
	counter=(int(time.time() / 30))
	# Create the mac for the key and message(counter)
	mac = hmac.digest(secret, struct.pack('>Q', counter), "sha1")
	# Determine the offset and extract 31 bits from this offset
	offset = mac[-1] & 0x0f
	output = struct.unpack('>L', mac[offset:offset+4])[0] & 0x7fffffff
	# Return the last 6 digits (padded with zeros)
	return '{:=06}'.format(output%1000000)

def awareGet(session, url):
	resp=session.get(url, timeout=1)
	if (resp.status_code != 200):
		print("GET failed for",url,"returned",resp.status_code)
		resp.raise_for_status()
	return(resp.json())

def awarePost(session, url, data):
	resp=session.post(url,data=json.dumps(data), timeout=1)
	if (resp.status_code != 200):
		print("failed to post to",url,"returned",resp.status_code)
		resp.raise_for_status()
	if len(resp.content)>0:
		return(resp.json())
	else:
		return()

def awarePut(session, url, data):
	resp=session.put(url,data=json.dumps(data), timeout=1)
	if (resp.status_code != 200):
		print("failed to put to",url,"returned",resp.status_code)
		resp.raise_for_status()
	return()

def doLogin(session,host,username,password):
	loginURL=host+"/api/v1/dologin"
	cred={"username":username,"password":password}
	awarePost(session,loginURL,cred)
	return()

def getAuthInfo(session,host):
	url=host+"/api/v1/auth"
	resp=awareGet(session, url)
	return(resp)

def postTOTP(session,host,totp,name):
	url=host+"/api/v1/totp"
	data={"code":totp,"name":name}
	awarePost(session, url, data)
	return()

def postMyTOTP(session,host):
	url=host+"/api/v1/me/totp"
	data={}
	resp=awarePost(session, url, data)
	return(resp)

def putMyTOTP(session,host,totp,name,uri):
	url=host+"/api/v1/me/totp"
	data={"verification_code":totp,"name":name,"uri":uri}
	awarePut(session, url, data)
	return()

def getUsers(session,host):
	url=host+"/api/v1/users"
	resp=awareGet(session, url)
	return(resp)

def getDevices(session,host):
	url=host+"/api/v1/devices"
	resp=awareGet(session, url)
	return(resp)

#
# Script to print lists of users and devices using the above functions.
#
session = requests.Session()

print("API example code for Alta Aware")
print("Enter the deployment and credentials")
host=input("Hostname: ")
username=input("Username: ")

# Attempt to avoid echoing password to terminal - doesn't always work.
password=getpass.getpass(prompt="Password: ")

# Authenticate with server.
doLogin(session,host,username,password)

# Read the MFA info to determine whether 2FA is required.
authInfo=getAuthInfo(session,host)
# If MFA needs to be set up, then do so
if (authInfo['mfaResetRequired']):
	from urllib import parse
	setup=postMyTOTP(session,host)
	secret = (dict(parse.parse_qsl(parse.urlsplit(setup['uri']).query)))['secret']
	print("The secret value is",secret,"don't lose it")
	putMyTOTP(session,host,awareTOTP(secret),"",setup['uri'])

elif (authInfo['mfaChallengeRequired']):
	#prompt for 2FA token/secret
	secret=getpass.getpass(prompt="Secret/TOTP: ")
	if len(secret)!=6:
		print("Generating 2FA")
		postTOTP(session,host,awareTOTP(secret),"")
	else:
		postTOTP(session,host,secret,"")

# Get the list of users and if successful, print it.
users=getUsers(session,host)
print("Found",len(users),"users")
for user in users:
	print("id:",user['id'],"username:",user['username'])

# Get the list of devices and if successful, print it.
devices=getDevices(session,host)
print("Found",len(devices),"devices")
for device in devices:
	print("id:",device['guid'],"name:",device['name'])
			

Example of an Object Oriented script including MFA generation:

#!/usr/bin/env python

# Simple class based example of using Alta Aware API
# Allow for 2FA secret generation, and 2FA entry via TOTP code or secret.
# Throws exceptions on errors, but no handling of these.
# Hostname, username, password and TOTP/secret come from user entry.
# Password and TOTP/secret via getpass function to try to stop echo to terminal.

import requests
import json
import getpass
import base64
import hmac
import struct
import time

class awareOTP:
	def __init__(self,secret):
		# Pad secret and base32 decode it.
		self.secret = base64.b32decode(secret + '=' * ((8 - len(secret)) % 8), casefold=True)

	def hotp(self,counter):
		# Create the mac for the key and message(counter)
		mac = hmac.digest(self.secret, struct.pack('>Q', counter), "sha1")
		# Determine the offset and extract 31 bits from this offset
		offset = mac[-1] & 0x0f
		output = struct.unpack('>L', mac[offset:offset+4])[0] & 0x7fffffff
		# Return the last 6 digits (padded with zeros)
		return '{:=06}'.format(output%1000000)

	def now(self):
		# create the TOTP based on current time.
		return self.hotp(int(time.time() / 30))

class awareAPIclient:
	def __init__(self, host,username,password):
		self.session = requests.Session()
		loginURL=host+"/api/v1/dologin"
		cred={"username":username,"password":password}
		self.awarePost(loginURL,cred)
		# If authentication succeeded, store the host.
		self.host=host

	def awarePut(self, url, data):
		resp=self.session.put(url,data=json.dumps(data), timeout=1)
		if (resp.status_code != 200):
			print("failed to put to",url,"returned",resp.status_code)
			resp.raise_for_status()
		return()

	def awarePost(self, url, data):
		resp=self.session.post(url,data=json.dumps(data), timeout=1)
		if (resp.status_code != 200):
			print("failed to post to",url,"returned",resp.status_code)
			resp.raise_for_status()
		if len(resp.content)>0:
			return(resp.json())
		else:
			return()

	def awareGet(self, url):
		resp=self.session.get(url, timeout=1)
		if (resp.status_code != 200):
			print("GET failed for",url,"returned",resp.status_code)
			resp.raise_for_status()
		return(resp.json())

	def getUsers(self):
		url=self.host+"/api/v1/users"
		resp=self.awareGet(url)
		return(resp)

	def getDevices(self):
		url=self.host+"/api/v1/devices"
		resp=self.awareGet(url)
		return(resp)

	def getAuthInfo(self):
		url=self.host+"/api/v1/auth"
		resp=self.awareGet(url)
		return(resp)

	def postTOTP(self,totp,name):
		url=self.host+"/api/v1/totp"
		data={"code":totp,"name":name}
		self.awarePost(url, data)
		return()

	def postMyTOTP(self):
		url=self.host+"/api/v1/me/totp"
		data={}
		resp=self.awarePost(url, data)
		return(resp)

	def putMyTOTP(self,totp,name,uri):
		url=self.host+"/api/v1/me/totp"
		data={"verification_code":totp,"name":name,"uri":uri}
		self.awarePut(url, data)
		return()

#
# Script to print a list of users using the above functions.
# Hostname, username and password come from user entry.
# Password via getpass function to try to stop echo to terminal.
# 2FA also via same function.

import getpass

print("API example code for Alta Aware")
print("Enter the deployment and credentials")
host=input("Hostname: ")
username=input("Username: ")
# Attempt to avoid echoing password to terminal - doesn't always work.
password=getpass.getpass(prompt="Password: ")

# Create client and authenticate
awareClient = awareAPIclient(host,username,password)

# Read the authentication info to determine whether 2FA is required.
authInfo=awareClient.getAuthInfo()

# If MFA needs to be set up, then do so
if (authInfo['mfaResetRequired']):
	from urllib import parse
	setup=awareClient.postMyTOTP()
	secret = (dict(parse.parse_qsl(parse.urlsplit(setup['uri']).query)))['secret']
	totp=awareOTP(secret)
	print("The secret value is",secret,"don't lose it")
	awareClient.putMyTOTP(totp.now(),"",setup['uri'])

elif (authInfo['mfaChallengeRequired']):
	#prompt for 2FA token/secret
	secret=getpass.getpass(prompt="Secret/TOTP: ")
	if len(secret)!=6:
		print("Generating 2FA")
		totp=awareOTP(secret)
		awareClient.postTOTP(totp.now(),"")
	else:
		awareClient.postTOTP(secret,"")

# Get the list of users and if successful, print it.
users=awareClient.getUsers()
print("Found",len(users),"users")
for user in users:
	print("id:",user['id'],"username:",user['username'])

# Get the list of devices and if successful, print it.
devices=awareClient.getDevices()
print("Found",len(devices),"devices")
for device in devices:
	print("id:",device['guid'],"name:",device['name'])