Regional API Gateways with WSO2 API Manager

Modern enterprises are moving fast towards an API driven architecture to cater demanding needs of digital transformation. As a part of this initiative it is at times required to setup API Gateways at various regions in the world so that the Gateways can be localized to their respective APIs/Services.

Secured APIs require a credential for access. When setting up regional Gateways it is at times required to localize the credential so that a credential obtained from one region only becomes valid to that particular region. To cater this requirement we can come up with a solution where the tokens issued from a region are prefixed with a region identifier. The API Gateway in the particular region should only allow requests with tokens bearing the relevant prefix to pass through.

WSO2 API Manager comprises of 5 main components. All these run in a single JVM by default. It is technically possible to run each component on a separate JVM as well for scaling and security zone requirements. Out of these 5 components, the Gateway and Key Manager are the most important to us in this scenario. The Key Manager issues and validates tokens where the API Gateway proxies all requests of the target API/Service. The following diagram depicts the solution we’re trying to build. It assumes two regions as US-East and US-West and has a Gateway and Key Manager pair each on each region.

Prefixing Tokens

WSO2 API Manager has a Java class named OauthTokenIssuerImpl which can be extended to override the method named accessToken. This is the method that generates the access token string. We can override this method to prefix the token that is being generated with the region identifier. We can start the Key Manager process by expecting the region identifier as a system parameter. Following is the code snippet of the accessToken method.

@Override public String accessToken(OAuthTokenReqMessageContext tokReqMsgCtx) throws OAuthSystemException { 
    String regionID = System.getProperty(REGION_ID);      
if(log.isDebugEnabled()){
log.debug(“Region ID = “ + regionID);
}
String accessToken = UUID.randomUUID().toString();
return regionID != null ? regionID + accessToken : accessToken; }

The complete class/project is available as a Github Project [1]. In order to get this running you first need to build the project using maven and generate the jar file. This jar file needs to be added to the Key Manager under <APIM_HOME>/repository/components/lib.

The next steps is configuring the Key Manager to run the above class. You can do this by setting the <IdentityOAuthTokenGenerator>to bear the name of the class you just implemented. This can be specified as per the following configuration on the <APIM_HOME>/repository/conf/identity/identity.xml file under the <OAuth> section.

<IdentityOAuthTokenGenerator>org.wso2.sample.tokengenerator.CustomTokenGenerator</IdentityOAuthTokenGenerator>

Please note that you need to restart the Key Manager once these steps have taken place.

Validating the token prefix at the Gateway

Next step is to get the Gateway in each region to only allow requests which have the appropriate token prefix to pass-through. The Gateway allows us to deploy a handler that is capable of intercepting requests on APIs to perform certain validations. We will be using such a handler to check if the token being sent to the Gateway bears the prefix that belong to the relevant region. The source code of the Gateway handler can be found in [2].

This handler class needs to extend from the org.apache.synapse.rest.AbstractHandler handler. And you need to override its handleRequest method to perform the validation. From within this method you need to iterate the list of headers and check if the “Authorization” header is of the form “Bearer <prefix><token>”. Following is the code snippet that does that.

String bearerToken = null;
for(String authzToken : authorizationHeader.split(",")){
if(authzToken.startsWith("Bearer")){
bearerToken = authzToken;
break;
}
}

if(bearerToken == null || bearerToken.split(" ")[1].startsWith(regionId)){
//No bearer token provided or the provided bearer token is of the expected region.
return true;
}

Once the code has been built you need to place the jar in the <APIM_HOME>/repository/components/lib directory of the API Gateway. And you need to configure the API Publisher in such a way that this new handler gets applied/configured for each API. You can do this by modifying the <APIM_HOME>/repository/resources/api_templates/velocity_template.xml file as below. The lines in bold text are the modifications we did to that part of the file.

#foreach($handler in $handlers)
<handler xmlns=”http://ws.apache.org/ns/synapse" class=”$handler.className”>
#if($handler.hasProperties())
#set ($map = $handler.getProperties() )
#foreach($property in $map.entrySet())
<property name=”$!property.key” value=”$!property.value”/>
#end
#end
</handler>
#if($handler.className == ‘org.wso2.carbon.apimgt.gateway.handlers.security.CORSRequestHandler’)
<handler class=”org.wso2.sample.token.validator.RegionValidator”/>
#end

#end

The above will ensure that all APIs you publish from here on wards will have this particular handler engaged so that your requests are validated for the relevant region prefix. You need to restart your API Manager after the above changes have been done.

To verify that the new handler is being applied properly, publish your API first and then navigate to <APIM_HOME>/repository/deployment/server/synapse-configs/default/api and open the xml file corresponding to the API you just created using a text editor. Scroll down to the <handlers> section and verify the new handler in place as below (in bold text)

<handlers>
.......
<handler class=”org.wso2.carbon.apimgt.gateway.handlers.security.CORSRequestHandler”> <property name=”apiImplementationType” value=”ENDPOINT”/>
</handler>
 <handler class=”org.wso2.sample.token.validator.RegionValidator”/>
.....
</handlers>

Conclusion

After all configuration changes have been done you need to start the servers by specifying their respective regions as a startup parameter. Ex: ./wso2server.sh -D__REGION__=”EAST”. This will ensure that all tokens created from that particular region bear the relevant prefix and the Gateway of that region will only allow to pass through tokens bearing that particular prefix belonging to the region.

[1] — https://github.com/nuwand/projects/tree/master/custom_token_generator/CustomTokenGenerator

[2] — https://github.com/nuwand/projects/tree/master/custom_token_generator/TokenRegionValidator