When a client uses the challenge and response method of authentication, it means the client authenticates with the StarLeaf Cloud as a real user.

The client must authenticate itself with the StarLeaf Cloud API server using the credentials (email address and password) of a user registered on the StarLeaf Cloud. These are the same login credentials with which the user can access the StarLeaf Portal. After authentication, data regarding the authenticated session is stored on the client using cookies and so the client must implement a cookie jar.

Before the client can make any API requests, the client must first authenticate itself with the StarLeaf Cloud API server using a challenge-response protocol. The client requests the challenge by making an HTTPS GET request to:
/challenge?username=<username>
where <username> is the user email address to be used for authentication. The server then responds with the challenge parameters.

The client must now use the challenge parameters, together with the user password, to create the challenge response. This challenge response must be sent to the server using a POST request to:
/authenticate
Assuming authentication is successful, the server responds by setting a number of cookies on the client containing data about the authenticated session, including an authentication token. This token authenticates all subsequent requests from the client, until it expires. Once the token has expired, any further requests from the client (other than to the /challenge path) yields a 401 Unauthorized response. On receipt of such a response, the client must repeat the authentication process summarized above.

Occasionally, it is necessary for a request to be retried with updated cookies. In this case, the server provides a 449 Retry response whilst updating the cookies. The client should then retry the request.

If the Cloud API is not enabled for the user or an attempt is made to access a resource outside of the user’s privilege level, then this generates a 403 Forbidden response.

Authentication process

Step one

To begin the challenge and response authentication process, make an HTTPS GET request to:

/challenge?username=<username>

where <username> is the user email address to be used for authentication. There is no request body.

This generates a 200 OK response with a body having the following structure:

{ 
  "salt": <string: required>, 
  "iterations": <integer: required>, 
  "challenge": <string: required> 
}  	
Field Description

salt

Hexadecimal representation of a 128 bit value—refer to Calculating the response for more details.

iterations

Integer—refer to Calculating the response for more details.

challenge

Hexadecimal representation of a 256 bit value—refer to Calculating the response for more details.

Step two

To complete the authentication process, make an HTTPS POST request to:

/authenticate

The structure of the request is as follows:

{ 
  "username": <string: required> 
  "response": <string: required> 
}	
Field Description

username

The user email address to be used for authentication.

response

Hexadecimal representation of a 256 bit value — refer to Calculating the response for more details.

On success, this generates a 204 No Content response with no body. An incorrect response to the challenge (or an unrecognized username) generates a 401 Unauthorized response from the server.

Calculating the response

This section defines how the client should calculate the response to the challenge generated by the server. The required steps are listed below. These are followed by an example Python implementation.

  1. Convert the salt and challenge parameters (which are hexadecimal strings) to binary representations.
  2. Derive the PBKDF2 key from the user password along with the salt and iterations parameters:
  3. key = PBKDF2(HMAC-SHA256, password, salt, iterations, 256)

    The salt and iterations parameters are invariant for each user and so the same key can be used for future authentication procedures for the same user.

  4. Generate the HMAC response using the derived key and the challenge parameter: response = HMAC-SHA256(key, challenge)

The challenge parameter is different each time the authentication procedure is executed and so a new response must be calculated each time.

Example Python implementation:

 
#!/usr/bin/python 
# Copyright (c) StarLeaf Ltd. 2015 
import hashlib 
import hmac 
import pbkdf2 
import binascii  
    
def cloud_api_authentication(password, salt_hex, iterations, challenge_hex):
	salt = binascii.unhexlify(salt_hex)     
	key = pbkdf2.PBKDF2(
	      passphrase=password, salt=salt, 
	      iterations=iterations, digestmodule=hashlib.sha256, 
	      macmodule=hmac
	).read(32)
				 
	challenge = binascii.unhexlify(challenge_hex)     
	hash = hmac.new(key, challenge, hashlib.sha256)     
	response = hash.hexdigest()   
	return response 
				
if __name__ == '__main__': 
	from optparse import OptionParser 
		
	parser = OptionParser() 
	parser.add_option(
	    "--password", dest='password', 
	    help="API password, as set in portal."
	)     
	parser.add_option(
	    "--salt", dest='salt', 
           help="Salt to apply to the password during key derivation."
	)     
	parser.add_option(
	    "--iterations", dest='iterations', type='int',   
           help="Number of iterations to hash during key derivation."
	) 
	parser.add_option(
	    "--challenge", dest='challenge', 
	    help="Challenge returned by server."
	)  
			
	(opt, args) = parser.parse_args() 
 
	response = cloud_api_authentication(
	    opt.password, opt.salt, opt.iterations, opt.challenge
	)    
	print "{\n  \"response\": \"%s\"\n}" % response