Showing posts with label JAAS. Show all posts
Showing posts with label JAAS. Show all posts

Saturday, September 24, 2016

JAAS vs AWS Security Policy vs RBAC vs FireBase vs ACL vs WTF ?

I've been thinking a bit about authentication and authorization lately, and while I may have my head semi-wrapped around authentication - authorization is kicking my ass a bit. Here's my understanding as of today.

First, there's something called "role based authorization" (RBAC) that pops up a lot, and is embodied in one way in the java EE specification. The basic idea behind RBAC is that a user (or a user group) is assigned a "role" that represents explicitly or implicitly a basket of permissions within an application. For example - an enterprise SAAS product that I work on at my day job defines "super user", "admin", and "standard" user roles under each customer's account. Each user is assigned a role that gives the user a certain basket of global permissions (this user can perform action X on any asset).

I was somewhat confused when I discovered that AWS defines an IAM role as a non-person user (or Principal in the JAAS world). It turns out that AWS implements something more powerful than the RBAC system I'm semi-familiar with - AWS builds up its security model around security policy specifications that can either be assigned to a user or group of users (like an RBAC role) or to an asset (like an ACL). When deciding whether a particular user may perform a particular action on a particular asset the AWS policy engine evaluates all applicable policies to come to a decision.

I have not worked at all with Google's Firebase, but it seems to implement a less powerful (compared to AWS IAM policies), but simpler access control mechanism that splits the difference between RBAC and ACL's via a policy specification that grants and restricts permissions on an application's tree of assets based on XPATH-like selection rules.

On thing I appreciate about Firebase's design is that it empowers the application developer with an access control mechanism, so if a team is building an application on Firebase, then the team's application can take advantage of Firebase's access control system to regulate access to the application's assets by the application's users.

On the other hand, The IAM policy tools in AWS provide a powerful mechanism for regulating what actions different team-members may take on AWS resources (S3 buckets, EC2 VM's, whatever) within a shared AWS account in which the team is deploying some application, but it's not clear to me how that team could leverage IAM's security policy engine within the team's own application to control how the application's (non-IAM) users may interact with the application's (non-AWS) assets. The AWS Cognito service seems to try to point in the direction of leveraging AWS policy within an application, but in an application space where business logic is implemented in the client, and the client interacts directly with the data-store. I'm still a member of the old school that thinks the client should implement UX, and access API's that implement the business logic and validation that precedes data manipulation.

Thursday, November 07, 2013

openID for JAAS

I finally have my little ToDo app wired up with openID authentication. I'm running an openid4java based relying party, littleId, on heroku that provides the client with a signed web token that the client can present as credentials to other services (yet to be developed!) that share the signing secret.

LittleId includes a JAAS (java authentication and authorization service) login module (littleware.apps.littleId.client.controller.JaasLoginModule) that a web service can plug into its authentication stack to verify littleId web tokens. A service must share the secret littleId signed the token with to verify the token. Currently littleToDo uses the token to authenticate with a do-nothing littleware service that uses this JAAS login configuration (login.config):

> cat ..\littleApps\fishRunner\login.config
littleware.login {
    /*... Stacked LDAP login module ...
        com.sun.security.auth.module.LdapLoginModule SUFFICIENT
        userProvider="ldap://XXXXX/dc=XXXXXX,dc=XXX"
        authIdentity="{USERNAME}"
        userFilter="(cn:dn:={USERNAME})"
        useSSL=false
        debug=true;
        */
        com.sun.jmx.remote.security.FileLoginModule SUFFICIENT
        passwordFile="passwords.properties";

        littleware.apps.littleId.client.controller.JaasLoginModule SUFFICIENT;
};

The openID flow is handled in the browser by a few javascript and typescript modules (on github), and a popup, openIdPop.html. The whole thing relies on CORS (cross origin resource sharing) with XDR cookies enabled (littleId is able to stay stateless on the server side by stashing some data in a cookie) to allow the popup (hosted on the same apps.frickjack.com domain as little-ToDo's assets) to access littleId's REST service running on heroku under littleware.herokuapp.com.

Anyway, the tricky stuff is all handled in the popup. The openIdPop.html popup is actually loaded twice during the openId flow. First, when the parent browser window opens the popup, the user selects the openId provider to authenticate with (currently Google or Yahoo). The selection-handler retrieves the parameters to post to the openID provider (via an XHR request to the littleId service), and submits the data to the provider (Yahoo or Google). After the user authenticates with the openID provider, then the provider redirects the browser back to the littleId service (openID relying party). LittleID verifies that the openID authentication succeeded, and generates a web token that it delivers back to openIdPop.html in the query parameters attached to the browser redirect URL. The code in openIdPop.html looks like this:

    littleware.littleYUI.bootstrap( {/*  classNamePrefix: 'pure'  */ } ).use( 
        'transition', 'littleware-auth-littleId', 'littleware-littleUtil',
        function (Y) {
            var util = Y.littleware.littleUtil;
            var log = new util.Logger("openIdPop.html");
            var littleId = Y.littleware.auth.littleId;

            littleId.helperFactory.get().then(
                function (loginHelper) {

                    // handle provider-selection event
                    Y.one("div#app").delegate('click',
                            function (e) {
                                e.preventDefault();
                                var providerName = e.currentTarget.getAttribute('data-provider');
                                log.log("Authenticating with provider: " + providerName);
                                loginHelper.prepareAuthRequest(providerName).then(
                                    function (request) {
                                        log.log("Check 1");
                                        var formBlock = loginHelper.buildProviderForm(request);
                                        Y.one("body").appendChild(formBlock);
                                        //log.log("Added form to body: " + formBlock.getHTML());
                                        formBlock.one( "form" ).getDOMNode().submit();  // exit to openId provider!
                                    },
                                    function (err) {
                                        alert("Error collecting auth data");
                                        log.log("Auth prep error");
                                        console.dir(err);
                                    }
                                );
                            }, "a.little-idprovider"
                     );

                    // handle auth data present in the URL paremeter if any ...
                    if ( littleId.CallbackData.isCallbackURL( window.location.href ) ) {
                        var callbackData = littleId.CallbackData.buildFromURL(window.location.href);
                        var message = "";
                        if (callbackData.authSuccess) {
                            message = "Authenticated as " + callbackData.userCreds.email +
                                ", secret: " + callbackData.userCreds.secret;
                        } else {
                            message = "Not authenticated";
                        }

                        // little bit of feedback
                        Y.one("div#app").setHTML("<p>" + message + "</p>");
                        
                        // if running as a popup - than call out to parent window, and close this popup
                        if (window.opener) {
                            log.log("Notifying parent window of openId callback ..." );
                            window.opener.littleware.auth.littleId.providerCallback(callbackData).then(
                                function () {
                                    log.log("Parent window callback ok");
                                    window.close();
                                },
                                function (err) {
                                    alert("Failed parent window callback");
                                    log.log("Failed parent window callback");
                                    console.dir(err);
                                }
                              );
                        }
                    }
                }
            );
        }
    );

Fortunately - the host application (little-ToDo) doesn't need to worry about that. The app just launches the popup to initiate an authentication workflow (
window.open("/littleware_apps/auth/openIdPop.html", "openid_popup");
), and listens for the littleId credentials attribute to change:

  littleIdHelper.after("userCredsChange",
      (ev) => {
          var creds = littleIdHelper.get("userCreds");
          ...
       }
  );

Hopefully some of that makes sense. It doesn't look like much, but I'm pretty happy, because this little authentication service ties together a bunch of things I've been working on into one almost coherent mess ( running services on heroku, hosting content on S3, openID, YUI modules with typescript, YUI promises and handlebars templates, responsive mobile-webapp UX ), and hopefully gives me a good pattern to follow for building some other services.