Thursday, December 27, 2012

A basic implementation of basic access authentication using JASPIC

Basic access authentication is a crude mechanism to authenticate that's part of the HTTP standard. It allows both an agent to send username/password credentials and a server to request the agent to authenticate itself. This happens in a simple but standardized way.

The mechanism can be easily implemented using Java EE's JASPIC and a sprinkle of utility code from the experimental OmniSecurity project (which is currently being discussed as one of the possible options to simplify security in Java EE 8).

A basic implementation looks as follows:

public class BasicAuthModule extends HttpServerAuthModule {
    
    @Override
    public AuthStatus validateHttpRequest(HttpServletRequest request, HttpServletResponse response, HttpMsgContext httpMsgContext) throws AuthException {
        
        String[] credentials = getCredentials(request);
        if (!isEmpty(credentials)) {
            
            UsernamePasswordIdentityStore identityStore = getReferenceOrNull(UsernamePasswordIdentityStore.class);
            if (identityStore != null) {
                if (identityStore.authenticate(credentials[0], credentials[1])) {
                    return httpMsgContext.notifyContainerAboutLogin(
                        identityStore.getUserName(), 
                        identityStore.getApplicationRoles()
                    );
                }                
            }            
        }
        
        if (httpMsgContext.isProtected()) {
            response.setHeader("WWW-Authenticate", "Basic realm=\"test realm:\"");
            return httpMsgContext.responseUnAuthorized();
        }
        
        return httpMsgContext.doNothing();
    }
    
    private String[] getCredentials(HttpServletRequest request) {
        
        String authorizationHeader = request.getHeader("Authorization");
        if (!isEmpty(authorizationHeader) && authorizationHeader.startsWith("Basic ") ) {
            return new String(parseBase64Binary(authorizationHeader.substring(6))).split(":");
        }
        
        return null;
    }
}
Full code in the OmniSecurity repo

Using injection, the example can be simplified a little and will then look as follows:

public class BasicAuthModule extends HttpServerAuthModule {

    @Inject
    private UsernamePasswordIdentityStore identityStore;
    
    @Override
    public AuthStatus validateHttpRequest(HttpServletRequest request, HttpServletResponse response, HttpMsgContext httpMsgContext) throws AuthException {
        
        String[] credentials = getCredentials(request);
        if (!isEmpty(credentials) && identityStore.authenticate(credentials[0], credentials[1])) {
            return httpMsgContext.notifyContainerAboutLogin(
                identityStore.getUserName(),
                identityStore.getApplicationRoles()
            );
        }
        
        if (httpMsgContext.isProtected()) {
            response.setHeader("WWW-Authenticate", "Basic realm=\"test realm:\"");
            return httpMsgContext.responseUnAuthorized();
        }
        
        return httpMsgContext.doNothing();
    }
    
    private String[] getCredentials(HttpServletRequest request) {
        
        String authorizationHeader = request.getHeader("Authorization");
        if (!isEmpty(authorizationHeader) && authorizationHeader.startsWith("Basic ") ) {
            return new String(parseBase64Binary(authorizationHeader.substring(6))).split(":");
        }
        
        return null;
    }
}

Note that the JASPIC auth module as shown here is responsible for implementing the client/server interaction details. Validating the credentials (username/password here) and obtaining the username and roles is delegated to an identity store (which can e.g. be database or LDAP based).

Arjan Tijms

No comments:

Post a Comment