Blog

New required steps to use the Halo API

We made a lot of improvements on the Halo API for this latest release, including making improvements in the method of authentication for API calls to make them more secure, enabling you to write automation and integration scripts with confidence.


When you access the API, you authenticate with your API Key ID and secret, and receive a temporary access token which you use for subsequent requests.  You no longer need to pass your API key with every API call you make.  This change also means that there are new steps required to use the Halo API.  We’ve outlined the steps and included code snippets below in Python and Ruby to show the authentication process.

1. Authenticate using a API KeyID and Secret – this will generate an “access token” required for all API calls

url = '/oauth/access_token?grant_type=client_credentials'
base64str = base64.encodestring('%s:%s' % (key_id, secret_key))[:-1]
authHeader = "Basic %s" % (base64str)

# connect to portal and POST the token request
c = self.connect()
c.request("POST", url, '', {"Authorization": authHeader})
resp = c.getresponse()

2. Pass the access token in each API call’s “Authorization” header

c = self.connect() 
c.request("GET", '/v1/%s' % (url), '', {"Authorization": 'Bearer %s' % (token), "Content-type": “application/json”}) 
return c.getresponse()

The API KeyID and Secret can be found in the portal UI under Settings > Site Administration > API Keys

If you are already using the Halo API, you will need to make some changes to your code to comply with the changes to the API’s new, more secure, more flexible authentication method.  However, the legacy API will still function for a while to give you time to transition over.  You will need to make the following changes to the following fields and values in your scripts:

Fields and values of the required changes…

1. the “access token” URL
* /oauth/access_token?grant_type=client_credentials

2. the returned “access token” fieldname from a authorization request
* access_token

3. a new API header value for all calls
* “Authorization” : “Bearer <access_token>”

Here’s a complete example using Python to authenticate, then to list all active Halo users.

import httplib
import json
import base64

class API:
def connect(self):
  """ setup a connection to api.cloudpassage.com
  """
  return httplib.HTTPSConnection('api.cloudpassage.com')
def get_token(self, key_id, secret_key):
  """ craft a Authorization header by encoding
    a specific key_id and secret_key
  """
  url = '/oauth/access_token?grant_type=client_credentials'
  base64str = base64.encodestring('%s:%s' % (key_id, secret_key)[:-1]
  authHeader = "Basic %s" % (base64str)

  # connect to portal and POST the token request
  c = self.connect()
  c.request("POST", url, '', {"Authorization": authHeader})
  resp = c.getresponse()

  # confirm it was authenticated, then parse
  # out the token using a JSON module
  if resp.status == 200:
    jsondata = resp.read().decode()
    data = json.loads(jsondata)
    # parse out and return the token
    token = data['access_token']
    return token
  else:
    raise AssertionError(resp.status, resp.reason)

def get(self, url, token):
  """ craft a API GET request using an authenticated
  token instead of a site-wide API Key
  """
  c = self.connect()
  c.request("GET", '/v1/%s' % (url), '', {"Authorization": “Bearer %s” % (token), "Content-type": "application/json"})
return c.getresponse()

# these values are from Settings > Site Administration > A
# Settings > Site Administration > API Keys
key_id = 'a0a9a982'
secret_key = 'eda161fd2f3541736a7e096bcb971094'
# instantiate our API class
api = API()
# generate a new token based on the
# provided key_id and secret_key
token = api.get_token(key_id, secret_key)
# make future API calls using the user's token
# a 200 means the request was successful
resp = api.get('/users', token)
print resp.status
# confirm a call w/out a token returns 401/Unauthorized
resp = api.get('/users', '')
print resp.status

Here’s the same example written in Ruby.

require 'rest_client'
require 'json'
require 'base64'

class APICalls
  def initialize ()
  end

  # craft a Authorization header by encoding
  # a specific key_id and secret_key
  def get_token(key_id, secret_key)
    url = "/oauth/access_token?grant_type=client_credentials"
    base64str = Base64.encode64("#{key_id}:#{secret_key}")
  # connect to portal and POST the token request
  resp = RestClient.post("https://api.cloudpassage.com/#{url}", '  ',
  {:"Authorization" => "Basic #{base64str}",
  :"Content-type" => "application/json"})
  # confirm it was authenticated, then parse
  # out the token using a JSON gem
  if resp.code == 200
    data = JSON.parse(resp)
    return data['access_token']
  else
    raise (resp.response)
  end
end

  # craft a API GET request using a authenticated
  # token instead of a site-wide API key
  def get(url, token)
    begin
    @resp = RestClient.get("https://api.cloudpassage.com/v1/#{url}",
    {:"Authorization" => "Bearer #{token}",
    :"Content-type" => “application/json”})
    rescue => e
      @resp = e.response
    end
  end
end

# these values are from 
# Settings > Site Administration > API Keys
key_id = 'a0a9a982'
secret_key = 'eda161fd2f3541736a7e096bcb971094'
# instantiate our API class
@api = APICalls.new()

# generate a new token based on the provided
# key_id and secret_key
token = @api.get_token(key_id, secret_key)

# make future API calls using the application-specific
# token. A 200 means the request was successful
resp = @api.get('/users', token)
puts resp.code

# confirm a call w/out a token returns 401/Unauthorized
resp = @api.get('/users', '')
puts resp.code

Output from running the example script…

We’ll be posting more code snippets and API help in our blog and forums – is there anything specific you’d like to see?  Let us know!

Stay up to date

Get the latest news and tips on protecting critical business assets.

Related Posts