com.pearson.pdn.learningstudio.oauth.OAuth2AssertionService.java Source code

Java tutorial

Introduction

Here is the source code for com.pearson.pdn.learningstudio.oauth.OAuth2AssertionService.java

Source

/*
 * LearningStudio RESTful API Libraries 
 * These libraries make it easier to use the LearningStudio Course APIs.
 * Full Documentation is provided with the library. 
 * 
 * Need Help or Have Questions? 
 * Please use the PDN Developer Community at https://community.pdn.pearson.com
 *
 * @category   LearningStudio Course APIs
 * @author     Wes Williams <wes.williams@pearson.com>
 * @author     Pearson Developer Services Team <apisupport@pearson.com>
 * @copyright  2014 Pearson Education Inc.
 * @license    http://www.apache.org/licenses/LICENSE-2.0  Apache 2.0
 * @version    1.0
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.pearson.pdn.learningstudio.oauth;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.URL;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
import java.util.TreeMap;

import javax.net.ssl.HttpsURLConnection;

import org.bouncycastle.crypto.engines.AESFastEngine;
import org.bouncycastle.crypto.macs.CMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.pearson.pdn.learningstudio.oauth.config.OAuth2AssertionConfig;
import com.pearson.pdn.learningstudio.oauth.request.OAuth2Request;

/**
 *   OAuth2 Assertion Service
 */
public class OAuth2AssertionService implements OAuthService {
    private final static String API_DOMAIN = "https://api.learningstudio.com";

    private OAuth2AssertionConfig configuration;

    /**
     * Constructs and OAuth2 assertion service
     * 
     * @param configuration   Configuration parameters for OAuth2
     */
    public OAuth2AssertionService(OAuth2AssertionConfig configuration) {
        this.configuration = configuration;
    }

    /**
     * Generates OAuth2 Assertion Request
     * 
     * @param username   Username for request
     * @return   OAuth2 request with assertion
     * @throws IOException
     */
    public OAuth2Request generateOAuth2AssertionRequest(String username) throws IOException {

        final String grantType = "assertion";
        final String assertionType = "urn:ecollege:names:moauth:1.0:assertion";
        final String url = API_DOMAIN + "/token";

        final String applicationId = configuration.getApplicationId();
        final String applicationName = configuration.getApplicationName();
        final String consumerKey = configuration.getConsumerKey();
        final String clientString = configuration.getClientString();
        final String consumerSecret = configuration.getConsumerSecret();

        OAuth2Request oauthRequest = null;
        HttpsURLConnection httpConn = null;
        JsonReader in = null;
        try {

            // Create the Assertion String
            String assertion = buildAssertion(applicationName, consumerKey, applicationId, clientString, username,
                    consumerSecret);

            // Create the data to send
            StringBuilder data = new StringBuilder();
            data.append("grant_type=").append(URLEncoder.encode(grantType, "UTF-8"));
            data.append("&assertion_type=").append(URLEncoder.encode(assertionType, "UTF-8"));
            data.append("&assertion=").append(URLEncoder.encode(assertion, "UTF-8"));

            // Create a byte array of the data to be sent
            byte[] byteArray = data.toString().getBytes("UTF-8");

            // Setup the Request
            URL request = new URL(url);
            httpConn = (HttpsURLConnection) request.openConnection();
            httpConn.setRequestMethod("POST");
            httpConn.addRequestProperty("User-Agent", "LS-Library-OAuth-Java-V1");
            httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            httpConn.setRequestProperty("Content-Length", "" + byteArray.length);
            httpConn.setDoOutput(true);

            // Write data
            OutputStream postStream = httpConn.getOutputStream();
            postStream.write(byteArray, 0, byteArray.length);
            postStream.close();

            long creationTime = System.currentTimeMillis();

            // Send Request & Get Response
            in = new JsonReader(new BufferedReader(new InputStreamReader(httpConn.getInputStream(), "UTF-8")));

            // Parse the Json response and retrieve the Access Token
            Gson gson = new Gson();
            Type mapTypeAccess = new TypeToken<Map<String, String>>() {
            }.getType();
            Map<String, String> ser = gson.fromJson(in, mapTypeAccess);
            String accessToken = ser.get("access_token");

            if (accessToken == null || accessToken.length() == 0) {
                throw new IOException("Missing Access Token");
            }

            oauthRequest = new OAuth2Request();
            oauthRequest.setAccessToken(accessToken);
            oauthRequest.setExpiresInSeconds(new Integer(ser.get("expires_in")));
            oauthRequest.setCreationTime(creationTime);

            Map<String, String> headers = new TreeMap<String, String>();
            headers.put("X-Authorization", "Access_Token access_token=" + accessToken);
            oauthRequest.setHeaders(headers);

        } finally {

            // Be sure to close out any resources or connections
            if (in != null)
                in.close();
            if (httpConn != null)
                httpConn.disconnect();
        }

        return oauthRequest;
    }

    /**
     * Builds an OAuth 2.0 assertion string
     * 
     * @param applicationName
     *            The name of the application brokering the token request
     * @param keyMoniker
     *            The assigned API key moniker
     * @param applicationID
     *            The assigned API application id
     * @param clientString
     *            The string value that uniquely identifies the campus/school
     * @param username
     *            The LearningStudio user's external identifier
     * @param secret
     *            The secret key used to sign the assertion string
     * 
     * @return A signed, encoded, and fully constructed assertion string
     * 
     * @throws UnsupportedEncodingException
     */
    private String buildAssertion(String applicationName, String keyMoniker, String applicationID,
            String clientString, String username, String secret) throws UnsupportedEncodingException {
        // Get the UTC Date Timestamp
        DateFormat gmtFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
        TimeZone gmtTime = TimeZone.getTimeZone("GMT");
        gmtFormat.setTimeZone(gmtTime);
        String timestamp = gmtFormat.format(new Date()) + "Z";

        // Setup the Assertion String
        String assertion = applicationName + "|" + keyMoniker + "|" + applicationID + "|" + clientString + "|"
                + username + "|" + timestamp;

        // Generate the CMAC used for Assertion Security
        String cmac = generateCmac(secret, assertion);

        // Add the CMAC to the Assertion String
        assertion = assertion + "|" + cmac;

        return assertion;
    }

    /**
     * Generates a HEX-encoded CMAC-AES digest
     * 
     * @param key
     *            The secret key used to sign the data
     * @param msg
     *            The data to be signed
     * 
     * @return A CMAC-AES digest
     * 
     * @throws UnsupportedEncodingException
     */
    private String generateCmac(String key, String msg) throws UnsupportedEncodingException {
        byte[] keyBytes = key.getBytes("UTF-8");
        byte[] data = msg.getBytes("UTF-8");

        CMac macProvider = new CMac(new AESFastEngine());
        macProvider.init(new KeyParameter(keyBytes));
        macProvider.reset();

        macProvider.update(data, 0, data.length);
        byte[] output = new byte[macProvider.getMacSize()];
        macProvider.doFinal(output, 0);

        return Strings.fromUTF8ByteArray(Hex.encode(output));
    }
}