OAuth 2.0 clients in Java programming, Part 3: Authorization code grant

Overview

OAuth is an open standard for authorization that lets clients obtain access to protected server resources on behalf of a resource owner. The resource owner could be a different client or the end user. OAuth also helps end users authorize third-party access to their server resources without having to share their credentials, such as user names and passwords. This series of articles adheres to the OAuth 2.0 authorization framework outlined in RFC6749. The complete OAuth 2.0 authorization framework as outlined under RFC 6749 can be found at the Internet Engineering Task Force website (see Resources).

Authorization grant

The authorization grant is a credential that represents the resource owner’s authorization that can be used to access a protected resource. This credential is used by the client to obtain an access token, and this access token is eventually sent along with the request to access a protected resource. OAuth 2.0 defines four grant types:

  1. Authorization code
  2. Implicit
  3. Resource owner password credentials
  4. Client credentials

This four-part article series takes you through the implementation of an OAuth 2.0 client in Java™ programming using each of the grant types listed above. In this third part, I explain how to implement the authorization code grant. The article explains this grant in detail and explains the sample client code that can be used to interface with any OAuth 2.0-compliant server supporting this grant. By the end of the article, you should have a complete understanding of the client implementation and be ready to download the sample client code for your own testing.

Authorization code grant

This grant is optimized for confidential clients and is used to obtain both access and refresh tokens. This is a redirection-based flow, and, as such, the client must be capable of interacting with the resource owner’s user agent (generally a web browser) and also must be capable of accepting incoming requests (through redirection) from the authorization server.

The authorization code grant is illustrated in Figure 1.

Figure 1. Authorization code flow

Flowchart showing the authorization code flow

The flow illustrated in Figure 1 includes the following steps:

  • (A) The client (typically, a web application) initiates the flow by directing the resource owner’s user agent (typically, a web browser) to the authorization endpoint. The client’s request includes the client identifier, requested scope, local state, and a redirection URI. The authorization server directs the user agent (typically, a web browser) back to the redirect URI after the access is granted (or denied).
  • (B) The resource owner authenticates with the authorization server through the user agent and grants or denies the client’s access request.
  • (C) If the resource owner grants access, the authorization server redirects the user agent (typically, a web browser) back to the client using the redirection URI provided earlier (in the request or during client registration). The redirection URI includes an authorization code and any local state provided by the client earlier.
  • (D) The client makes an access token request from the authorization server’s token endpoint by including the authorization code received in the previous step. When making the request, the client authenticates with the authorization server using the client credentials. The client also includes the redirection URI used to obtain the authorization code for verification.
  • (E) The authorization server authenticates the client. It validates the authorization code and ensures that the redirection URI received matches the URI used to redirect the client in step (C). If valid, the authorization server responds back with an access token and, optionally, a refresh token in case an offline access was requested.

Authorization code request

The authorization code request corresponds to steps (A) and (B) as described in Figure 1. In step (A), the client makes a request to the authorization server in the application/x-www-form-urlencoded format, as shown in Listing 1.

Listing 1. Example of an authorization code request
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

The request must contain the following parameters:

  • response_type: Required. The value must be set to code.
  • client_id: Required. The client ID.
  • redirect_uri: Required. Used for user agent redirection.
  • scope: Optional. The scope of the access request.
  • state: Optional. To maintain the state between the request and callback.

After the request is verified by the authorization server, the server sends an HTTP redirect code 302 response back to the client. The response will also include a redirection URI in the http Location header. In step (B), the client must redirect the user agent (typically, a web browser) to this URI. This redirection URI is usually a login page where the resource owner can sign in with their credentials and grant/revoke access to the client’s request.

Authorization code response

The authorization code response is shown in step (C) of Figure 1. If the resource owner grants the access request, the authorization server issues an authorization code. The authorization server redirects the user agent to the redirect URI provided as a part of the request in step (A) and includes the authorization code as a part of the query component of the redirection URI using the application/x-www-form-urlencoded format.

The URI parameters are as follows:

  • Code: Required. The authorization code generated by the authorization server. The code is temporary and must expire shortly after it was generated. The client must not use the authorization code more than once. Any further requests using the same code should be revoked by the authorization server. The authorization code is bound to the client identifier and the redirection URI.
  • State: Required. If the state parameter was present in the client’s authorization code request, this parameter must be set to the exact value received from the client.

Access token request

This corresponds to step (D) in Figure 1. The client makes a request to the token endpoint (authorization server) using the application/x-www-form-urlencoded format as shown in Listing 2.

Listing 2. Example of an access token request
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

             grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
             &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom&client_id=c342

The access token request must have the following parameters set:

  • grant_type: Required. The value must be set to authorization_code.
  • client_id: Required. The client ID.
  • client_secret: Optional. Secret. To authenticate with authorization server.
  • code: Required. The authorization code received from the server.
  • redirect_uri: Required. Identical to that sent in step (A).

The authorization server verifies that the code and redirect URI are valid. In the case of confidential clients, the authorization server also authenticates the client using its client credentials passed in the body of the request or in the authorization header.

Access token response

This corresponds to step (E) in Figure 1. If the access token request is valid and is authorized, the authorization server returns the access token in an access token response. An example of a successful response is shown in Listing 3.

Listing 3. Example of a successful access token response
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"Bearer",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

If the request is not valid or unauthorized, the authorization server returns an appropriate error message with code.

Refresh access token request

This is an optional step, which is applicable if the client requested offline access and was provided a refresh_token as a part of the access token request. An access token is temporary and usually expires after an hour. After the access token expires, the client would need to repeat the authentication process and the resource owner would need to log in and provide authorization to enable the client to make the access token request again.

If the client needs to refresh access tokens while the resource owner is not present at the browser to log in and authenticate, the client uses the offline access. The client can request an offline access while making the first authorization code request (see step (A)). Under this scheme, the authorization server returns a refresh token in addition to the access token. The refresh token is a long-living token that does not expire, unless it is explicitly revoked by the resource owner. Every time the access token expires, the client can use the refresh token to regenerate an access token without the resource owner needing to sign in and authorize the access request.

The client makes a request to the token endpoint (authorization server) using the application/x-www-form-urlencoded format, as shown in Listing 4:

Listing 4. Request to the token endpoint
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA

The request parameters are defined as follows:

  • grant_type: Required. The value must be set to refresh_token.
  • refresh_token: Required. This is retrieved earlier from access token request.
  • scope: Optional. The scope of the access request.

The authorization server verifies the refresh token and issues a new access token.

Refresh access token response

If the request is successful, the authorization server returns a new access token. An example of a successful response is shown in Listing 5.

Listing 5. Refresh access token response
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"Bearer",
  "expires_in":3600,
  "example_parameter":"example_value"
}

If the request is not valid or unauthorized, the authorization server returns an appropriate error message with code.

Setup

The sample Outh2.0 client is a dynamic web project. You can download a .war file containing the project and source code from Downloads. You import the .war file into your Eclipse environment.

Prerequisites

You need the Eclipse IDE for Java EE developers to set up your development environment and import the attached project. You can download Eclipse from the Eclipse downloads page.

You need the Apache Tomcat JSP/Servlet container to run the OAuth 2.0 web client. You can download Tomcat from the Apache Tomcat download page.

Dependencies

The client code project depends on the following JAR files:

  1. commons-codec-1.6.jar
  2. commons-logging-1.1.1.jar
  3. httpclient-4.2.5.jar
  4. httpclient-cache-4.2.5.jar
  5. httpcore-4.2.4.jar
  6. httpmime-4.2.5.jar
  7. json-simple-1.1.1.jar

The JAR files mentioned in points 1 to 6 can be found in the HttpComponents JAR file, which can be downloaded from the HttpComponents Downloads download page. The json-simple-1.1.1.jar file can be downloaded from the JSON Simple project. Make sure you copy these jars to the lib folder of your Apache Tomcat installation directory. Some of the needed JAR files may already be available by default in Tomcat, so you only need to copy the missing ones.

Import the project .war file into Eclipse

After you have Eclipse and Apache Tomcat installed, you need to import the .war file from the Downloads section. To import the war file, follow these simple steps outlined on the Eclipse website at “Importing Web archive (WAR) files.”

Make sure that you associate the Tomcat server with your project while importing the .war file into Eclipse. You need to select the Tomcat version and provide the path for the Tomcat installation root directory to complete the configuration.

You can get more detailed instructions on how to associate the Tomcat server with your project at “Creating a Tomcat server.”

After you successfully import the .war file into your Eclipse workspace, you will find the source code under Java Resources/src in the project hierarchy.

Run the OAuth 2.0 client

After the .war file has been successfully imported and Tomcat has been set up with the necessary JAR files, you can run the client by right clicking on the project name OAuth2.0_AuthCode and selecting Run As and Run on Server.

This deploys the client onto the server and loads the index.html page in Eclipse’s internal browser. Personally, I prefer to interact with the web client on an external browser.

You can access the web client from any browser by navigating to: http://localhost:8080/OAuth2.0_AuthCode.

Subsequent sections take you through the client code in detail and show you how to test this client with popular OAuth 2.0-compliant servers such as Salesforce, Google, and IBM.

OAuth 2.0 client code

The sample OAuth 2.0 client in this article implements the authorization code grant. The sample client code is a web application instead of a regular Java project, which was the case for the grant types discussed in the earlier articles. This is because the authorization code grant flow is meant to cater to web applications and is optimized for a user agent that is typically a web browser.

Input parameters

The input parameters for the client need to be supplied using the Oauth2Client.config properties file that is available under the src folder in the project. The input parameters are:

  • scope: This is an optional parameter. It represents the scope of the access request. The access token that is returned by the server has access to only those services mentioned in the scope.
  • state: This is an optional parameter. It is used to maintain a state between the client request and redirection response from the authorization server for the purpose ensuring the integrity of the client.
  • grant_type: This needs to be set to the authorization_code representing the authorization code grant type.
  • client_id: The client or consumer ID provided by the resource server when you register the app with it.
  • client_secret: The client or consumer secret provided by the resource server when you register the app with it.
  • access_token: The access token returned by the token endpoint in response to a valid and authorized access token request. The authorization code returned by the authorization server is exchanged for an access token.
  • refresh_token: The refresh token returned by the token endpoint in response to a valid and authorized access token request. The refresh token can be used to refresh an expired access token without needing the resource owner to be present for authentication once again. The client needs to explicitly request the refresh token from the server.
  • redirect_uri: The URI to which the authorization server will redirect the user agent as a part of the authorization code request. The authorization code is sent to this URI.
  • authentication_server_url: This represents the authorization server. All requests for obtaining an authorization code need to be sent to this URL.
  • token_endpoint_url: This represents the token endpoint. All requests for obtaining access tokens and refreshing access tokens through the refresh token request need to be sent to this URL.
  • resource_server_url: This represents the URL of the resource server that needs to be contacted to access a protected resource by passing to it the access token in the authorization header.
  • approval_prompt_key: The property name that is used by the authorization server to define the approval prompt condition. Generally, each authorization server (Salesforce, Google, IBM, and so on) has a custom property that needs to be passed in as a part of the authorization code request to signify whether the client wants to enforce the approval prompt for each request. The property name for Google is approval_prompt. For Salesforce, it is login consent.
  • access_type_key: The property name that is used by the authorization server to define the access type condition. Generally, each authorization server (Salesforce, Google, IBM, and so on) has a custom method by which the client can convey that it wants a refresh token to be issued along with the access token as a part of the access token request. Google does this by providing the access_type property. Salesforce requires you to enter the value refresh_token as a part of the scope.
  • access_type_value: The value for the access_type_key property. For Google, you need to pass in the value offline for the server to include the refresh token in addition to the access token.

Figure 2 shows the index.html page of the sample client code. You should see this page after you have successfully configured the project in Eclipse and Deployed it to Tomcat.

Figure 2. Test client home page

Screen image of the OAuth 2.0 index page

The client exposes two fundamental operations that you need to understand regarding OAuth 2.0. First, the client shows how to get an access token from the server. Second, the client shows how to use an existing access token to access a protected resource from the server.

To run the client:

  • First, put all required values in the Oauth2Client.config file.
  • Click Start Test for Get Access token. An HTTP GET request is sent to the OAuth2Client servlet, which is accessible at the following URI http://localhost:8080/OAuth2.0_AuthCode/handler.
  • The user interface code passes in the following query parameter as a part of the call to the OAuth2Client servlet: caller=token (access token request), caller=resource (access protected resource request).

The client code in Listing 6 is an excerpt from the OAuth2Client class’s doGet method.

Listing 6. Excerpt from the sample client’s doGet method
String caller = request.getParameter(OAuthConstants.CALLER);
String code = request.getParameter(OAuthConstants.CODE);
                
//Load the properties file
Properties config = OAuthUtils.getClientConfigProps(OAuthConstants.CONFIG_FILE_PATH);
                                
//Generate the OAuthDetails bean from the config properties file
OAuth2Details oauthDetails = OAuthUtils.createOAuthDetails(config);
                                
//Validate Input
List<String> invalidProps = OAuthUtils.validateInput(oauthDetails);
        if(invalidProps!=null && invalidProps.size() == 0){
                //Validation successful
                        
                if(OAuthUtils.isValid(caller)){
                        //Request was sent from web application.
                        //Check type of request
                        if(caller.equalsIgnoreCase(OAuthConstants.TOKEN)){
                                //Request for Access token
                                oauthDetails.setAccessTokenRequest(true);
                                String location =                                       
                                OAuthUtils.getAuthorizationCode(oauthDetails);
                                        
                                //Send redirect to location returned by endpoint
                                response.sendRedirect(location);
                                return;
                }
                else{
                        //Request for accessing protected resource
                                if(!OAuthUtils.isValid(oauthDetails.getResourceServerUrl())){
                                        invalidProps.add(OAuthConstants.RESOURCE_SERVER_URL);
                                                
                                }
                                        
                                if(!OAuthUtils.isValid(oauthDetails.getAccessToken())){
                                        if(!OAuthUtils.isValid(oauthDetails.getRefreshToken())){
                                                invalidProps.add(OAuthConstants.REFRESH_TOKEN);
                                        }
                                                
                                }
                                        
                                        
                                if(invalidProps.size() > 0){
                                        sendError(response, invalidProps);
                                        return;
                                }
                                        
                                Map<String,String> map = OAuthUtils.getProtectedResource(oauthDetails);
                                response.getWriter().println(new Gson().toJson(map));
                                return;
                        }
                }
                else if(OAuthUtils.isValid(code)){
                        //Callback from endpoint with code.
                        Map<String,String> map = OAuthUtils.getAccessToken(oauthDetails, code);
                        response.getWriter().println(new Gson().toJson(map));
                        return;
                }
                else{
                        //Invalid request/error response
                        String queryString = request.getQueryString();
                        String error = "Invalid request";
                        if(OAuthUtils.isValid(queryString)){
                                //Endpoint returned an error
                                error = queryString;
                        }
                        response.getWriter().println(error);
                        return;
                                
                        }
                }
                else{
                        //Input is not valid. Send error
                        sendError(response, invalidProps);
                        return;
                        
                }

Notes on Listing 6:

  • The client retrieves the query parameters, caller and code. If the request was sent by the user interface as shown earlier, the caller parameter will have a valid value; otherwise, the request was sent from the authorization server as a part of the redirection call and code will have a valid value.
  • The client code then creates an OAuthDetails bean by reading the properties provided in the OAuth2Client.config file.
  • The bean is then validated for correctness and completeness. If any invalid or missing properties are found, an appropriate error response with the missing/incorrect properties is sent to the user interface.
  • The client code then proceeds to verify the requested operation and the appropriate operation is invoked.

Access token request

The code in Listing 7 demonstrates how to make the authorization code request.

Listing 7. Authorization code request code

Listing 7. Authorization code request code

HttpPost post = new HttpPost(oauthDetails.getAuthenticationServerUrl());
String location = null;

String state = oauthDetails.getState();

List<BasicNameValuePair> parametersBody = new ArrayList<BasicNameValuePair>();

parametersBody.add(new BasicNameValuePair(OAuthConstants.RESPONSE_TYPE,
                OAuthConstants.CODE));

parametersBody.add(new BasicNameValuePair(OAuthConstants.CLIENT_ID,
                oauthDetails.getClientId()));

parametersBody.add(new BasicNameValuePair(OAuthConstants.REDIRECT_URI,
                oauthDetails.getRedirectURI()));

if (isValid(oauthDetails.getScope())) {
        parametersBody.add(new BasicNameValuePair(OAuthConstants.SCOPE,
                        oauthDetails.getScope()));
}

if (isValid(oauthDetails.getApprovalPromptValue())) {
        parametersBody.add(new BasicNameValuePair(
                        oauthDetails.getApprovalPromptKey(), oauthDetails
                                        .getApprovalPromptValue()));
}
                
if (isValid(oauthDetails.getAccessTypeValue())) {
        parametersBody.add(new BasicNameValuePair(
                        oauthDetails.getAccessTypeKey(), oauthDetails
                                        .getAccessTypeValue()));
}

if (isValid(state)) {
        parametersBody.add(new BasicNameValuePair(OAuthConstants.STATE,
                        oauthDetails.getState()));
}

DefaultHttpClient client = new DefaultHttpClient();
HttpResponse response = null;
String accessToken = null;
try {
        post.setEntity(new UrlEncodedFormEntity(parametersBody, HTTP.UTF_8));

        response = client.execute(post);
        int code = response.getStatusLine().getStatusCode();
        System.out.println("Code " + code);
        Map<String, String> map = handleURLEncodedResponse(response);

        if (OAuthConstants.HTTP_SEND_REDIRECT == code) {
                location = getHeader(response.getAllHeaders(),
                                OAuthConstants.LOCATION_HEADER);
                if (location == null) {
                        System.out
                                        .println("The endpoint did not pass in valid location header for redirect");
                        throw new RuntimeException(
                                        "The endpoint did not pass in valid location header for redirect");
                }
        } else {
                System.out
                                .println("Was expecting code 302 from endpoint to indicate redirect. Recieved httpCode "
                                                + code);
                throw new RuntimeException(
                                "Was expecting code 302 from endpoint to indicate redirect. Recieved httpCode "
                                                + code);
        }

} catch (ClientProtocolException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        throw new RuntimeException(e.getMessage());
} catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        throw new RuntimeException(e.getMessage());
}

return location;

Notes on the code in Listing 7:

  • The code starts by creating an HttpPost method, which is used to send URL-encoded parameters.
  • response_type, client_id, and redirect_uri are the mandatory parameters and are included in the request.
  • Other optional parameters such as state, scope, approval_prompt_key/value, and access_type_key/value are included if valid values for them have been provided in the config file.
  • A DefaultHttpClient is used to make the request to the authorization server.
  • The authorization server validates the request parameters and replies with a 302 HTTP redirect code along with the Location header.
  • The code recognizes the 302 received from the authorization server and retrieves the Location header from the response headers.
  • The Location header contains the URI the client needs to redirect the user agent (web browser) to. The URI is generally a login prompt for the resource owner to sign in and provide approval to the client.
  • The value for the location URI is returned to the calling method (OAuth2Client.doGet()).
  • The Oauth2Client.doGet() method redirects the response to the location URI.
  • The resource owner’s user agent (web browser) is now redirected to a login page/approval page where the resource owner needs to sign in and provide approval to the client making the request.
  • After the resource owner gives approval to the client, the authorization server uses the redirect URI passed in the original authorization code request to send the code.

The code in Listing 8 shows how the final access token request is made using the code received in the previous step.

Listing 8. Sample code access token request
HttpPost post = new HttpPost(oauthDetails.getTokenEndpointUrl());
                String clientId = oauthDetails.getClientId();
                String clientSecret = oauthDetails.getClientSecret();
                String scope = oauthDetails.getScope();
                Map<String, String> map = new HashMap<String, String>();

                List<BasicNameValuePair> parametersBody = new ArrayList<BasicNameValuePair>();

                parametersBody.add(new BasicNameValuePair(OAuthConstants.GRANT_TYPE,
                                oauthDetails.getGrantType()));

                parametersBody.add(new BasicNameValuePair(OAuthConstants.CODE,
                                authorizationCode));

                parametersBody.add(new BasicNameValuePair(OAuthConstants.CLIENT_ID,
                                clientId));

                if (isValid(clientSecret)) {
                        parametersBody.add(new BasicNameValuePair(
                                        OAuthConstants.CLIENT_SECRET, clientSecret));
                }

                parametersBody.add(new BasicNameValuePair(OAuthConstants.REDIRECT_URI,
                                oauthDetails.getRedirectURI()));

                DefaultHttpClient client = new DefaultHttpClient();
                HttpResponse response = null;
                String accessToken = null;
                try {
                        post.setEntity(new UrlEncodedFormEntity(parametersBody, HTTP.UTF_8));

                        response = client.execute(post);
                        int code = response.getStatusLine().getStatusCode();

                        map = handleResponse(response);
                        accessToken = map.get(OAuthConstants.ACCESS_TOKEN);

                } catch (ClientProtocolException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        throw new RuntimeException(e.getMessage());
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        throw new RuntimeException(e.getMessage());
                }

                return map;

Notes on Listing 8:

  • The code uses an HttpPost method to make the access token request to the token endpoint.
  • grant_type, code, client_id, and redirect_uri are the mandatory parameters.
  • If the client_secret is valid, it is included in the request as well.
  • The value for code is the code returned by the authorization server in the previous request.
  • If the request is valid, the token endpoint returns the access token and a refresh token if the request was for offline access.
  • Note that the redirect URI that is sent as a part of this request needs to be identical to the one sent as a part of the authorization code request. The token endpoint validates that the redirect URI matches the one specified in the client’s application, whose data is available with the token endpoint.

Refresh access token

As mentioned earlier, the access token is generally temporary in nature, with a typical lifetime of an hour. Having a refresh token lets the client refresh an expired access token automatically without needing the resource owner to sign in and authenticate the client again. The code in Listing 9 illustrates this.

Listing 9. Sample code for refresh access token
String clientId = oauthDetails.getClientId();
String clientSecret = oauthDetails.getClientSecret();
String scope = oauthDetails.getScope();
String refreshToken = oauthDetails.getRefreshToken();
Map<String, String> map = new HashMap<String, String>();

                if (!isValid(refreshToken)) {
                        throw new RuntimeException(
                                        "Please provide valid refresh token in config file");
                }

                List<BasicNameValuePair> parametersBody = new ArrayList<BasicNameValuePair>();

                parametersBody.add(new BasicNameValuePair(OAuthConstants.GRANT_TYPE,
                                OAuthConstants.REFRESH_TOKEN));

                parametersBody.add(new BasicNameValuePair(OAuthConstants.REFRESH_TOKEN,
                                oauthDetails.getRefreshToken()));

                if (isValid(clientId)) {
                        parametersBody.add(new BasicNameValuePair(OAuthConstants.CLIENT_ID,
                                        clientId));
                }

                if (isValid(clientSecret)) {
                        parametersBody.add(new BasicNameValuePair(
                                        OAuthConstants.CLIENT_SECRET, clientSecret));
                }

                if (isValid(scope)) {
                        parametersBody.add(new BasicNameValuePair(OAuthConstants.SCOPE,
                                        scope));
                }

                DefaultHttpClient client = new DefaultHttpClient();
                HttpResponse response = null;
                try {
                        post.setEntity(new UrlEncodedFormEntity(parametersBody, HTTP.UTF_8));

                        response = client.execute(post);
                        int code = response.getStatusLine().getStatusCode();

                        map = handleResponse(response);

                } catch (ClientProtocolException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        throw new RuntimeException(e.getMessage());
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        throw new RuntimeException(e.getMessage());
                }

                return map;

Notes on Listing 9:

  • The code uses an HttpPost method to send the request to the token endpoint.
  • The grant_type and refresh_token parameters are mandatory.
  • If valid, the client_id, client_secret, and scope are included as well.
  • If the request is valid, the token endpoint returns a new access token.

Access a protected resource

The code in Listing 10 demonstrates how to access a protected resource using the access token. If the access token is expired, the code refreshes the access token and then retries accessing the protected resource.

Listing 10. Sample code to access a protected resource

Listing 10. Sample code to access a protected resource

String resourceURL = oauthDetails.getResourceServerUrl();

                Map<String, String> map = new HashMap<String, String>();

                HttpGet get = new HttpGet(resourceURL);
                get.addHeader(OAuthConstants.AUTHORIZATION,
                                getAuthorizationHeaderForAccessToken(oauthDetails
                                                .getAccessToken()));
                DefaultHttpClient client = new DefaultHttpClient();
                HttpResponse response = null;
                String accessToken = null;
                int code = -1;
                try {
                        response = client.execute(get);
                        code = response.getStatusLine().getStatusCode();
                        if (code == OAuthConstants.HTTP_UNAUTHORIZED
                                        || code == OAuthConstants.HTTP_FORBIDDEN) {
                                // Access token is invalid or expired.Regenerate the access
                                // token
                                System.out
                                                .println("Access token is invalid or expired. Refreshing access token....");
                                map = refreshAccessToken(oauthDetails);
                                accessToken = map.get(OAuthConstants.ACCESS_TOKEN);

                                if (isValid(accessToken)) {
                                        // update the access token
                                        System.out.println("New access token: " + accessToken);
                                        oauthDetails.setAccessToken(accessToken);
                                        get.removeHeaders(OAuthConstants.AUTHORIZATION);
                                        get.addHeader(OAuthConstants.AUTHORIZATION,
                                                        getAuthorizationHeaderForAccessToken(oauthDetails
                                                                        .getAccessToken()));
                                        get.releaseConnection();
                                        response = client.execute(get);
                                        code = response.getStatusLine().getStatusCode();
                                        if (code >= 400) {
                                                throw new RuntimeException(
                                                                "Could not access protected resource. Server returned http code: "
                                                                                + code);

                                        }

                                } else {
                                        throw new RuntimeException("Could not refresh access token");
                                }

                        }

                        map = handleResponse(response);

                } catch (ClientProtocolException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } finally {
                        get.releaseConnection();
                }

                return map;

Notes on Listing 10:

  • This method accepts the OauthDetails bean with the values retrieved from the config file.
  • As the name suggests, this method uses a simple HttpGet method to try to retrieve a protected resource from the resource server.
  • To authenticate with the resource server, the access token needs to be sent as a part of the authorization header. For example, Authorization: Bearer accessTokenValue.
  • The code creates a DefaultHttpClient to make the get request to the resource server.
  • If the response code received from the resource server is 401 or 403, the access token used for authentication has probably expired or is invalid.
  • The next step is to regenerate the access token (see Listing 9).
  • After the access token is successfully regenerated, the code updates the access token value in the OauthDetails bean. The code also replaces the existing authorization header in the get method with the new access token value.
  • The code now makes another request to access the protected resource.
  • If the access token is valid and the resource server URL is correct, you should be able to see the contents of the response in the console.

Test the client with Salesforce.com

Registering with Salesforce.com

Salesforce.com is a popular Software as a Service (SaaS) application that supports the authorization code grant type for OAuth 2.0.

To register with Salseforce.com:

  1. Click Login, then click Sign up for free.
  2. Complete the registration to receive your credentials.
  3. In addition to a user name and password, you also receive a security token. The password that you provide for making an access token request needs to be a concatenation of your password and security token (for example, password12312123).
  4. See Resources for a link to a useful article on how to create an app in salesforce.com.
  5. The redirect URI that you specify during app creation is used by salesforce.com to communicate with you for the Authorization Code grant type.

Running the sample client with Salesforce.com

Now that you have set up your OAuth 2.0-compliant server at Salesforce, you are ready to test your client and retrieve protected information from the server.

You can find the Oauth2Client_salesforce.config file under the Java resources/src folder. This configuration file is customized for salesforce.com and can be used as a template for providing configuration values to test against Salesforce.com.

Run the Oauth2Client web app on the Tomcat server, as shown earlier, to get to the Test client start page, as shown in Figure 3.

Figure 3. Salesforce test client page

Test client start page

Figure 3 shows the test client page. Click Start Test for Get Access token. You will next see the login prompt for the Salesforce access token request, as shown in Figure 4.

Figure 4. Salesforce access token request (login prompt)

Salesforce.com log in window

Figure 4 shows the Salesforce login page to which the client redirected the user agent (web browser) after the 302 and Location header was received from Salesforce.com.

After logging in, you will see the Salesforce access token request approval prompt as shown in Figure 5.

Figure 5. Salesforce access token request (approval prompt)

Access request screen

Figure 5 shows the approval prompt for the resource owner (Post login) to grant access to the VarunOAuth2.0 App, which is the sample code requesting access to the resource owner’s data.

Salesforce access token output

After you grant access to the client on the Salesforce approval prompt, the token endpoint responds with the access and refresh tokens along with other Salesforce-specific data. You should see the output in your Eclipse console window, as shown in Listing 11.

Listing 11. Salesforce access token output

Listing 11. Salesforce access token output

********** Response Received **********
  id = https://login.salesforce.com/id/00D90000000mQaYEAU/00590000001HCB7AAO
  issued_at = 1408356835704
  scope = full refresh_token
  instance_url = https://ap1.salesforce.com
  token_type = Bearer
  refresh_token = 5Aep8617VFpoP.M.4vPT_5eQXhIJTvFPNyK2GaBz7xFooRQE590MJSZNVqfTXKUqoiZH_yhm_ZpaVsmp
  id_token = eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjE5MCJ9.eyJleHAiOjE0MDgzNTY5NTUsInN1YiI6Imh0dHBzOi8vbG9naW4uc2FsZXNmb3JjZS5jb20vaWQvMDBEOTAwMDAwMDBtUWFZRUFVLzAwNTkwMDAwMDAxSENCN0FBTyIsImF0X2hhc2giOiJ3eWJpZ1NRTEtaNjdiYlRxWUJxNEVnIiwiYXVkIjoiM01WRzlZNmRfQnRwNHhwNXhLcHgxcFBtcFZidnlUV09Gd2FWbkxwOFpDYjRZVjBRX2FJcHI2WTZVazd1ZHcxcWtCTFZQcVVFNWY2blU1NW5xTWljMCIsImlzcyI6Imh0dHBzOi8vbG9naW4uc2FsZXNmb3JjZS5jb20iLCJpYXQiOjE0MDgzNTY4MzV9.W-r2-k-creIijmRmZ5qQff-bonjjtynNivJAv5XaSGedGhPavWlQpmn76B9k5vB7TWUDTX6y2NroTuIpBi-F2jrO0dunN1giPfv-YKyrCYrpy75J7NXmnSnDGrKhhYgkcR7x9s9MLMoMD-Vf1wsJr58XU2He-UwfrihfkdJzvLKiWRNQfz1gCUwlNSws70AQSqrBfB6MHpLqE7ogG1M3SOp6B4hbcA8NC_zwnvJwIDmF4t3_rf6VsgPuPjV_t4PphBhbrln7sm4b9OMcRRycc8WcCvgBvNPjI37uImciDYUGIP25NEy5sRM3mEU392YlmR5AoHUqOVOqdO9DS1ULWxBy3Q-Fp1wyKYyWiCrUMXe5QdtmeBmkpzptCKXwAhfxhLBdai4bBFfh8K3If4UP-WeNcpMwNkiRhVElwntlqtCPaSs4BLiZGonpLLgwET-f_Iyxs4BauCNvyWDbme_2it2V5AEHgJoKvuf2oU6hD8-Sit8MsdEdc2ugf-nk96QJt5px3QvChfDIE8B7W5trzXagkvzVcXXJV06zJbDUf-ioz7zDTI4Popkxlb31cQiaLAtz2IxIUtjZAfAcTXJaxi8txjV8glqS8Z61145bUaitXgGmZhZAqeefrqLneyCDD--EPNWDIdQYSPhRPbtFb5Aa89AMDNIePxTNnIWShNs
  access_token = 00D90000000mQaY!AQ8AQMgSI4eZOjvKJGVPuWISCkuS1WC0R6gOWpMg57bMwVWGkyS9_Wraa5ooMrjTfaqMmXmXlgParPk1rjn0vH5BxbNRq3W0
  signature = o1VBoC/PzvMw08hZGxYvXLiV/SXQirHBl8qOrk1Mu6Q=

Retrieving user info from the Salesforce server

Now that you have the access token, you can make a request to access the resource owner’s account info from Salesforce, which requires OAuth 2.0 authentication.

Update the Oauth2Client.confg file with the access token and refresh token. Also set the resource server URL property with the resource server URL that you want to test against. For example, you can set it to the id field returned by Salesforce as a part of the access token response. This id field is used for accessing the resource owner’s account information (for example, https://login.salesforce.com/id/00D90000000mQaYEAU/00590000001HCB7AAO).

Refresh the project and run the project again on the Tomcat server. Click Start Test for Access Protected Resource.

Your Eclipse console window should show output similar to Listing 12.

Listing 12. Salesforce-protected resource output

Listing 12. Salesforce-protected resource output

********** Response Received **********
  addr_city = null
  email_verified = true
  locale = en_US
  addr_state = Unknown
  asserted_user = true
  nick_name = vern.ojha1********
  id = https://login.salesforce.com/id/00D9000000QaYEAU/005900001HCB7AAO
  first_name = varun
  timezone = America/Los_Angeles
  username = *******
  mobile_phone = null
  user_id = ************
  addr_street = null
  status = {"created_date":null,"body":null}
  user_type = STANDARD
  urls = {"sobjects":"https://ap1.salesforce.com/services/data/v{version}/sobjects/","feeds":"https://ap1.salesforce.com/services/data/v{version}/chatter/feeds","users":"https://ap1.salesforce.com/services/data/v{version}/chatter/users","query":"https://ap1.salesforce.com/services/data/v{version}/query/","enterprise":"https://ap1.salesforce.com/services/Soap/c/{version}/00D90000000mQaY","recent":"https://ap1.salesforce.com/services/data/v{version}/recent/","feed_items":"https://ap1.salesforce.com/services/data/v{version}/chatter/feed-items","search":"https://ap1.salesforce.com/services/data/v{version}/search/","partner":"https://ap1.salesforce.com/services/Soap/u/{version}/00D90000000mQaY","rest":"https://ap1.salesforce.com/services/data/v{version}/","groups":"https://ap1.salesforce.com/services/data/v{version}/chatter/groups","metadata":"https://ap1.salesforce.com/services/Soap/m/{version}/00D90000000mQaY","profile":"https://ap1.salesforce.com/0000001CB7AAO"}
  mobile_phone_verified = false
  is_app_installed = true
  photos = {"picture":"https://c.ap1.content.force.com/profilephoto/005/F","thumbnail":"https://c.ap1.content.force.com/profilephoto/005/T"}
  display_name = varun ojha
  last_modified_date = 2013-06-04T07:43:42.000+0000
  email = **************
  addr_country = IN
  organization_id = 00D90000000mQaYEAU
  last_name = ojha
  utcOffset = -28800000
  active = true
  language = en_US
  addr_zip = 560071

As you can see, you are able to successfully access the resource owner’s account information from Salesforce by authenticating using OAuth 2.0. After the access token provided in the configuration file expires, the client automatically regenerates the access token in the next request and uses that to retrieve the protected resource available at the resource server URL.

Registering with Google

Google uses OAuth 2.0 to authenticate APIs that can be used to access services such as GoogleDrive, TaskQueue, and CloudSQL to name a few. You can set up an app to test your OAuth 2.0 client with Google by following the instructions at: https://developers.google.com/accounts/docs/OAuth2?hl=ES.

Running the client with Google

Now that you have set up your OAuth 2.0-compliant server, you are ready to test your client and retrieve protected information from the server.

You can find the Oauth2Client_Google.config file under the Java resources/src folder. This configuration file is customized for use with Google services and can be used as a template for providing configuration values.

Run the Oauth2Client web app on the Tomcat server, as shown earlier. The flow and outputs for the Get Access token and Access protected resources operations are shown in Figure 6.

Figure 6. Access token request (login prompt)

Access Token Request (Login Prompt)

Figure 6 shows the Google login page to which the client redirected the user agent (web browser) after the 302 and Location header was received from Google.

Figure 7 shows the approval prompt for the resource owner (post login) to grant access to the Cast Iron App, which is requesting access to the resource owner’s data.

Figure 7. Access token request (approval prompt)

Cast Iron approval screen

After the resource owner grants access to the client, the token endpoint responds with the access and refresh tokens. You should see output in your console window similar to that shown in Listing 13.

Listing 13.
********** Response Received **********
  expires_in = 3600
  token_type = Bearer
  refresh_token = 1/TtCxaFlKMRsHeIlxrY-2ZJIO8DcRmQEiQ_2Wxw8
  access_token = ya29.ZQDpI-ahF6TMURwAAABqBu-2-U0_lUWfbwh053j3db3PzaNXV4k_k6fc_VT7uQ

Retrieving user info from the Google server

Now that you have the access token, you can make a request to access the resource owner’s GoogleDrive information, which requires OAuth 2.0 authentication.

Update the Oauth2Client.confg file with the access token and refresh token, and populate the resource server url property with the resource server URL that you wish to test against. I am going to replace it with https://www.googleapis.com/drive/v2/about, which is the GoogleDrive account info URL.

Now, refresh the project and run it on the Tomcat server again. Click Start Test for Access Protected Resource.

You should see output in your console window similar to the snippet shown in Listing 14.

Listing 14.

Listing 14.

********** Response Received **********
  name = Varun Ojha
  features = [{"featureName":"ocr"},{"featureName":"translation","featureRate":0.5}]
  quotaBytesTotal = 16106127360
  largestChangeId = 1911
  rootFolderId = 0ALupA2Opqyp1Uk9PVA
  quotaType = LIMITED
  quotaBytesByService = [{"bytesUsed":"229","serviceName":"DRIVE"},{"bytesUsed":"1154860956","serviceName":"GMAIL"},{"bytesUsed":"568174452","serviceName":"PHOTOS"}]
  permissionId = 01822620516456565194
  quotaBytesUsedInTrash = 119332

As you can see, you are able to successfully access the resource owner’s account info from GoogleDrive by authenticating using OAuth 2.0.

After the access token provided in the configuration file expires, the client automatically regenerates the access token in the next request and uses the regenerated token to retrieve the protected resource available at the resource server URL.

Testing the client with IBM endpoints

This client sample code has been successfully tested with OAuth 2.0-compliant IBM endpoints such as IBM Websphere® Application Server and IBM Datapower.®

Instructions for setting up an OAuth 2.0 server on a Websphere Application Server can be found at “Using OAuth: Enabling the OAuth service provider in WebSphere Application Server.”

IBM endpoints can be tested the same way you tested for Salesforce and Google.

Conclusion

Part 3 of this tutorial series explained the basics of the authorization code grant type. It demonstrated how a generic OAuth 2.0 client in Java code can be written to connect to multiple OAuth 2.0-compliant endpoints and retrieve protected resources from them. The sample client web project from Downloads can be imported into an Eclipse environment and used as a starting point for creating a customized Oauth 2.0 client.

Download

Description Name Size
Sample Java code for Authorization code grant type OAuth2.0_AuthCode_part_3.war 25KB

ArticleTitle=OAuth 2.0 clients in Java programming, Part 3: Authorization code grant

Source: OAuth 2.0 clients in Java programming, Part 3: Authorization code grant

Post author

Dustin Gurley is an Designer, Developer, Artist, Instructor, Critical Theorist and Systems Engineer. He has an extensive background working professionally with 2D/2.5D/3D Motion Graphics, Compositing, Film, Video, Photography and client-side performance techniques as it pertains to web development. Dustin recently completed work on his Master of Fine Art degree in Motion Media Design (Motion Graphics) from the Savannah College of Art and Design. Prior to beginning his graduate work, Dustin obtained a Bachelor of Art degree in Communication Studies with a concentration in Broadcast and Emerging Media from the University of North Carolina at Wilmington. In addition to design and modeling, Dustin enjoys toying with his view camera, working with scratch film, authoring media related material and contributing to various industry conferences. When not in front of a computer, Dustin can be found with his wife, Regina Everett Gurley. The couple enjoys dividing their time between their home just outside of Raleigh, North Carolina and the beautiful North Carolina coast. Currently, Dustin serves as the Lead Instructor of Internet Technologies for Wake Technical Community College in Raleigh, North Carolina.