A lot of applications require the need for an in-session authorization mechanism. This means that when an end-user is within an authenticated session, the end-user will be prompted to enter a set of credentials before he can be allowed to perform specific transactions. These transactions are identified as high-risk transactions, such as changing a password or changing a registered e-mail address. The set of requested credentials could be the same set used to sign-in to the application (which is confusingly often mislabeled as re-authentication), or it could be a different set of credentials specific for the transaction that is being requested.
Almost all the time, this in-session authorization mechanism is desired to be delivered or performed out-of-band. That is, the secondary credentials are either sent out to a delivery channel different from where the transaction was initiated, or the secondary credentials are entered in a delivery channel different from where the transaction was initiated.
I’m specifically addressing the general concept of authorization, instead of the more specific concept of authentication. In the context of using an out-of-band mechanism for authorization, if we identify that high-risk transaction to be the “signing-in” transaction, then the mechanism leads to what is known in the industry as multi-factor authentication.
Multi-factor authentication (MFA) is available in a lot of applications. However, being a specialization of the more general concept of out-of-band authorization (OOBA), there are scenarios that MFA do not take into account during application design. These scenarios, if not accounted for, often lead to an insecure implementation of a general OOBA mechanism.
High-risk Transaction Identification
The first step in an OOBA mechanism is to identify if a transaction is a high-risk transaction. Most of the time, just the transaction name itself is enough to identify it as high-risk. An example is the password change transaction.
There are times where identification depends on the payload of the transaction request. An example would be an ATM withdrawal where withdrawing less than a certain threshold is not considered high-risk, and withdrawing above that threshold is considered high-risk. In this case, the transaction request has to be inspected to determine whether it is high-risk or not. This has implications on application design because the application cannot prompt for secondary credentials before the user has even entered the transaction request details.
Transaction and Authorization Context
An important part of OOBA is the association of the authorization context with that of the transaction context. Before a transaction is allowed to go through, the authorization context associated with the transaction should be verified. It is not enough to verify just any authorization context; it has to be the authorization context associated with the transaction.
If the authorization context is verified in a separate operation than that of the transaction, it is very important that the context association be maintained. That is, it is not enough to ask a verification service, “Are these the correct credentials?” We have to ask the verification service, “Are these the correct credentials for this particular transaction request?”
In a web application, in order for the context association to be maintained, the transaction request and the secondary credentials should be sent in a single HTTP request. Technically, separate HTTP requests could be used, but would require a mechanism of associating the separate HTTP requests with each other.
Design Notes
Web Forms
Out-of-band authorization can be implemented in web forms through a user control embedded in the form. The OOBA user control visibility can be toggled depending on the position of a transaction within a workflow. For the most common type of OOBA, the user control merely provides entry fields where OOBA secondary credentials are entered.
Server-side validation of the transaction request and the OOBA credentials occur within a single HTTP request before the transaction is completed.
authzValidationNeeded = … depending on the transaction request payload …
if (authzValidationNeeded)
authzCredentials = … retrieved from form fields …
if Empty(authzCrendentials) || !Validate(authzCredentials)
then
return an authz error,
allow the user to re-enter credentials,
and resubmit transaction and credentials
Because of the importance of associating the authorization context with the transaction, the authorization context should not be validated in a separate earlier step (in a separate earlier HTTP request) than that of the transaction itself.
MVC Forms
The authorization token follows the same mechanism used in the built-in MVC AntiForgery mechanism; passed to the server as a separate form field. It differs only in that the token value is not generated at the time the view is generated. The token value is requested from the user by making the form field visible and having the user input the value.
Server-side validation of the transaction request is the same as that in Web Forms.
WCF Ajax Services
WCF Ajax services pose an implementation challenge because in a general architectural design, we want to separate the concerns of authorization with that of the transaction. If we require the transaction interface to be modified to accommodate the authorization credentials, like:
FooResponse FooOperation(FooRequest request, AuthorizationCredentials credentials)
Then authorization concerns interfere with the clean operation interface of the transaction.
Since it is desired to pass the authorization credentials in the same HTTP request as that of the transaction, the only other place we can pass them are through the HTTP headers.
Server-side validation of the transaction request and the OOBA credentials occur within a single HTTP request before the transaction is completed.
authzValidationNeeded = … depending on the transaction request payload …
if (authzValidationNeeded)
authzCredentials = … retrieved from HTTP header …
if Empty(authzCrendentials) || !Validate(authzCredentials)
then
return an authz error,
allow the user to re-enter credentials,
and resubmit transaction and credentials
On the client side, the transaction invocation is conventionally done through JavaScript. The invocation would originally look like:
IFooService.FooOperation(fooRequest,
Function.createDelegate(this, this.onFooOperationSuccess),
Function.createDelegate(this, this.onFooOperationFailure),
fooContext);
If identified as a high-risk transaction, the invocation should be changed to present the authorization credentials form, and invoke the service operation after the credentials have been entered, like:
authzWidget.show(this, fooRequest, fooContext,
function (that, request, context, credentials) {
IFooService.FooOperation(request,
Function.createDelegate(that, that.onFooOperationSuccess),
Function.createDelegate(that, that.onFooOperationFailure),
{
context: context,
credentials : credentials
});
});
Or like (using jQuery’s proxy instead of Microsoft Ajax’s delegate):
authzWidget.show(this, fooRequest, fooContext,
function (that, request, context, credentials) {
IFooService.FooOperation(request,
$.proxy(that.onFooOperationSuccess, that),
$.proxy(that.onFooOperationFailure, that),
{
context: context,
credentials : credentials
});
});
The Microsoft Ajax WebRequestManager is modified to check for the presence of the credentials in the invocation context and send that as part of the HTTP headers.
Sys.Net.WebRequestManager.add_invokingRequest(function (sender, eventArgs) {
var webRequest = eventArgs.get_webRequest();
var userContext = webRequest.get_userContext();
if (typeof userContext == "object" && userContext.credentials) {
var headers = webRequest.get_headers();
headers["X-RequestAuthorizationToken"] = userContext.credentials;
}
});
Additional Notes
Slightly related to the implementation design above, let me explain my claim that authentication is an extension to authorization. Authorization is the core process. I understand that this is highly controversial. I’ll start by qualifying that there are two kinds of authorization — an authenticated authorization and an unauthenticated authorization. Most developers are familiar with the former wherein the identity of the caller is part of the context used to determine whether or not a transaction is authorized. What about an unauthenticated authorization? What is it and when is it used?
An unauthenticated authorization is when there is no separate identity context that can be used to determine authorization. Only the information or credentials that are included in the authorization request are used to determine authorization. What transaction would use such an unauthenticated authorization? It would be THE transaction that creates the identity context. In short, the sign-in transaction.
Think of that. In the most common case, an end-user provides a username and password to authorize the creation of an identity context. That identity context is usually a token or cookie that is passed around during the lifetime of a session to perform additional authenticated authorizations. Extending this concept further to MFA, MFA is just a means of aggregating multiple unauthenticated authorizations with the end result of “signing-in” and creating an identity context.
What about the term “re-authentication”? Re-authentication is when the identity context needs to be re-established, perhaps because it was lost, it expired, or it was explicitly revoked. Re-authentication can use the same means as the sign-in process — username/password, MFA, etc. Until the end-user re-authenticates, he doesn’t have an identity context and cannot perform any authenticated authorizations.
A “password reset” flow is just another channel to perform an unauthenticated authorization to establish an identity context; of course, once the identity context has been established, the system enforces the end-user to provide a new password to the system.
End-users sometimes confuse re-authentication with general authorization. This is because some applications request the same set of credentials as those used to sign-in. It is not, however, re-authentication, because the existing identity context remains valid. Take for example the popular eBay web site. You sign-in to establish an identity context. Within the application, if you want to edit your account information, you are prompted again for username/password. You can cancel that transaction (editing your account information), back out, and your original identity context is still valid as you move somewhere else in the application. What it was trying to do was perform an authorization on the transaction to edit account information, but not actually to re-authenticate. It just so happens that the authorization process asked for username/password. The authorization process could have used a different mechanism, such as challenge questions/answers, or an out-of-band mechanism like SecurID.