Implementing AAA
Introduction: Implementing
authentication, authorization, and auditing for software products.
Description: User interface
and application programming interfaces must be secured so that only
authenticated and authorized users can use the functionality. There are many
protocols, techniques and signin experiences which vary depending on the target
audience of the product just as much as the technology stack involved. This
article talks about some of the main considerations when designing identity and
authentication modules across a variety of products.
WebAPIs often use various authentication methods such as
basic authentication, token authentication, or even session authentication.
However, applications using the webAPIs may also require sessions to associate
the resources for the same user using the same user-agent* (defined in RFC
2616). For example, consider the Cart
API. To get the items in the shopping cart of a user the application may have to
use the GET method on the Cart API as follows:
GET Cart/ API
“X-Merchant-ID:
client_key”
“X-Session: <user session identifier>”
The application can also add or edit items in the
shopping cart or delete it. This example illustrates the need for the
application to associate an identifier for the scope of a set of webAPI calls.
This session identifier is usually obtained as a hash of
the session cookie which is provided by the authentication and authorization
server. The session identifier can be requested as part of the login process or
by a separate API Call to a session endpoint. An active session removes the
need to re-authenticate. It provides a familiar end-user experience and
functionality. The session can also be used with user-agent features or
extensions to assist with authentication such as password-manager or 2-factor
device reader.
The session identifier is usually a hash of the session.
It is dynamic in nature, for external use and useful only for identifying
something that is temporary. It can be requested based on predetermined client_secrets.
Sessions can time out or be explicitly torn down – either
by the user or by the system forcing re-authentication. Therefore, session
management must expire/clear the associated cookie and identifiers.
Sessions will need to be protected against csrf attack
and clickjacking just as they are for other resources.
Sessions are treated the same as credentials in terms of
their lifetime. The user for the session can be looked up as follows:
{HttpCookie cookie =
HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt(cookie.Value);
SampleIdentity id = new SampleIdentity(ticket);
GenericPrincipal prin = new GenericPrinicipal(id, null);
HttpContext.Current.User = prin;}
Micro-services and
their popularity are a demonstration of the power of APIs especially when the
callers are mobile applications and personal desktops. Many companies implement microservices as
NodeJS or Django web applications with OAuth and SSO. I have not seen the use
of CAS in companies, but I have seen it in educational institutions. Django for
instance brings the following functionality:
django - social auth - which makes social authentication
simpler.
Pinax - which makes it popular for websites.
django-allauth which integrates authentication,
addressing, registration, account management as well as 3rd party social
account.
django-userena which makes user accounts simpler
django-social registration which combines OpenID, OAuth
and FacebookConnect
django-registration which is probably the most widely
used for the framework
django-email-registration which claims to be very simple
to use and other such packages.
These implementations are essentially to facilitate the
user account registration via templated views and a database or other
membership provider backends.
There are other implementations as well such as
EngineAuth, SimpleAuth and AppEngine-OAuth-Library. EngineAuth does the
multiprovider authentication and saves the userid to a cookie.
SimpleAuth supports OAuth and OpenID. AppEngine-OAuth now
provides user authentication against third party websites.
NodeJS style
implementation evne allows the use of the providers as strategy in addition to
bringing some of the functionalities as described above for Django. If we look
at a ‘passport’ implementation for example, I like the fact that we can easily
change the strategy to direct against the provider of choice. In fact, the
interface is something that makes it quite clear.
Methods used are like
app.get('/login', function(req, res, next)) {
passport.authenticate('AuthBackendOfChoice', function
(req,res, next) {
:
etc.
Additional methods include:
var passport = require('passport') , OAuthStrategy =
require('passport-oauth').OAuthStrategy; passport.use('provider', new
OAuthStrategy({ requestTokenURL:
'https://www.provider.com/oauth/request_token', accessTokenURL:
'https://www.provider.com/oauth/access_token', userAuthorizationURL:
'https://www.provider.com/oauth/authorize', consumerKey: '123-456-789',
consumerSecret: 'shhh-its-a-secret' callbackURL:
'https://www.example.com/auth/provider/callback' }, function(token,
tokenSecret, profile, done) { User.findOrCreate(..., function(err, user) {
done(err, user); }); } ));
There seems to be little or no django-passport
implementation in the source repositories or for that matter any
python-passport implementation.
Netor technologies has a mention for something that's
same name and is also an interesting read.
For example, they create a table to keep the
application_info and user_info. The application_info is like the client in the
OAuth protocol. In that it keeps track of the applications as well as the user
information. The user information is keeping track of usernames and passwords.
The user_applications is the mapping between the user and the applications.
The authentication is handled using a Challenge Response
scheme. The server responds with the user's password salt along with a newly
generated challenge salt and a challenge id. The client sends back a response
with the hash resulting from hash(hash(password+salt) + challenge). These are
read by the server and deleted after use. There is no need to keep them.
The code for create user looks like this:
def create(store, user, application = None):
if
application is None:
application = Application.findByName(unicode('passport'))
result =
UserLogin.findExisting(user.userid, application.applicationid)
if result is
None:
result = store.add(UserLogin(user, application))
store.commit()
return result
and the authentication methods are handled in the
controllers. The BaseController has the method to get user login and the
ServiceController has the method to authenticate via a challenge.
This seems a clean example for doing a basic registration
of user accounts and integrating with the application.
Regarding audit, most membership providers and their
corresponding data stores can help turn on data capture and audit trails.
It is also possible to send tags and key-value pairs in
parameters to webAPI calls which significantly enhance any custom logic to be
built. For example,
[Fact]
public void TestPost3()
{
var httpContent
= new StringContent("{ \"firstName\": \"foo\" }",
Encoding.UTF8, "application/json");
var client =
new HttpClient();
var result =
client.PostAsync("http://localhost:17232/api/Transformation/Post3",
httpContent).GetAwaiter().GetResult();
}
[HttpPost]
[ActionName("Post3")]
public void Post3([FromBody]IDictionary<string,
string> data)
{
// do something
}
Conclusion: IAM is not a
canned component that can be slapped on all products without providing a rich
set of integrations and leveraging the technology stacks available. While some
vendors make it easy and seamless to use, the technology stack underneath is
complex and implements a variety of design requirements.
No comments:
Post a Comment