Thursday, July 2, 2015

How to use SAML2 grant type to generate access tokens in web applications (Generate access tokens programatically using SAML2 grant type). - WSO2 API Manager

Exchanging SAML2 bearer tokens with OAuth2 (SAML extension grant type)

SAML 2.0 is an XML-based protocol. It uses security tokens containing assertions to pass information about an enduser between a SAML authority and a SAML consumer.
A SAML authority is an identity provider (IDP) and a SAML consumer is a service provider (SP).
A lot of enterprise applications use SAML2 to engage a third-party identity provider to grant access to systems that are only authenticated against the enterprise application.
These enterprise applications might need to consume OAuth-protected resources through APIs, after validating them against an OAuth2.0 authentication server.
However, an enterprise application that already has a working SAML2.0 based SSO infrastructure between itself and the IDP prefers to use the existing trust relationship, even if the OAuth authorization server is entirely different from the IDP. The SAML2 Bearer Assertion Profile for OAuth2.0 helps leverage this existing trust relationship by presenting the SAML2.0 token to the authorization server and exchanging it to an OAuth2.0 access token.

You can use SAML grant type for web applications to generate tokens.
https://docs.wso2.com/display/AM160/Token+API#TokenAPI-ExchangingSAML2bearertokenswithOAuth2(SAMLextensiongranttype)


Sample curl command .
curl -k -d "grant_type=urn:ietf:params:oauth:grant-type:saml2-bearer&assertion=&scope=PRODUCTION" -H "Authorization: Basic SVpzSWk2SERiQjVlOFZLZFpBblVpX2ZaM2Y4YTpHbTBiSjZvV1Y4ZkM1T1FMTGxDNmpzbEFDVzhh, Content-Type: application/x-www-form-urlencoded" https://serverurl/token

How to invoke token API from web app and get token programmatically.

To generate user access token using SAML assertion you can add following code block inside your web application.
When you login to your app using SSO there would be access you will get SAML response. You can store that in application session and use it to get token whenever requires.



Please refer following code for Access token issuer.

package com.test.org.oauth2;
import org.apache.amber.oauth2.client.OAuthClient;
import org.apache.amber.oauth2.client.URLConnectionClient;
import org.apache.amber.oauth2.client.request.OAuthClientRequest;
import org.apache.amber.oauth2.common.token.OAuthToken;
import org.apache.catalina.Session;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class AccessTokenIssuer {
    private static Log log = LogFactory.getLog(AccessTokenIssuer.class);
    private Session session;
    private static OAuthClient oAuthClient;

    public static void init() {
        if (oAuthClient == null) {
            oAuthClient = new OAuthClient(new URLConnectionClient());
        }
    }

    public AccessTokenIssuer(Session session) {
        init();
        this.session = session;
    }

    public String getAccessToken(String consumerKey, String consumerSecret, GrantType grantType)
            throws Exception {
        OAuthToken oAuthToken = null;

        if (session == null) {
            throw new Exception("Session object is null");
        }
// You need to implement logic for this operation according to your system design. some url
        String oAuthTokenEndPoint = "token end point url"

        if (oAuthTokenEndPoint == null) {
            throw new Exception("OAuthTokenEndPoint is not set properly in digital_airline.xml");
        }


        String assertion = "";
        if (grantType == GrantType.SAML20_BEARER_ASSERTION) {
    // You need to implement logic for this operation according to your system design
            String samlResponse = "get SAML response from session";
    // You need to implement logic for this operation according to your system design
            assertion = "get assertion from SAML response";
        }
        OAuthClientRequest accessRequest = OAuthClientRequest.
                tokenLocation(oAuthTokenEndPoint)
                .setGrantType(getAmberGrantType(grantType))
                .setClientId(consumerKey)
                .setClientSecret(consumerSecret)
                .setAssertion(assertion)
                .buildBodyMessage();
        oAuthToken = oAuthClient.accessToken(accessRequest).getOAuthToken();

        session.getSession().setAttribute("OAUTH_TOKEN" , oAuthToken);
        session.getSession().setAttribute("LAST_ACCESSED_TIME" , System.currentTimeMillis());

        return oAuthToken.getAccessToken();
    }

    private static org.apache.amber.oauth2.common.message.types.GrantType getAmberGrantType(
            GrantType grantType) {
        if (grantType == GrantType.SAML20_BEARER_ASSERTION) {
            return org.apache.amber.oauth2.common.message.types.GrantType.SAML20_BEARER_ASSERTION;
        } else if (grantType == GrantType.CLIENT_CREDENTIALS) {
            return org.apache.amber.oauth2.common.message.types.GrantType.CLIENT_CREDENTIALS;
        } else if (grantType == GrantType.REFRESH_TOKEN) {
            return org.apache.amber.oauth2.common.message.types.GrantType.REFRESH_TOKEN;
        } else {
            return org.apache.amber.oauth2.common.message.types.GrantType.PASSWORD;
        }
    }
}


After you login to system get session object and initiate access token issuer as follows.
AccessTokenIssuer accessTokenIssuer = new AccessTokenIssuer(session);

Then keep reference for that object during session.
Then when you need access token request token as follows. You need to pass consumer key and secret key.

tokenResponse = accessTokenIssuer.getAccessToken(key,secret, GrantType.SAML20_BEARER_ASSERTION);

Then you will get access token and you can use it as required.

5 comments:

  1. Hi Sanjeewa,

    Nice post !!,thanks

    but how does it works in this scenario.
    Scenario: subscribe APIs from store , and invoke them using a web application (web application will have consumer-key and consumer-secret configured)
    now my web application is public any one can register and send request to the APIs.as per your SAML2 grant type we can send the parameters as consumer-key and , consumer secret for getting the access token.
    here does consumer-key and consumer-secret same for all the users who have used my web application?? if so how does the token manager issues token?will that be same access token for all users until the token expires?

    ReplyDelete
  2. Answers for your questions,

    Q: here does consumer-key and consumer-secret same for all the users who have used my web application?
    A: Yes consumer key and secret will be same for given application. Once we selected required APIs for our application we will bundle them to single application in API Manager and get consumer and secret keys.

    Q: if so how does the token manager issues token?
    A: Even if consumer key secret same we will generate different tokens for different users. Based on user you will get different token. So each user will get different token.


    ReplyDelete
  3. Thanks Sanjeewa for your quick reply!!
    I have tried the scenario and i get to see the same access token for all the users who have login until token get expires. technically which is correct Token API will generate access token for the client credentials if it is not available/expires else the same token will give back , it doesn't no for which actual user it is issuing access token because we are not passing user information.
    i have tried with the client credentials authentication-consumer-key and consumer-secret alone i provided the input to get the access token. below is the example curl command which i tried.
    curl -i -H "Content-Type: application/x-www-form-urlencoded" -X POST "http://localhost:8280/token" -d "grant_type=client_credentials&client_id=GrUOQfVjnZQVgWHWqMGoIKnukOwa&client_secret=DYpu7y3Pr1PI0fcLH6CBjUyC2eca"

    My requirement is to create mobile banking app which will consume the APIs in WSO2 AM.

    1) I am not going to create users using WSO2 Am admin module and add users.because the actual users which my app is going to use is already registered with the bank and those details are present in other host system.
    2) I am going to publish an API in WSO2 AM which will validates actual end users credentials to validate the credentials.

    So in this scenario in order to invoke very first service i.e to validate login credentials API in WSO2 AM , i would need access token.

    If i use the above authentication type i am get to see same access token for all the users. how will i differentiate the users here. no idea whether it is correct or not.
    can you suggest your thoughts here h, how can i handle this situation??

    thanks in Advance.

    ReplyDelete
    Replies
    1. Hi Siva Kumari,

      You can let your users to login application with SAML and obtain SAML assertion. Then request password with SAML grant type(as described here https://docs.wso2.com/pages/viewpage.action?pageId=45944343). So you will get user specific token.

      For that you need to connect user store of bank to API Manager. So you dont have to create users from management console but you can see created users in API Manager. For that this(https://docs.wso2.com/display/AM190/Configuring+Secondary+User+Stores) article may help you.

      Thanks

      Delete
  4. Thanks a lot Sanjeewa for your quick response and clarification!!

    ReplyDelete