Wso2ESB: Call Rest API through Entitlement policy from client producing CORS issue

563 views Asked by At

I am facing Cross origin issue when tried to call a ESB Rest API which has Entitlement policy. Please find the below client call, ESB API, and Handler.

  1. Ajax call:

 $.ajax({
         beforeSend: function(xhr) {
         xhr.setRequestHeader('Authorization', 'Bearer 5d8ce0224d82cca7fb55fdcf4015b67');
},
                  withCredentials: true,
               dataType: "json",
               type: 'GET',
               url: relativeURL+"/test,
                  },
                  success: function(json) 
                  {}});

  1. ESB API Configuration

<api xmlns="http://ws.apache.org/ns/synapse" name="CustomerApi"     context="/CustomerApi">
      <resource methods="POST GET OPTIONS DELETE PUT">
      <inSequence>
         <log level="custom">
            <property name="Message Flow" value="Customer Search API - IN">   </property>
            <property name="HTTP_METHOD IS###########" expression="$axis2:HTTP_METHOD"></property>
            <property name="ip address" expression="get-property('axis2','REMOTE_ADDR')"></property>
         </log>
         <property name="Authorization" expression="get-property('transport','Authorization')"></property>
         <property name="Access-Control-Allow-Credentials" value="true" scope="transport"></property>
         <property name="Access-Control-Allow-Headers" value="authorization,Access-Control-Allow-Origin,Content-Type,origin,accept,X-Requested-With" scope="transport"></property>
         <property name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS" scope="transport"></property>
         <property name="Access-Control-Allow-Origin" value="*" scope="transport"></property>
         <property name="xacml_use_rest" value="true" scope="axis2" type="STRING"></property>
         <property name="xacml_resource_prefix" value="/api/customers" scope="axis2"></property>
         <property name="xacml_resource_prefix_only" value="true" scope="axis2"></property>
         <property name="TIME_IN" expression="get-property('SYSTEM_TIME')" scope="default" type="LONG"></property>
         <log level="custom">
            <property name="Authorization.........." expression="get-property('transport','Authorization')"></property>
         </log>
         <entitlementService remoteServiceUrl="https://localhost:9444/services" remoteServiceUserName="admin" remoteServicePassword="enc:kuv2MubUUveMyv6GeHrXr9il59ajJIqUI4eoYHcgGKf/BBFOWn96NTjJQI+wYbWjKW6r79S7L7ZzgYeWx7DlGbff5X3pBN2Gh9yV0BHP1E93QtFqR7uTWi141Tr7V7ZwScwNqJbiNoV+vyLbsqKJE7T3nP8Ih9Y6omygbcLcHzg=" callbackClass="org.wso2.carbon.identity.entitlement.mediator.callback.UTEntitlementCallbackHandler" client="basicAuth">
            <onReject>
               <log level="custom">
                  <property name="Message Flow" value="REJECTED@ Dobbies"></property>
               </log>
               <property name="HTTP_SC" value="401" scope="axis2" type="STRING"></property>
               <payloadFactory media-type="xml">
                  <format>
                     <oatherizationresponse xmlns="">Not Authorized </oatherizationresponse>
                  </format>
                  <args></args>
               </payloadFactory>
               <respond></respond>
            </onReject>
            <onAccept>
               <log level="custom">
                  <property name="Message Flow" value="ACCEPTED@ Dobbies"></property>
               </log>
               <property name="Authorization" expression="fn:concat('Basic ', base64Encode('test:test'))" scope="transport"></property>
               <send>
                  <endpoint>
                     <address uri="/api/customers/"></address>
                  </endpoint>
               </send>
               <property name="TIME_OUT" expression="get-property('SYSTEM_TIME')" scope="default" type="LONG"></property>
               <script language="js">var time1 = mc.getProperty("TIME_IN");var time2 = mc.getProperty("TIME_OUT");var timeTaken = time2 - time1;mc.setProperty("RESPONSE_TIME", timeTaken);</script>
               <log level="custom">
                  <property name="Time Duration in ms:" expression="get-property('RESPONSE_TIME') "></property>
               </log>
            </onAccept>
            <obligations></obligations>
            <advice></advice>
         </entitlementService>
      </inSequence>
      <outSequence>
         <send></send>
      </outSequence>
       </resource>
      <handlers>
         <handler class="org.wso2.handler.SimpleOauthHandlerNew">
      </handler>
      </handlers
      </api>

  1. SimpleOauthHandlerNew:

package org.wso2.handler;
    import java.util.Map;

    import org.apache.axis2.client.Options;
    import org.apache.axis2.client.ServiceClient;
    import org.apache.axis2.context.ConfigurationContext;
    import org.apache.axis2.context.ConfigurationContextFactory;
    import org.apache.axis2.transport.http.HTTPConstants;
    import org.apache.axis2.transport.http.HttpTransportProperties;
    import org.apache.http.HttpHeaders;
    import org.apache.synapse.ManagedLifecycle;
    import org.apache.synapse.MessageContext;
    import org.apache.synapse.core.SynapseEnvironment;
    import org.apache.synapse.core.axis2.Axis2MessageContext;
     import org.apache.synapse.rest.AbstractHandler;
    import org.wso2.carbon.identity.oauth2.stub.OAuth2ServiceStub;
    import    org.wso2.carbon.identity.oauth2.stub.OAuth2TokenValidationServiceStub;
    import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationRequestDTO;
    import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationRequestDTO_OAuth2AccessToken;
    import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationResponseDTO;
 
    public class SimpleOauthHandlerNew extends AbstractHandler implements  ManagedLifecycle {
 
    private String securityHeader = HttpHeaders.AUTHORIZATION;
    private String consumerKeyHeaderSegment = "Bearer";
    private String oauthHeaderSplitter = ",";
    private String consumerKeySegmentDelimiter = " ";
    private String oauth2TokenValidationService = "oauth2TokenValidationService";
    private String identityServerUserName = "identityServerUserName";
    private String identityServerPw = "identityServerPw";
    private String oAuth2Service = "oauth2Service";
 
    @Override
    public boolean handleRequest(MessageContext messageContext) {
        try{
            ConfigurationContext configCtx = ConfigurationContextFactory.createConfigurationContextFromFileSystem(null, null);
            //Read parameters from axis2.xml
            String identityServerUrl = messageContext.getConfiguration().getAxisConfiguration().getParameter(oauth2TokenValidationService).getValue().toString();
            String username = messageContext.getConfiguration().getAxisConfiguration().getParameter(identityServerUserName).getValue().toString();
            String password = messageContext.getConfiguration().getAxisConfiguration().getParameter(identityServerPw).getValue().toString();
 
             OAuth2TokenValidationServiceStub stub = new OAuth2TokenValidationServiceStub(configCtx,identityServerUrl);
             
             String oauth2ServiceUrl = messageContext.getConfiguration().getAxisConfiguration().getParameter(oAuth2Service).getValue().toString();
             OAuth2ServiceStub oAuth2ServiceStub = new OAuth2ServiceStub(configCtx,oauth2ServiceUrl);
             
             
             ServiceClient client = stub._getServiceClient();
             Options options = client.getOptions();
             HttpTransportProperties.Authenticator authenticator = new HttpTransportProperties.Authenticator();
             authenticator.setUsername(username);
             authenticator.setPassword(password);
             authenticator.setPreemptiveAuthentication(true);
 
             options.setProperty(HTTPConstants.AUTHENTICATE, authenticator);
             client.setOptions(options);
             OAuth2TokenValidationRequestDTO dto = new OAuth2TokenValidationRequestDTO();
           // dto.set("bearer");
             Map headers = (Map) ((Axis2MessageContext) messageContext).getAxis2MessageContext().
                     getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
            String apiKey = null;
            if (headers != null) {
                apiKey = extractCustomerKeyFromAuthHeader(headers);
            }
            OAuth2TokenValidationRequestDTO_OAuth2AccessToken accessToken = new OAuth2TokenValidationRequestDTO_OAuth2AccessToken();
            accessToken.setTokenType("bearer");
            accessToken.setIdentifier(apiKey);
            dto.setAccessToken(accessToken);
            //validate passed apiKey(token)
            if (stub.validate(dto).getValid()) {
                String user = stub.validate(dto).getAuthorizedUser();
                System.out.println(">>>>>>>>>"+user);
                user = user.substring(0, user.indexOf('@'));
                System.out.println(">>>>>>>>>"+user);
                org.apache.axis2.context.MessageContext msgContext;
                Axis2MessageContext axis2Msgcontext = null;
                axis2Msgcontext = (Axis2MessageContext) messageContext;
                msgContext = axis2Msgcontext.getAxis2MessageContext();
                msgContext.setProperty("username", user);
                return true;
            }else{
                return false;
            }
           }catch(Exception e){
            e.printStackTrace();
            return false;
           }
          }
 
       public String extractCustomerKeyFromAuthHeader(Map headersMap) {
 
        String authHeader = (String) headersMap.get(securityHeader);
        if (authHeader == null) {
            return null;
        }
 
        if (authHeader.startsWith("OAuth ") || authHeader.startsWith("oauth "))       {
            authHeader = authHeader.substring(authHeader.indexOf("o"));
        }
 
        String[] headers = authHeader.split(oauthHeaderSplitter);
        if (headers != null) {
            for (int i = 0; i < headers.length; i++) {
                String[] elements =   headers[i].split(consumerKeySegmentDelimiter);
                if (elements != null && elements.length > 1) {
                    int j = 0;
                    boolean isConsumerKeyHeaderAvailable = false;
                    for (String element : elements) {
                        if (!"".equals(element.trim())) {
                            if (consumerKeyHeaderSegment.equals(elements[j].trim())) {
                                isConsumerKeyHeaderAvailable = true;
                            } else if (isConsumerKeyHeaderAvailable) {
                                return removeLeadingAndTrailing(elements[j].trim());
                            }
                        }
                        j++;
                    }
                }
            }
        }
        return null;
        }
 
       private String removeLeadingAndTrailing(String base) {
        String result = base;
 
        if (base.startsWith("\"") || base.endsWith("\"")) {
            result = base.replace("\"", "");
        }
        return result.trim();
       }
 
     @Override
     public boolean handleResponse(MessageContext messageContext) {
        return true;
     }
 
     @Override
     public void init(SynapseEnvironment synapseEnvironment) {
        
     }
 
     @Override
     public void destroy() {
        
     }
 }

In browser im getting

XMLHttpRequest cannot load /CustomerApi?limit=10&offset=0. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://172.250.46.250:8095' is therefore not allowed access.

Please provide me a solution to call the Rest end point from the client. Thanks in advance.

1

There are 1 answers

0
Damith Jinasena On

It's ages since this question is asked but not answered. I had the same issue and was stuck with it for few days and thought adding my findings.

One way to add CORS support is to expose your API via WSO2 API Manager and configure CORS support there https://docs.wso2.com/display/AM200/Enabling+CORS+for+APIs

With WSO2 EI, easiest way to add CORS support for your API is to set a HTTP header before the response is sent back.

Eg:

<header description="CORS" name="Access-Control-Allow-Origin" scope="transport" value="*"/>