Tuesday, July 13, 2021

 

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