During the last days I have refined the DominoStatelessTokenServlet a little bit. It is now a pre-beta release, and I think it is time to explain some details about it. While it is still a proof-of-concept, it demonstrates how a stateless authentication can easily be implemented. A lot of testing is still required until it is ready for production use, but I think it provides really cool things for the domino environment.
First, it fixes the problematic 200er HTTP response code when an authentication was not successfull. Then it introduces a higher security level for web based applications, because the authentication token is only transferred in the HTTP headers: A CSRF attack as shown here is not possible anymore. The authentication works accross multiple servers / clusters, which can become interesting for example when you want to share the authentication securely between a Bluemix hosted application and your companies hosted infrastructure; the token is created from a server running in your company, and is then used to identify a user in the cloud-based application (It’s a real authentication, not a “misused authorization” like OAuth).
The token can also be safely stored in an mobile app: When the device gets lost, the user credentials are not compromised. And it allows to create different tokens for different applications for the same user (currently not implemented).
As a short demonstration, I have added a Angular JS example (with a hardcoded token) to show how it works in practise: An AJAX request is sent to the servlet, and the JSON response contains the name of the current Domino user.
The HTML page is not really complicated, it will just show the returned username:
<!doctype html> <html ng-app> <head> <title>Hello AngularJS</title> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script> <script src="hello.js"></script> </head> <body> <div ng-controller="Hello"> <p>Hello {{greeting.username}}!</p> </div> </body> </html>
The “Hello” controller which performs the AJAX request adds the “X-AUTH-TOKEN” to the HTTP headers of the request:
function Hello($scope, $http) {
// add a valid token to the headers
$http.defaults.headers.common['X-AUTH-TOKEN'] =
'MTQyNDI2OTE0NDgxNCFUVGVzdFVzZXIwMQ==!HyC1mnvvdaneLaW0Wn48kZ1MaTrdowr1e4nWBRWRX8Y=';
// load the data
$http.get('http://localhost/token/validate/').
success(function(data) {
$scope.greeting = data;
});
}
And this is the result:
The Token consist of two parts: The data part, and the Hmac hash of the data. Both are Base64 encoded, and when the data part is decoded, you can currently see the username and the timestamp of the token generation:
- Encoded
MTQyNDI2OTE0NDgxNCFUVGVzdFVzZXIwMQ==
- Decoded
1424269144814!TTestUser01
Because the data are hashed it is not possible to modify them. The timestamp is validated from the servlet; as soon it is too old, it is not longer valid.
To create a token, the servlet must be currently opened with the username and the password as URL parameters:
http://localhost/token/create/?username=TTestUser01&password=test01
In the response, the newly generated token is added to the HTTP headers:
In the servlet configuration (web.xml), a backend user is defined on whose behalf a lookup to the NAB is made to verify the provided HTTP password. The password for the hash is also stored there, and the maximum age of the token.