Protect your resources with the Bluemix App User Registry Add-on

15 August 2014
PDF (742 KB)
 
Photo of Xiu Lei Zhu

Xiu Lei Zhu

Software Enginner, IBM Bluemix Runtime

@acostry on Twitter

Sign up for IBM Bluemix
This cloud platform is stocked with free services, runtimes, and infrastructure to help you quickly build and deploy your next mobile or web application.

These days it is increasingly common for websites to offer services that tie together functionality from other sites. Some of these are great services, but what's not so great is that some implementations request your user name and password to the other site. When you agree to share your credentials like this, you are exposing your password to someone else and giving them full access to do anything they want, including changing your password and locking you out.

In this article, I will show you how to develop an OAuth-protected resource application in Bluemix.

OAuth 2.0 addresses this problem by letting users grant access to their private resources on one site to other sites, without exposing their credentials. For instance, OAuth is commonly used as a way for users to log into third-party websites using their Google, Facebook, or Twitter accounts without worrying about their access credentials being compromised. On the development side, however, OAuth lets client application developers interact with protected resources in a simple way.

In this article, I will show you how to develop an OAuth-protected resource application in IBM Bluemix. With Bluemix, you can develop your applications with OAuth by connecting the Bluemix App User Registry Add-on to your applications, and coding endpoints to enable authorization and authentication to secure your resources. The resource application consumes the Mongo service and the Bluemix App User Registry Add-on, and provides a REST service that other client applications can request to consume. The REST service is protected by the App User Registry Add-on, so the client applications must provide appropriate authorization information.

(Note: With few or no changes the application could use Cloudant instead of Mongo and get better QoS.)

I will also cover how to develop a client application that requires authorization during the request for protected resources. All authorization and authentication processes follow the OAuth 2.0 specification.

What you will need

 
  • A Bluemix ID, so you can use the App User Registry Add-on.
  • A basic familiarity with OAuth 2.0.
  • A J2EE development environment. I used Eclipse with the Rational Team Concert plug-in. Please note that the App User Registry Add-on supports only Java™ code.
  • Both the resource application code and the client application code (above buttons) downloaded to your local J2EE development environment.

Explanation of the code

 

In this section, we'll look at the code associated with the App User Registry Add-on.

1. How to get endpoints

 

The Bluemix App User Registry Add-on provides endpoints that can be used to perform authorization and authentication when using the App User Registry Add-on. These endpoints are acquired using the VCAP_SERVICES environment variable of the application. The following code snippet shows how to get these endpoints. You can get the complete version from com.bluemix.bankacct.lib.ConfigParms.java.

publicfinalstatic String[] getSecurityEndpoints() {

		String[] securityEndpoints = new String[3];
		String vcap_services = System.getenv("VCAP_SERVICES");

		JSONObject vcap_service_obj = null;
		try {
			vcap_service_obj = JSONObject.parse(vcap_services);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		JSONArray security_services = (JSONArray) vcap_service_obj.get("AppUserRegistry");
		JSONObject first_security = (JSONObject) security_services.get(0);
		JSONObject first_credential = (JSONObject) first_security.get("credentials");

		JSONObject authorize = (JSONObject) first_credential.get("authorize");
		String authorize_endpoint = (String) authorize.get("endpoint");
		
		JSONObject getAccessToken = (JSONObject) first_credential.get("getAccessToken");
		String getAccessToken_endpoint = (String) getAccessToken.get("endpoint");
		
		JSONObject checkToken = (JSONObject) first_credential.get("checkToken");
		String checkToken_endpoint = (String) checkToken.get("endpoint");
		
		securityEndpoints[0] = getAccessToken_endpoint;
		securityEndpoints[1] = authorize_endpoint;
		securityEndpoints[2] = checkToken_endpoint;

		return securityEndpoints;
	}

A sample VCAP_SERVICES of the App User Registry Add-on looks like this:

{
  "AppUserRegistry": [
    {
      "name": "App User Registry-xx",
      "label": "AppUserRegistry",
      "plan": "free",
      "credentials": {
        "authorize": {
          "method": "ALL",
          "endpoint": " ${appuserregistry_service_url}/oauth/authorize"
        },
        "getAccessToken": {
          "method": "ALL",
          "endpoint": "${appuserregistry_service_url}/oauth/token"
        },
        "checkToken": {
          "method": "ALL",
          "endpoint": "${appuserregistry_service_url}/check_token"
        },
        "getKey": {
          "method": "GET",
          "endpoint": "${appuserregistry_service_url}/token_key"
        }
      }
    }
  ]
}

2. How to get tokens for client apps

 

In this sample app, the grant type we use is authorization_code, so the client needs to get a valid token before it can be allowed to access the protected resources. To request a valid token, we must first get an authorization code. Making a GET request to authorize_endpoint with appropriate parameters will get an authorization response with a code value appended.

A sample GET request looks like this:

Click to see code listing

GET /authorize?response_type=code&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1 
Host: server.example.com

A sample authorization response looks like this:

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA

With an authorization code, we can request a token from the authentication server. The code snippet shown below gets both an access token and a refresh token. You can get the complete version from BankAdminclient/com.bluemix.bankacct.front.GetToken.java.

// code --- authorization code
public String[] getToken(String code) throws IOException {

		DefaultHttpClient httpClient = Utils.createHttpClient();
		String tokens[] = new String[2];

		try {
			String endpointToken = ConfigParms.GET_ACCESS_TOKEN_REQ_CODE_PARAM + "&"
					+ "code=" + code + "&" + ConfigParms.REDIRECT_URI;
			
			HttpPost accessTokenReq = new HttpPost(ConfigParms.OAUTH20_TOKEN_URI);
			HttpEntity reqEntity = new ByteArrayEntity(
					endpointToken.getBytes(ConfigParms.DEFAULT_ENCODE));

			accessTokenReq.setHeader(ConfigParms.DEFAULT_CONTENT_TYPE_HEADER);
			accessTokenReq.setHeader(ConfigParms.DEFAULT_ACCEPT_HEADER);

			Credentials clientCredentials = new UsernamePasswordCredentials(
					ConfigParms.CLIENT_ID, ConfigParms.CLIENT_SECRET);
			accessTokenReq.addHeader(BasicScheme.authenticate(
					clientCredentials, ConfigParms.DEFAULT_ENCODE, false));
			
			accessTokenReq.setEntity(reqEntity);
			
			HttpResponse accessTokenResp = httpClient.execute(accessTokenReq);
			tokens = Utils.readTokensFromResponse(accessTokenResp);
			
		} catch (ClientProtocolException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} finally {
			httpClient.getConnectionManager().shutdown();
		}
		return tokens;
	}

3. How to check tokens in resource apps

 

Before allowing the client app get the protected resources, the resource app needs to check the access token the client app provided. If the access token is valid, access is granted. If not, the client app request is rejected. The check_token code is shown below. You can get the complete version from BankAccountResource/com.bluemix.bankacct.resource.CustomerResource.java.

Click to see code listing

              JSONObject object = new JSONObject();
		String scopesStr = null;
		String username = null;
		String correctUsername = ConfigParms.USERNAME;
		ArrayList<?> scopesList = null;
		DefaultHttpClient httpClient = Utils.createHttpClient();
		HttpResponse accessTokenResp = null;
			try {

                           // Making check token request to Oauth server

				String CHECK_TOKEN_PARM = "token=" + access_token;
				HttpPost accessTokenReq = new HttpPost(ConfigParms.OAUTH20_CHECK_TOKEN_URI);
				HttpEntity reqEntity = new ByteArrayEntity(
					CHECK_TOKEN_PARM.getBytes(ConfigParms.DEFAULT_ENCODE));
				accessTokenReq.setHeader(ConfigParms.DEFAULT_CONTENT_TYPE_HEADER);
				accessTokenReq.setHeader(ConfigParms.DEFAULT_ACCEPT_HEADER);

				Credentials clientCredentials = new UsernamePasswordCredentials(
						ConfigParms.RESOURCE_ID, ConfigParms.RESOURCE_SECRET);
				accessTokenReq.addHeader(BasicScheme.authenticate(
						clientCredentials, ConfigParms.DEFAULT_ENCODE, false));

				accessTokenReq.setEntity(reqEntity);
				accessTokenResp = httpClient.execute(accessTokenReq);
				
                              // Checking if the response is valid, if true, we need to read the useful information contained in this token.

				if (accessTokenResp.getStatusLine().toString().contains("200")) {
				       String jsonStr = Utils.getContentFromResponse(accessTokenResp);
					Map<String, ArrayList> respMap = Utils.getObjectFromJSONResponse(jsonStr, Map.class);
					scopesList = respMap.get(Utils.KEY_SCOPES);
					scopesStr = scopesList.toString();					
					Map<String, String> respMap2 = Utils.getObjectFromJSONResponse(jsonStr, Map.class);
			        username = respMap2.get(Utils.KEY_USERNAME);					
				} else {
					System.out.println("Your access_token is invalid");
					returnnull;
				}
			} catch (ClientProtocolException e) {
				throw e;
			} catch (IOException e) {
				throw e;
			} finally {
				httpClient.getConnectionManager().shutdown();
			}
	
               //After getting a valid token, we still need to check if the information contained in the token is exactly what we need.

		if (correctUsername.equals(username)&&scopesStr.contains(ConfigParms.SCOPE)) {
			System.out.println("You access_token is valid");			
			serviceMongo = new CustomerServiceMongoImpl();
			tempCustomer = serviceMongo.getCustomer(customerName);
			if (tempCustomer == null) {
				System.out.println("getCustomer : No customer info retrieved");
				return Response.ok().build();
			}
			try {
				object.put("tempCustomer", tempCustomer);
			} catch (JSONException e) {
				e.printStackTrace();
			}
			return Response.ok().entity(object.toString()).build();
		} else {
			System.out.println("Your access_token is invalid");
			returnnull;
		}

In the above code, we first check the access token's validity, then make sure the resource owner and request scope are the right ones to map to the protected resources.

You probably have some questions at this point, so let's walk through the process of building this application on Bluemix step by step.

Working with the applications step by step

 

The resource app is named BankAccountResource. In this application, we provide some Bank User account information and expose it as a REST service that is protected by the App User Registry Add-on. We need to bind MongoLab and the App User Registry Add-on to this application before we can run it.

The client app is named BankAcoountClient. In this application, we will make a request to search the Bank User account information and provide appropriate authorization information. We need to bind the App User Registry Add-on to this application before we can run it.

Step 1. Get started

 
  1. Log in to Bluemix.
  2. Install the Cloud Foundry Command Line Tool.

Step 2. Connect the App User Registry Add-on

 
  1. In your DASHBOARD, click Connect an Add-on, then select App User Registry.
  2. Specify Space as dev and App as Leave unbound. Click CREATE.
  3. On the App User Registry Add-on page, click Add a User.

    Click to see larger image

  4. Fill in your user information and click Add.

Step 3. Create the resource application

 
  1. In your DASHBOARD, click APPS, then click Create an Application. Select Liberty for Java.
  2. In this sample, we set Name as BankAccountResource and Host as BankAccountResouce.mybluemix.net. These values must be unique, so you'll need to specify values not being used by other apps.

    Please note that if you make changes here, you'll need to update BankAccountResource/com.bluemix.bankacct.lib.ConfigParms.java and BankAccountClient/com.bluemix.bankacct.lib.ConfigParms.java with the following line:

    publicfinalstatic String RESOURCE_CONTEXTROOT = "http://BankAccountResource" + "." + CLOUDHOST;
  3. Click the resource application created in last step to go to the application OVERVIEW page. Click ADD A SERVICE.

    Click to see larger image

  4. Select the app name and click CREATE.
  5. On the application OVERVIEW page, click the connected App User Registry Add-on to configure this application as a resource provider application.

    Click to see larger image

  6. Click the Resource Server Configuration tab. Select ON for the Resource Server Enabled switch. Make sure your configuration specifies the values below, then click SAVE.
    • Scope: bluemix.scope
    • Resource Server: test
    • Secret: pass4icap
    • Resource Owner: BankAdmin

    Click to see larger image

Step 4. Create the client application

 
  1. In the APPS section, click Create an Application. Select Liberty for Java and click it.
  2. In this sample, we set Name as BankAccountClient and Host as BankAccountClient.mybluemix.net. These values must be unique, so you'll need to specify values not being used by other apps.

    Please note that if you make changes here, you'll need to update BankAccountResource/com.bluemix.bankacct.lib.ConfigParms.java and BankAccountClient/com.bluemix.bankacct.lib.ConfigParms.java with the following line:

    public final static String CLIENT_CONTEXTROOT = "http://BankAccountClient" + "." + CLOUDHOST;
  3. Click on the client application created in last step to go to the application OVERVIEW page.
  4. Click Connect an Add-on and connect the App User Registry Add-on.
  5. On the application OVERVIEW page, click the connected App User Registry Add-on to configure this application as a client application.
  6. Click the Client Application Configuration tab. Select ON for the Client Application Enabled switch. Make sure your configuration specifies the values below, then click SAVE.

    Click to see larger image

Step 5. Deploy the applications

 
  1. After downloading the resource app code and client app code to local and making some changes based on the above steps, export the two projects to BankAccountClient.war and BankAccountResource.war.
  2. Using CLI, deploy the resource application with the following command.
        cf push BankAccountResource -p BankAccountResource.war

    If you modified the app name in an earlier step, the command will be:

    cf push <appname> -p BankAccountResource.war
  3. On the BankAccountResource application OVERVIEW page, click Add A Service to bind a MongoLab service.
  4. Using CLI, deploy the client application with the following command.
        cf push BankAccountClient -p BankAccountClient.war

    If you modified the app name in an earlier step, the command will be:

        cf push <appname> -p BankAccountClient.war

Step 6. Access the applications

 
  1. Access the resource application with http://BankAccountResource.mybluemix.net to populate the database to Mongo. This data is protected by the App User Registry Add-on.
  2. On the resource application home page, click Populate DataBase.
  3. Access the client application with http://BankAccountClient.mybluemix.net. The page shown below should be displayed.

    Click to see larger image

  4. You can click either picture to make a request for the protected resources. The only difference is whether you access the protected resources with or without the request scope. For example click the one on the left to bring up the Sign in page:
  5. Sign in with BankAdmin/pass4icap and you will get the authorization page. Click Ok to complete the authorization process, then you will get the protected resources.

    Click to see larger image

Conclusion

 

Now that we have successfully created and run these two applications, you should have a better understanding of the App User Registry Add-on. You may have noticed that the values we used for configuration are all defined in the resource and client applications. If you look into the code, you will find more clues about these sample apps. For more information about the App User Registry Add-on, including useful endpoints we did not cover, please see the Bluemix Documentation.

Add a comment

Note: HTML elements are not supported within comments.


1000 characters left

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Cloud computing
ArticleID=980457
ArticleTitle=Protect your resources with the Bluemix App User Registry Add-on
publish-date=08152014