Reducing Authorization Latency

Authorizing user access to resources adds latency because the authorization needs to be completed before resource access is performed.

This Authorization Latency increases as the complexity of authorization schemes increases. In most cases authorization requires database access which will always introduce significant latency to all requests on a server even if the authorization database is running on a distinct server than the "business" database.

A complex authorization scheme would typically require several joins. The following SQL query retrieves the list of resources and associated roles a user has in a system where authorization rules define roles for user groups on resource groups: 

SELECT groups.resource_id, rules.role_id
FROM       authorization_rules as rules
INNER JOIN resources
ON resources.id = rules.resource_id
INNER JOIN resource_groups     as groups
ON groups.id = resources.group_id
INNER JOIN user_groups_users   as users
ON users.user_group_id = rules.a.user_group_id
WHERE ( users.user_id = $user_id )

The above authorization request joining four tables is likely to take more time than many "business" queries. Despite this complexity, this model does not allow to define groups of groups for users and resources.

Authorization requests succeed 99.999% of the time because users in a system designed properly are authorized to perform what they request. This can be seen as unnecessary latency most of the time.

The proposed alternative is to perform authorization in parallel of the execution of the request. After both have completed, the server checks the result of the authorization to either forward or drop the response.

User authentication should probably still be performed before everything else to prevent denial of service attacks. Authenticated users are much less likely to attempt unauthorized access than non-authenticated users.

An implementation of parallel authorization should be straightforward for read requests. The authorization spawns a thread or coroutine before the request is executed to fetch the response:

thread_id = user.authorized( resource, "read" )
response = resource.read()
if not thread_id.join().authorized {
  throw( "Not Authorized" )
}
return response

Another more complex read scenario is where a number of records could be returned from the database but the user only has reading rights to some of them. The authorization then returns a list of authorized resource identifiers and a query must be done on the results to extract only those authorized:

thread_id = user.authorized_resources( resource_type, "read" )
responses = resources.read()
for id in thread_id.join().authorized_resources {
  if ( responses[ id ] ) {
    yield responses[ id ]
  }
}

If the ratio of the number of responses returned by the database to the number of authorized records is too high, unused records could significantly increase latency and one should consider falling back to authorizing first then reading only authorized resources.

On update requests, non-authorized updates must be undone. Using a relational database, a transaction can be used, performing the update, committing if the authorization succeeds, or rolling back the transaction if it fails:

thread_id = user.authorized( resource, "update" )
try {
  begin_transaction()
  response = resource.update()
  if not thread_id.join().authorized {
    request.user.disable() // to prevent further abuse by this user
    throw( "Not Authorized" )
  }
} catch( e ) {
  rollback_transaction()
  re_throw( e )
}
commit_transaction()
return response

Implementing this on a non-transactional system, such as a NoSQL database, would require at least a versioning system where each update in the database creates a new revision that can be undone.
thread_id = user.authorize( resource, "update" )
response = resource.update()
if not thread_id.join().authorized {
  response.undo()
  request.user.disable() // to prevent further abuse by this user
  throw( "Not Authorized" )
}
return response

There are a few other issues not discussed here, regarding the retrieval of unauthorized documents before these are removed, see my first comment bellow.