Thursday, January 30, 2014

Invoking APIs using a Web App with OAuth2 and use of JWT - WSO2 API Manager

In this post I am to share my experience and understandings using WSO2 API Manager(API-M) for a very common and useful scenario in the industry. 

In brief following is the flow.

An API is exposed for app developers to be used under the control of API Manager (which adds access control for the API). Then app developers make their apps consuming those APIs. After development and testing is completed they make it available for end users at App store. The end users can then get registered in the store and use the apps with own credentials. The app will provide the desired services calling the APIs it has subscribed to.

The above scenario is well demonstrated in WSO2 API-M with the pizza shack example explained in the documentation at [1].




For clarity I will be including the steps in brief. For detailed steps we can refer documentation at [1]. 

API Developer Role

  • We deploy the back-end services related to 'pizza ordering' in WSO2-Application server or any other desired application server. (Download the code from API-M samples svn, build using Maven3 and deploy it in WSO2 AS. If we check the WADL, we can check the resources it exposes. Note the endpoint URL.)


  • Then we publishes these services as APIs in WSO2 API-M Publisher, so that they will be available in API-M Store (Login to API-M Publisher, in default pack https://localhost:9443/publisher and publish the APIs as guided in the sample doc. We should make sure the production endpoint URL  matches with what we observed in the first step). 

App Developer Role

  • Now here comes an App developer who wish to develop an App to order Pizza. He/she can register this App in store and get subscribed to these APIs that are required for development of application. So this APP developer will be consuming the services exposed by the APIs published by the previous developer. The code for the pizza ordering sample web app can be downloaded from svn as well.




  • At subscription he/she gets consumer secret and consumer key which are then used to request OAuth tokens to access the APIs (In this example we use user name and password which is required in grant type 'password'. There are several other possible grant types as well, if we don't want to send password).


Get the consumer key and secret from 'My Subscriptions'.

             

  • Developer embeds the consumer key and consumer secret into the Pizza ordering application (In most of the cases in web.xml).
    <context-param>
        <param-name>consumerKey</param-name>
        <param-value>FyfSK4RNHqGETmnNkaI87hIoNFQa</param-value>
    </context-param>
    <context-param>
        <param-name>consumerSecret</param-name>
        <param-value>1NFr7jb8JBA3IFa6gkjoN_PoYAca</param-value>
    </context-param>

At this point we can check how the token works with a simple curl command as follows. Provide the access token taken from above UI.
curl -k -H "Authorization: Bearer <access_token>" https://localhost:8245/pizzashack/menu/1.0.0


 which will return the menu details for pizza orering as follows,

[{"price":"13.99","icon":"/images/6.png","description":"Grilled white chicken, hickory-smoked bacon and fresh sliced onions in barbeque sauce","name":"BBQ Chicken Bacon"},{"price":"24.99","icon":"......................:"/images/5.png","description":"Rich and creamy blend of spinach and garlic Parmesan with Alfredo sauce","name":"Spinach Alfredo"},{"price":"15.99","icon":"/images/4.png","description":"Six cheese blend of mozzarella, Parmesan, Romano, Asiago and Fontina","name":"Tuscan Six Cheese"}]

Since we have already seen the access token we can use it here. But when an end user comes and tries to order pizzas he/she is not seen these. Also this token is related with USER_TYPE: APPLICATION which has more privileges than an end user, so we can't anyway let the user use it. So what is happening underneath is generating a separate token for end users, using the embedded consumer key/secret and end user entered credentials(if password grant type is used) which will be related with USER_TYPE: APPLICATION_USER.

End User


So here comes the end user who get registered in the App Store.
Then end users can use the application to order pizzas online, entering their credentials in the application at http://localhost/pizzashack.


The API-M sitting in the middle act as the Authorization server in the scenario, managing the usage of the exposed APIs.

So where does the JWT assertion comes into play?

JWT assertion is a format used to send the details of the end user who invoked the API. Just as a SAML assertion would carry user claims JWT also carries user claims in JSON notation. We can find more details on this at [2]. This is used to pass those details to the back-end service which might require them for monitoring or some other purpose. The default JWT token will be as follows.
{
   "iss":"wso2.org/products/am",
   "exp":1391029971429,
   "http://wso2.org/claims/subscriber":"admin",
   "http://wso2.org/claims/applicationid":"1",
   "http://wso2.org/claims/applicationname":"DefaultApplication",
   "http://wso2.org/claims/applicationtier":"Unlimited",
   "http://wso2.org/claims/apicontext":"/pizzashack/menu",
   "http://wso2.org/claims/version":"1.0.0",
   "http://wso2.org/claims/tier":"Bronze",
   "http://wso2.org/claims/keytype":"PRODUCTION",
   "http://wso2.org/claims/usertype":"APPLICATION",
   "http://wso2.org/claims/enduser":"admin",
   "http://wso2.org/claims/enduserTenantId":"-1234"
}

Cheers!

Ref:




8 comments :

  1. Hi pushpalanka, nice post.

    I try to follow the steps with AM port offset = 3 and I cannot login in the webapp, in AM I see a message like this in console:

    Connection refused or failed for : localhost/127.0.0.1:9443 {org.apache.synapse.transport.passthru.ConnectCallback}

    So in some place it´s 9443 the default port for something. any idea about this??

    Thanks a lot.

    ReplyDelete
  2. Hi Jorge,

    Thanks! What is the error you get for Webapp login at AS?

    ReplyDelete
  3. I work with:

    AS with port offset = 2
    AM with port offset = 3
    BAM with port offset = 0

    In the AS console I don´t see any error. In the webapp UI I see the message that I cannot login with admin/admin.

    In AM console with offset = 3 I see the error that I put in my first comment, If I put port offset=0 the error disappear and all work just fine.

    After a while, I cannot find a solution so change the port offset again:

    AS with port offset = 2
    AM with port offset = 0
    BAM with port offset = 1

    AM start to work OK, so all this scenario work just fine, but I faced a problem in BAM with the new port offset, and in this case I can find where to change the cassandra port to 7161 and finally I can see the AM statistics

    I think that in BAM and AM documentation we need some kind of guide related to what to do when we change the port offset, what others changes we need must do in the config files.

    ReplyDelete
    Replies
    1. Thanks for sharing your findings Jorge. I will look into this.

      Delete
    2. Hi Jorge,

      I couldn't replicate what you experienced in API-M.
      For BAM, yes, the Cassandra port is not affected by the port offset setting and we have to configure it. This is because, the Cassandra can reside externally as well, where the given port is taken as it is, despite BAM server's internal configuration.

      It will be of help, if you can share the API-M you are using as well.

      Delete
  4. Hi,

    I see that there is an option to sign the JWT token that is being sent to backend service. Where can I configure the private /public key pair used to sign the token? And is there a code snippet to show how to validate the signature ?

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete
  6. Top web site, I hadn't come across idlemendacity.blogspot.com earlier during my searches!
    Keep up the superb work!

    ReplyDelete