How to Authenticate REST Requests

by Aamir Bhatt

Authentication of REST requests can be achieved through Service Oriented Architecture (SOA) over HTTP world via,

  • HTTP basic auth over HTTPS
  • Cookies and session management
  • Token in HTTP headers
  • Query Authentication with additional signature parameters

Each of these authentications have their own pros and cons.

Token in HTTP Headers

A token is put within the HTTP headers, so that the request is authenticated.

TECHNICAL TERMS

  • Client – An application making protected resource requests on behalf of the resource owner and with its authorization. The term “client” does not imply any particular implementation characteristics (e.g., whether the application executes on a server, a desktop, or other devices).
  • Authorization server – The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization.
  • Secret – Something that is kept or meant to be kept unknown or unseen by others, like password.
  • Salt – Salt is random data that is used as an additional input to a one-way function that hashes a Password or Passphrase. The primary function of salts is to defend against dictionary attacks versus a list of password hashes and against pre-computed rainbow table attacks. A new salt is randomly generated for each password. In a typical setting, the salt and the password are concatenated and processed with a cryptographic hash function, and the resulting output (but not the original password) is stored with the salt in a database. Hashing allows for later authentication while protecting the plaintext password in the event that the authentication data store is compromised.

ARCHITECTURE:

Each client has its own secret which is unique and this secret is kept confidential between client and developers. Based on client secret we create time based expiration tokens, which are then passed into request headers for authentication.

PROTOCOL FLOW

Abstract Protocol Flow

Abstract Protocol Flow

The abstract flow illustrated in Figure 1 describes the interaction between the four roles and includes the following steps:

  • The client requests authorization from the resource owner. The authorization request can be made directly to the resource owner (as shown), or preferably indirectly via the authorization server as an intermediary.
  • The client receives an authorization grant, which is a credential representing the resource owner’s authorization, expressed using one of four grant types defined in this specification or using an extension grant type. The authorization grant type depends on the method used by the client to request authorization and the types supported by the authorization server.
  • The client requests an access token by authenticating with the authorization server and presenting the authorization grant.
  • The authorization server authenticates the client and validates the authorization grant, and if valid, issues an access token.

CODE DEMO Using Flask REST

Installation

Install Flask-RESTful with pip

Quick Start

create a file main.py and add below snippet

from flask import Flask
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)

class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}

api.add_resource(HelloWorld, '/')

if __name__ == '__main__':
    app.run(debug=True)

Runserver

python main.py

Here / url corresponds to Hello World Resource. Now we need to authenticate this request.

Authorization Grant

An authorization grant is a credential representing the resource owner’s authorization (to access its protected resources) used by the client to obtain an access token.

Access Token

Access tokens are credentials used to access protected resources. An access token is a string representing an authorization issued to the client. The string is usually opaque to the client. Tokens represent specific scopes and duration of access, granted by the resource owner, and enforced by the resource server and authorization server.

The token may denote an identifier used to retrieve the authorization information or may self-contain the authorization information in a verifiable manner (i.e. a token string consisting of some data and a signature). Additional authentication credentials, which are beyond the scope of this specification, may be required in order for the client to use a token.

The access token provides an abstraction layer, replacing different authorization constructs (e.g. username and password) with a single token understood by the resource server. This abstraction enables issuing access tokens more restrictive than the authorization grant used to obtain them, as well as removing the resource server’s need to understand a wide range of authentication methods.

Access tokens can have different formats, structures, and methods of utilization (e.g., cryptographic properties) based on the resource server security requirements. Access token attributes and the methods used to access protected resources are beyond the scope of this specification.

Refresh Token

Refresh tokens are credentials used to obtain access tokens. Refresh tokens are issued to the client by the authorization server and are used to obtain a new access token when the current access token becomes invalid or expires, or to obtain additional access tokens with identical or narrower scope (access tokens may have a shorter lifetime and fewer permissions than authorized by the resource owner). Issuing a refresh token is optional at the discretion of the authorization server. If the authorization server issues a refresh token, it is included when issuing an access token (i.e. step (D) in Figure 1).

A refresh token is a string representing the authorization granted to the client by the resource owner. The string is usually opaque to the client. The token denotes an identifier used to retrieve the authorization information. Unlike access tokens, refresh tokens are intended for use only with authorization servers and are never sent to resource servers.

Expired Access Token

The flow illustrated in Figure 2 includes the following steps:

(A) The client requests an access token by authenticating the authorization server and presenting an authorization grant.

(B) The authorization server authenticates the client and validates the authorization grant, and if valid, issues an access token and a refresh token.

(C) The client makes a protected resource request to the resource server by presenting the access token.

(D) The resource server validates the access token, and if valid, serves the request.

(E) Steps (C) and (D) repeat until the access token expires. If the client knows the access token expired, it skips to step (G); otherwise, it makes another protected resource request.

(F) Since the access token is invalid, the resource server returns an invalid token error.

(G) The client requests a new access token by authenticating with the authorization server and presenting the refresh token. The client authentication requirements are based on the client type and on the authorization server policies.

(H) The authorization server authenticates the client and validates the refresh token, and if valid, issues a new access token (and optionally, a new refresh token).

Create authorization grant request and access in Flask:

To client, we have given secret ‘i m client secret’ and salt “ date.today+ newyork”. And client id secret and salt produces hashed client secret, that is required for Authorization Grant access. This client does.

Client secret: ‘i m client secret’
Salt = “ date.today().strftime('%m/%d/%Y')+' newyork”

import hashlib

import hmac

from datetime import datetime

from datetime import date

hashed = hmac.new(Client secret, salt, sha1)

token = hashed.digest().encode("hex")

print token produces output

‘0a8e51c54f5e6157354547829

Authenticate REST request

 

The client sends the above code to the server. If it matches as shown below verify_client, then token is generated and we save in database.

 


Main.py 
..


api.add_resource(TokenListResource, '/tokens')

class TokenListResource(Resource):
   parser = reqparse.RequestParser()
   parser.add_argument('client_id', type=int, required=True, length=2)
   parser.add_argument('client_secret_hash', type=str, required=True)


     def post(self):
       data = self.parser.parse_args()
       client_id = data.get('client_id', '')
       client_secret_hash = data.get('client_secret_hash', '')
       is_client = Client() \
           .verify_client(id=client_id,  client_secret_hash=client_secret_hash) 
#verify same way we did in client side, client id gives us client_secret from database then we 
# do same algo that we did client side then match that hash with client_secret_hash
       if is_client:
           Token().create(client_id, expires_on=expires_on)
           client_obj = Client().get(client_id)
           token = client_obj.token
           data = token
           return resp_envelope(status=STATUS_SUCCESS,
                                data=data), httplib.CREATED
       else:
           return cannot_authenticate()

Now we will first authenticate the url / and then give access to hello world Resource . For this we need decorator requires_auth


@requires_auth
class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}


def requires_auth(f):
   @wraps(f)
   def decorated(*args, **kwargs):
       print args, kwargs
       auth_token = request.headers.get('X-PA-AUTH-TOKEN', None)
       if not auth_token:
           return missing_headers(['X-PA-AUTH-TOKEN'])
       elif authorize(auth_token) == 0:   # check with database
           return cannot_authenticate()
       return f(*args, **kwargs)

   return decorated



def cannot_authenticate():
   message = resp_envelope(status=0, data={}, err_code=1001,
                           err_mesg="Not Authorized",
                           err_desc="Not Authorized")
   return message, UNAUTHORIZED


def missing_headers(missing_headers):
   print missing_headers
   missing_header = ','.join(missing_headers)
   message = resp_envelope(status=0, data={}, err_code=1003,
                           err_mesg="Missing header", err_desc=missing_header)
   return message, BAD_REQUEST


def access_denied():
   message = resp_envelope(status=0, data={}, err_code=1010,
                           err_mesg="Access denied", err_desc='access_denied')
   return message, BAD_REQUEST

Client side
In The client  call we will add token as header 
 headers = []
headers['X-AUTH-TOKEN']=‘7762288ec0355454460909cd671bd737fed62c43’

   r = requests.get(url=‘/’, data={}, headers=headers)

This way we authenticate our Hello World request.

Leave a Reply

Your email address will not be published. Required fields are marked *

Tools & Practices

Tools and Technologies we use at Applied

Contact us now

Popular Posts