Sunday, September 29, 2013

Passive STS Java sample with WSO2 Identity Server

Following explain how to setup and test WSO2 Identity Server's Passive STS using a sample Relying Party (RP) application written in Java. For a .Net based app please refer [1] at WSO2 Library.

The sources of this application can be found at [2].
Application's .war file exist at [3]

WSO2 Identity Server 4.5.0 [4] and Tomcat 6 were used for testing. The application should work for other IS versions as well.

Configuring the Relying Party

In Sample web app's web.xml...

1. for 'idpUrl', give Identiy Server's Passive STS URL.
        i.e. https://localhost:9443/passivests

2.'replyUrl' should be the URL of the web app.
        i.e. http://localhost:8080/PassiveSTSSampleApp

3.'realm' should be an unique identifier for the web app.
        e.g. PassiveSTSSampleApp

4. Make 'displayFullResponse' "true" if you want to see the full RSTR (Request Security Token Response) returned from the IS.

Configuring Identity Server (IS)

1. In IS Management Console, go to 'Security Token Service'.


2. Go to 'Passive STS Configuration' in it.


3. Click 'Add new trusted service'.


4. For 'Service Realm Name' give the same identifier we gave as realm in Web app.

5. Select claims. These will be returned to the RP by the IS.


Execution Flow

1. From a web browser, try to access 'http://localhost:8080/PassiveSTSSampleApp'

2. This request will be intercepted by a Servlet Filter and browser will redirect to IS' Passive STS service.

3. Passive STS prompts the user for credentials.



4. After obtaining the credentials, it will construct the response with the selected claims.

5. Then, Passive STS redirects the browser to the RP.

6. Finally, web browser will display the response received by the IS.


Client Source Code

public class AuthFilter implements Filter {
    
    private String idpUrl;
    private String action; //wa
    private String replyUrl; //wreply
    private String realm; //wtrealm
    private String displayFullResponse;

    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
            
        if(request.getParameter("wresult") == null || request.getParameter("wresult").isEmpty()){
          String redirectUrl =  idpUrl + "?wa=" + action + "&wreply=" + replyUrl + "&wtrealm=" + realm;
          response.sendRedirect(redirectUrl);
          return;
        }
        
        String newLineRemovedStr = request.getParameter("wresult").replaceAll("(\\r|\\n)", "");
        handleResponse(request, newLineRemovedStr);
        
        if("true".equals(displayFullResponse)){
            String htmlSafeStr = escapeHtml(Utils.prettyFormat(newLineRemovedStr,2));
            request.getSession().setAttribute("RSTR", htmlSafeStr);
            request.getSession().setAttribute("displayFullResponse", "true");
        } else {
            request.getSession().setAttribute("displayFullResponse", "false");
        }
        
        chain.doFilter(request, response);
    }
    
    private void handleResponse(HttpServletRequest request, String response){
        OMElement element = null;
        String username = null;
        Map<String, String> claimMap = new HashMap<String, String>();
        
        try {
            element = AXIOMUtil.stringToOM(response);
        } catch (XMLStreamException e) {
            e.printStackTrace();
        }
        
        element = element.getFirstChildWithName(new QName("http://docs.oasis-open.org/ws-sx/ws-trust/200512", "RequestSecurityTokenResponse"));
        element = element.getFirstChildWithName(new QName("http://docs.oasis-open.org/ws-sx/ws-trust/200512", "RequestedSecurityToken"));
        element = element.getFirstChildWithName(new QName("urn:oasis:names:tc:SAML:1.0:assertion", "Assertion"));
        element = element.getFirstChildWithName(new QName("urn:oasis:names:tc:SAML:1.0:assertion", "AttributeStatement"));
        
        if(element != null){
            OMElement subjectElement = element.getFirstChildWithName(new QName("urn:oasis:names:tc:SAML:1.0:assertion", "Subject"));
            
            username = subjectElement.getFirstElement().getText();
            
            Iterator itr = element.getChildrenWithName(new QName("urn:oasis:names:tc:SAML:1.0:assertion", "Attribute"));
            
            while(itr.hasNext()){
                OMElement elem = (OMElement)itr.next();
                String claimURI = ((OMAttribute)elem.getAttribute(new QName("AttributeNamespace"))).getAttributeValue();
                claimMap.put(claimURI, elem.getFirstElement().getText()); 
            }
            
            request.getSession().setAttribute("message", "Response from the Passive STS for User: " + username);
            request.getSession().setAttribute("claimMap", claimMap);
        } else {
            request.getSession().setAttribute("message", "No claims received! Verify RP is registered at Passive STS");
            request.getSession().setAttribute("claimMap", null);
        }
    }

    public void init(FilterConfig fConfig) throws ServletException {
        //Initialize the configurations
        idpUrl = fConfig.getInitParameter("idpUrl");
        action = fConfig.getInitParameter("action");
        replyUrl = fConfig.getInitParameter("replyUrl");
        realm = fConfig.getInitParameter("realm");
        displayFullResponse = fConfig.getInitParameter("displayFullResponse");
    }
}

Ref:

[1] http://wso2.com/library/articles/2011/12/configuring-wso2-identity-server-passive-sts-aspnet-client/
[2] https://svn.wso2.org/repos/wso2/people/dulanja/samples/passive-sts/src/
[3] https://svn.wso2.org/repos/wso2/people/dulanja/samples/passive-sts/bin/
[4] http://wso2.com/products/identity-server/

1 comment:

Anonymous said...

Hi Dulanja,

My requirements for authentication and authorization are as follows:

1. In login page, i will enter the email id as a username and password. On click on login button, first it should retrieve the username from sqlserver (jdbc data source) using email id as input.

2. After retrieving the username, pass that username and password to read only Active directory to authenticate.

3. After successful authentication from AD, then retrieve the claims from SQLServer (JDBC User store).

For points 1 and 2, i have implemented CustomUserStoreManager. I have successfully authenticated.

Now point 3 where claim values should be retrieve from External DB, how i should modified the passive sts to set this claim value?

Thanks,
Ranjitsingh B.