D Development Tutorial: Creating an Image Gallery

This appendix explains an extended example that builds an image gallery service for storing and retrieving images. This tutorial uses Oracle Application Express.

Topics:

D.1 Before You Begin

This section describes some common conventions used in this example as well as best practices regarding API entry points.

Topics:

D.1.1 About URIs

Throughout this example, URIs and URI Templates are referenced using an abbreviated form that omits the host name, context root and workspace path prefix. Consider the following example:

gallery/images/

To access this URI in your Web browser, you would use a URI in the following format:

https://<host>:<port>/ords/<workspace>/gallery/images/

where

  • <host> is the host on which Oracle REST Data Services is running.

  • <port> is the port on which Oracle REST Data Services is listening.

  • /ords is the context root where Oracle REST Data Services is deployed.

  • /<workspace>/ is the workspace path prefix of the Oracle Application Express workspace where the RESTful Service is defined.

D.1.2 About Browser Support

This example uses many modern features defined in HTML5 and related specifications. It has only been tested in Mozilla Firefox and Google Chrome. It has not been tested in Microsoft Internet Explorer or on smart-phone/tablet web browsers. Please use recent versions of either Mozilla Firefox or Google Chrome for this example.

D.1.3 Creating an Application Express Workspace

To follow the instructions for creation the Gallery example application and related objects, first, create a new Oracle Application Express Workspace (in Full Development mode). See the Oracle Application Express Documentation for details on how to do this.

Call the workspace resteasy and call the administrator user of the workspace resteasy_admin. Ensure the resteasy_admin user is a a member of the RESTful Services user group.

D.2 Creating the Gallery Database Table

To create the Gallery database table, follow these steps:

  1. Log into the resteasy workspace.
  2. Navigate to SQL Workshop and then SQL Commands.
  3. Enter or copy and paste in the following SQL:
    CREATE SEQUENCE GALLERY_SEQ
    /
    CREATE TABLE GALLERY (
      ID NUMBER NOT NULL ENABLE,
      TITLE VARCHAR2(1000) NOT NULL ENABLE,
      CONTENT_TYPE VARCHAR2(1000) NOT NULL ENABLE,
      IMAGE BLOB NOT NULL ENABLE,
      CONSTRAINT GALLERY_PK PRIMARY KEY (ID) ENABLE
    )
    /
    CREATE OR REPLACE TRIGGER BI_GALLERY
     before insert on GALLERY for each row
     begin 
      if :NEW.ID is null then 
       select GALLERY_SEQ.nextval into :NEW.ID from sys.dual;
      end if;
     end;
     /
     ALTER TRIGGER BI_GALLERY ENABLE
     /
    

D.3 Creating the Gallery RESTful Service Module

To create the Gallery RESTful services module, follow these steps:

  1. Navigate to SQL Workshop and then RESTful Services.

  2. Click Create on the right side, and enter the following information:

    • Name: gallery.example

    • URI Prefix: gallery/

    • URI Template: images/

    • Method: POST

    • Source: Enter or copy and paste in the following:

      declare 
       image_id integer;
      begin
       insert into gallery (title,content_type,image) 
                   values  (:title,:content_type,:body)
                   returning id into image_id;
       :status := 201;
       :location := image_id;
      end;
      
  3. Click Create Module.

  4. Click the POST handler under images/

  5. For Requires Secure Access, select No.

  6. Click Create Parameter, and enter the following:

    • Name: Slug

    • Bind Variable Name: title

  7. Click Create.

  8. Click Create Parameter on the bottom right, and enter the following information:

    • Name: X-APEX-FORWARD

    • Bind Variable Name: location

    • Access Method: OUT

  9. Click Create.

  10. Click Create Parameter on the bottom right, and enter the following information:

    • Name: X-APEX-STATUS-CODE

    • Bind Variable Name: status

    • Access Method: OUT

    • Parameter Type: Integer

  11. Click Create.

At this point you have created the module with a single service that can store new images. Next, add a service to display the list of stored images:

  1. Navigate to SQL Workshop and then RESTful Services.

  2. Click the module named gallery.example.

  3. Click Create Handler under images/, and enter the following information:

    • Method: GET

    • Source Type: Feed

    • Requires Secure Access: No

    • Source: Enter or copy and paste in the following:

      select id,title,content_type from gallery order by id desc
      
  4. Click Create.

At this point you have created the service to store and list images. Next, add a service to display individual images:

  1. Navigate to SQL Workshop and then RESTful Services.

  2. Click the module named gallery.example.

  3. Click Create Template under gallery.example, and enter the following information:

    • URI Template: images/{id}

  4. Click Create.

  5. Click Create Handler under images/{id}, and enter the following information:

    • Method: GET

    • Source Type: Media Resource

    • Requires Secure Access: No

    • Source: Enter or copy and paste in the following:

      select content_type, image from gallery where id = :id
      
  6. Click Create.

D.4 Trying Out the Gallery RESTful Service

To try out the Gallery RESTful Service, follow these steps:

  1. Navigate to SQL Workshop and then RESTful Services.
  2. Click the module named gallery.example.
  3. Click the GET handler located under images/.
  4. Click Test.

    The following URI should be displayed in the browser:

    https://<host>:<port>/ords/resteasy/gallery/images/
    

    Content similar to the following should be displayed:

    {"next":
     {"$ref":
      "http://localhost:8080/ords/resteasy/gallery/images/?page=1"
     },
     "items":[]
    }
    
    • The content is a JSON document that lists the location of each image in the gallery, but since you have not yet added any images, the list (the items[] element) is empty.

    • The JSON has no extra white space to minimize its size, this can make it difficult to decipher, it is recommended to add a JSON viewing plugin to your browser to make viewing the JSON easier.

To create an Oracle Application Express application to enable users to add and view images in the gallery, see Creating the Gallery Application.

D.5 Creating the Gallery Application

To create an Oracle Application Express application that uses the gallery RESTful Services, follow these steps:

  1. Navigate to Application Builder.
  2. Click Create.
  3. Choose Database, then click Next.
  4. Enter Image Gallery in the Name field, then click Next.
  5. Click Create Application, and then Create Application again to confirm creation of the application.
  6. Click page 1, Home.
  7. Under Regions click the + (plus sign) icon to create a new region.
  8. For Region Type, choose HTML and click Next, then click Next on the next page.
  9. For Region Template, choose No Template.
  10. For Title, enter Tasks, and click Next.
  11. For Enter HTML Text Region Source, specify:
    <a class="button" id="upload-btn">Upload Image</a>
    
  12. Click Create Region.
  13. Under Regions, click the + (plus sign) icon to create a new region.
  14. For Region Type, choose HTML, and click Next, then Next again.
  15. For Region Template, choose DIV Region with ID.
  16. For Title, enter Images.
  17. Click Create Region.
  18. Click the Images region and click the Attributes tab.
  19. For Static ID, enter images, and click Apply Changes.
  20. Under Page, click the Edit icon, then click the JavaScript tab.
  21. For Function and Global Variable Declaration, enter or copy and paste in the following:
    var workspace_path_prefix = 'resteasy';
     var gallery_url = './' + workspace_path_prefix + '/gallery/images/'; 
     function uploadFiles(url, fileOrBlob, onload) {   
      var name = 'unspecified';
      if ( fileOrBlob['name'] ) {
       name = fileOrBlob.name;
      }   
      var xhr = new XMLHttpRequest();
      xhr.open('POST', url, true);
      xhr.setRequestHeader('Slug',name);
      xhr.onload = onload;   
      xhr.send(fileOrBlob);  
     }
    
     function createUploader() {
      var $upload = $('<div id="uploader" title="Image Upload"\
       style="display:none">\
       <form>\
        <fieldset>\
         <label for="file">File</label>\
         <input type="file" name="file" id="file"\
          class="text ui-widget-content ui-corner-all"/>\
        </fieldset>\
       </form>\
      </div>');
      $(document.body).append($upload);
      $upload.dialog({ 
       autoOpen:false,
       modal: true,
       buttons: {
        "Upload": function() {
         var file = document.querySelector('input[type="file"]');
         uploadFiles(gallery_url,file.files[0],function() {
          $('#uploader').dialog("close");
          getImages();
         });
        },
        "Cancel": function() {
         $('#uploader').dialog("close");
        }
       }  
      });
      $('#upload-btn').click(function() {    
       $('#uploader').dialog("open");  
      }); 
     }  
    
     function getImages() {  
      var xhr = new XMLHttpRequest();
      xhr.open('GET', gallery_url);
      xhr.onload = function(e) {
       var data = JSON.parse(this.response);
       $('#image-list').remove();
       var $images = $('<ol id="image-list"></ol>');
       for ( i in data.items ) {
        var item = data.items[i];
        var uri = item.uri['$ref'];
        var $image = $('<li></li>')                   
                     .append('<a href="' + uri + '" + title="' + 
                             item.title + '"><img src="graphics/'+ uri + 
                             '"></a>');
        $images.append($image);
       }
       $('#images').append($images);
      }  
      xhr.send(); 
     }
     
    
  22. For Execute when Page Loads, enter or copy and paste in the following:
    createUploader();
    getImages();
    
  23. Click Apply Changes.
  24. Under Page, click the Edit icon, then click the CSS tab.
  25. For Inline, enter or copy and paste in the following:
    a img { border:none; } 
    #images ol { margin: 1em auto; width: 100%; } 
    #images li { display: inline; } 
    #images a { background: #fff; display: inline; float: left; 
                margin: 0 0 27px 30px; width: auto; padding: 10px 10px 15px; 
                textalign: center; text-decoration: none; color: #333; 
                font-size: 18px; -webkit-box-shadow: 0 3px 6px rgba(0,0,0,.25);
                -moz-boxshadow: 0 3px 6px rgba(0,0,0,.25); }
    #images img { display: block; width: 190px; margin-bottom: 12px; } 
    label {font-weight: bold; text-align: right;float: left;
           width: 120px; margin-right: 0.625em; }
    label :after {content(":")} 
    input, textarea { width: 250px; margin-bottom: 5px;textalign: left}
    textarea {height: 150px;}
    br { clear: left; }
    #images a:after { content: attr(title); }
    .button {
      border-top: 1px solid #96d1f8; 
      background: #65a9d7;
      background: 
       -webkit-gradient(linear,left top,left bottom,
                        from(#3e779d),to(#65a9d7));    
      background: 
       -webkit-linear-gradient(top, #3e779d, #65a9d7);
      background: 
       -moz-linear-gradient(top, #3e779d, #65a9d7);
      background: -ms-linear-gradient(top, #3e779d, #65a9d7);
      background: -o-linear-gradient(top, #3e779d, #65a9d7);
      padding: 5px 10px;    
      -webkit-border-radius: 8px;
      -moz-border-radius: 8px;
      border-radius: 8px;    
      -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0;
      -moz-box-shadow: rgba(0,0,0,1) 0 1px 0;
      box-shadow: rgba(0,0,0,1) 0 1px 0;
      text-shadow: rgba(0,0,0,.4) 0 1px 0;
      color: white;
      font-size: 14px;
      text-decoration: none;
      vertical-align: middle;
      } 
    
     .button:hover {
      border-top-color: #28597a;    
      background: #28597a;
      color: #ccc;
      cursor: pointer;     
     }
    
     .button:active {
      border-top-color: #1b435e;    
      background: #1b435e;
     }
     
    
  26. Click Apply Changes.

D.6 Trying Out the Gallery Application

To try out the Gallery application, follow these steps:

  1. Navigate to Application Builder.
  2. Click Run beside the Image Gallery application.
  3. Log in as the resteasy_admin user.
  4. Click Upload Image.
  5. Choose an image file (a JPEG or PNG file) and click Upload.

    The application displays the uploaded image.

D.7 Securing the Gallery RESTful Services

It is not wise to allow public access to the image uploading service, and it is probably not ideal to allow public access to the images in the gallery either. Therefore, you should protect access to the RESTful services.

RESTful Services support two kinds of authentication:

  • First Party Authentication. This is authentication intended to be used by the party who created the RESTful service, enabling an Application Express application to easily consume a protected RESTful service. The application must be located with the RESTful service, that is, it must be located in the same Oracle Application Express workspace. The application must use the standard Oracle Application Express authentication.

  • Third Party Authentication. This is authentication intended to be used by third party applications not related to the party who created the RESTful service. Third party authentication relies on the OAuth 2.0 protocol.

Topics:

D.7.1 Protecting the RESTful Services

To protect the RESTful services, follow these steps:

  1. Navigate to SQL Workshop and then RESTful Services.

  2. Click RESTful Service Privileges in the section labeled Tasks.

  3. Click Create, and enter the following:

    • Name: example.gallery

    • Label: Gallery Access

    • Assigned Groups: RESTful Services

    • Description: View and Post images in the Gallery

    • Protected Modules: gallery.example

  4. Click Create.

To check that access to the RESTful Service is now restricted, follow these steps:

  1. Navigate to SQL Workshop and then RESTful Services.

  2. Click the module named gallery.example.

  3. Click the GET handler located under images/.

  4. Click Test.

    The URI in the following format should be displayed in the browser:

    https://<host>:<port>/ords/resteasy/gallery/images
    

    An error page should be displayed with the error message:

    401 Unauthorized.
    

See Also:

This is the expected result, because a protected RESTful Service cannot be accessed unless proper credentials are provided. To add the required credentials to the request, see Modifying the Application to Use First Party Authentication

D.7.2 Modifying the Application to Use First Party Authentication

First Party Authentication relies on the cookie and user session established by the Application Express application, but Oracle REST Data Services needs additional information to enable it to verify the cookie. It needs to know the application ID and the current session ID. This information is always known to the Application Express application, and must be included with the request made to the RESTful service by adding the custom Apex-Session HTTP header to each request sent to a RESTful Service. The application ID and session ID are sent as the value of the header, separated from each other by a comma delimiter. For example:

GET /ords/resteasy/gallery/images/
Host: server.example.com
Apex-Session: 102,6028968452563

Sometimes it is not possible to include a custom header in the HTTP request. For example, when displaying an image in an HTML page using the <img> tag, an alternative mechanism is used for these scenarios. The application ID and session ID are included in a query parameter named _apex_session, which is added to the Request URI, which contains the application ID and session ID separated by a comma. For example:

<img src="graphics/101?_apex_session=102,6028968452563">

Note that this approach must only be used when it is not possible to use a custom header. Otherwise, this approach is discouraged because of the increased risk of the session ID being inadvertently stored or disclosed due to its inclusion in the URI.

To modify the application to add the first party authentication information to each request, follow these steps:

  1. Navigate to Application Builder.

  2. Click the Edit button beside the Image Gallery application.

  3. Click the first page, named Home.

  4. Under Page click the Edit icon, and click the JavaScript tab.

  5. Add the following at the start of the Function and Global Variable Declaration field:

    function setApexSession(pathOrXhr) {  
     var appId = $v('pFlowId');
     var sessionId = $v('pInstance');
     var apexSession = appId + ',' + sessionId;   
     if ( typeof pathOrXhr === 'string' ) {
      var path = pathOrXhr;   
      if ( path.indexOf('?') == -1 ) {
       path = path + '?_apex_session=' + apexSession;
      } else {
       path = path + '&_apex_session=' + apexSession;
      }
      return path;
     } else {
      var xhr = pathOrXhr;
      xhr.setRequestHeader('Apex-Session',apexSession);
      return xhr;
     }
    }
    
  6. This defines a JavaScript function named setApexSession() which will add the first party authentication information to an XMLHttpRequest object or a string containing a path.

    Now you must modify the existing JavaScript code to add call this function when appropriate.

  7. After the line reading xhr.open('POST',url,true);, add the following line:

    setApexSession(xhr);
    
  8. After the line reading xhr.open('GET', gallery_url);, add the following line:

    setApexSession(xhr);
    
  9. Change the line reading var uri = item.uri['$ref']; to:

    var uri = setApexSession(item.uri['$ref']);
    
  10. Click Apply Changes.

  11. Try running the application as before. It should work, because it is now providing the RESTful Services with the required authentication information.

D.8 Accessing the RESTful Services from a Third Party Application

If third parties want to consume and use the Gallery RESTful services, they must register the third party application in order to gain OAuth 2.0 credentials, which can then be used to initiate an interactive process by which users can authorize the third party application to access the RESTful Services on their behalf.

Once an application is registered, it can then acquire an access token. The access token must be provided with each request to a protected RESTful Service. Oracle REST Data Services verifies the access token before allowing access to the RESTful service.

OAuth 2.0 defines a number of different protocol flows that can be used by applications to acquire an access token. Oracle REST Data Services supports two of these protocol flows:

  • Authorization Code. This flow is used when the third party application is able to keep its client credentials secure, for example, a third party website that is properly secured.

  • Implicit Grant. This flow is used when the third party application cannot assure that its credentials would remain secret, for example, a JavaScript-based browser application or a native smartphone application.

The first step is to register the third party application. To demonstrate this, you will create a user representing the third party developer, and then use that user to register an application.

The steps in the related topics create a user in the RESTEASY workspace user repository and perform related actions.

Note:

In addition to authenticating users defined in workspace user repositories, Oracle REST Data Services can also authenticate against any user repository accessible from WebLogic Server or GlassFish. For information, see Authenticating Against WebLogic Server and GlassFish User Repositories.

Topics:

D.8.1 Creating the Third Party Developer User

To create the third party developer user (the user account for the third party developer who wants to register an application to access the RESTful services), follow these steps:

  1. Navigate to Administration.
  2. Click Manage Users and Groups.
  3. Click Create User, and enter the following information:
    • Username: 3rdparty_dev

    • Email Address: Email address for this developer user

    • Password: Password for this user

    • User Groups: OAuth 2.0 Client Developer

  4. Click Create User.

D.8.2 Registering the Third Party Application

To register the third party application to use the Implicit Grant OAuth 2.0 protocol flow, follow these steps:

  1. Go to the following URI in your browser:

    https://server:port/ords/resteasy/ui/oauth2/clients/

  2. Enter the credentials of the 3rdparty_dev user created above, and click Sign In.
  3. Click Register Client, and enter the following information:
    • Name: 3rd Party Gallery

    • Description: Demonstrates consuming the Gallery RESTful Service

    • Response Type: Token

    • Redirect URI: https://server:port/i/oauthdemo/gallery.html

    • Support Email: Desired email address

    • Required Scopes: Gallery Access

  4. Click Register.
  5. Click 3rd Party Gallery in the list that appears on the next page.
  6. Note the values of the Client Identifier and the Authorization URI fields.

D.8.3 Acquiring an Access Token

To acquire an access token, a user must be prompted to approve access. To initiate the approval process, direct the user to the approval page using the following URI:

https://server:port/ords/resteasy/oauth2/auth?response_type=token&\
                                              client_id=CLIENT_IDENTIFIER&\
                                              state=STATE

where:

  • CLIENT_IDENTIFIER is the Client Identifier assigned to the application when it was registered.

  • STATE is a unique value generated by the application used to prevent Cross Site Request Forgery (CSRF) attacks.

Note the following about the Oracle REST Data Services OAuth 2.0 implementation:

  • The OAuth 2.0 specification allows two optional parameters to be supplied in the above request:

    • redirect_uri: Identifies the location where the authorization server will redirect back to after the user has approved/denied access.

    • scope: Identifies the RESTful Service Privileges that the client wishes to access.

    Oracle REST Data Services does not support either of these parameters: both of these values are specified when the client is registered, so it would be redundant to repeat them here. Any values supplied for these parameters will be ignored.

  • The OAuth 2.0 specification recommends the use of the state parameter, but Oracle REST Data Services requires the use of the parameter because of its importance in helping to prevent CSRF attacks.

  • The response type is also specified when the application is registered, and thus the response_type parameter is also redundant; however, the OAuth 2.0 specification states the parameter is always required, so it must be included. It is an error if the response_type value differs from the registered response type.

When the preceding URI is accessed in a browser, the user is prompted to sign on, and then prompted to review the application's request for access and choose whether to approve or deny access.

If the user approves the request, then the browser will be redirected back to the registered redirect URI, and the access token will be encoded in the fragment portion of the URI:

https://server:port/i/oauthdemo/gallery.html#token_type=bearer&\
                                             access_token=ACCESS_TOKEN&\
                                             expires_in=TOKEN_LIFETIME&\
                                             state=STATE

where:

  • ACCESS_TOKEN is the unique, unguessable access token assigned to the current user session, and which must be provided with subsequent requests to the RESTful service.

  • TOKEN_LIFETIME is the number of seconds for which the access token is valid.

  • STATE is the unique value supplied by the application at the start of the authorization flow. If the returned state value does not match the initial state value, then there is an error condition, and the access token must not be used, because it is possible an attacker is attempting to subvert the authorization process through a CSRF attack.

Note:

You can modify the default OAuth access token duration (or lifetime) for all the generated access tokens. To achieve this, add the security.oauth.tokenLifetime entry to the defaults.xml configuration file in the following way, with the OAuth access token duration specified in seconds:

<entry key="security.oauth.tokenLifetime”>600</entry>

If the user denies the request, or the user is not authorized to access the RESTful Service, the browser will be redirected back to the registered redirect URI, and an error message will be encoded in the fragment portion of the URI:

https://server:port/i/oauthdemo/gallery.html#error=access_denied&state=STATE

where:

  • error=access_denied informs the client that the user is not authorized to access the RESTful Service, or chose not to approve access to the application.

  • STATE is the unique value supplied by the application at the start of the authorization flow. If the returned state value does not match the initial state value, then there is an error condition, the client should ignore this response. It is possible an attacker is attempting to subvert the authorization process via a CSRF attack.

D.8.4 Using an Access Token

After the application has acquired an access token, the access token must be included with each request made to the protected RESTful service. To do this, an Authorization header is added to the HTTP request, with the following syntax:

Authorization: Bearer ACCESS_TOKEN

where:

  • ACCESS_TOKEN is the access token value.

For example, a JavaScript-based browser application might invoke the Gallery service as follows:

var accessToken = ... /* initialize with the value of the access token */
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://server:port/ords/resteasy/gallery/images/',true);
/* Add the Access Token to the request */ 
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);  
xhr.onload = function(e) {   
 /* logic to process the returned JSON document */
 ...   
};
xhr.send();

The preceding example uses the XMLHttpRequest.setRequestHeader(name,value) function to add the Authorization header to the HTTP request. If the access token is valid, then the server will respond with a JSON document listing the images in the gallery.

D.8.5 About Browser Origins

One of the key security concepts of web browsers is the Same Origin Policy, which permits scripts running on pages originating from the same web site (an Origin) to access each other's data with no restrictions, but prevents access to data originating from other web sites.

An origin is defined by the protocol, host name and port of a web-site. For example https://example.com is one origin and https://another.example.com is a different origin, because the host name differs. Similarly, http://example.com is a different origin than https://example.com because the protocol differs. Finally, http://example.com is a different origin from http://example.com:8080 because the port differs.

For example, if a third party client of the Gallery RESTful service is located at:

https://thirdparty.com/gallery.html

and the Gallery RESTful service is located at:

https://example.com/ords/resteasy/gallery/images/

then the Same Origin Policy will prevent gallery.html making an XMLHttpRequest to https://example.com/ords/resteasy/gallery/images/, because scripts in the https://thirdparty.com origin can only access data from that same origin, and https://example.com is clearly a different origin.

This is proper if the authors of https://example.com do not trust the authors of https://thirdparty.com. However, if the authors do have reason to trust each other, then the Same Origin Policy is too restrictive. Fortunately, a protocol called Cross Origin Resource Sharing (CORS), provides a means for https://example.com to inform the web browser that it trusts https://thirdparty.com and thus to instruct the browser to permit gallery.html to make an XMLHttpRequest to https://example.com/ords/resteasy/gallery/images/.

D.8.6 Configuring a RESTful Service for Cross Origin Resource Sharing

To configure a RESTful service for Cross Origin Resource Sharing, follow these steps:

  1. Navigate to SQL Workshop and then RESTful Services.
  2. Click the module named gallery.example.
  3. For Origins Allowed, enter the origins that are permitted to access the RESTful service (origins are separated by a comma).
  4. Press Apply Changes

D.8.7 Acquiring a Token Using the Authorization Code Protocol Flow

Other sections have explained acquiring an access token using the OAuth 2.0 Implicit protocol flow. This section explains how to do the same using the Authorization Code protocol flow. The process is slightly more involved than for the Implicit protocol flow, because it requires exchanging an authorization code for an access token.

This section will mimic this exchange process using cURL.

Topics:

D.8.7.1 Registering the Client Application

[To register the client, follow these steps:

  1. Go to the following URI in your browser:

    https://server:port/ords/resteasy/ui/oauth2/clients/

  2. Enter the credentials of the 3rdparty_dev user, and click Sign In.
  3. Click Register Client, and enter the following information:
    • Name: Another Gallery

    • Description: Demonstrates using the Authorization Code OAuth 2.0 Protocol Flow

    • Response Type: Code

    • Redirect URI : https://gallery.example.demo

    • Support EMail: any desired email address

    • Required Scopes: Gallery Access

  4. Click Register.
  5. Click 3rd Party Gallery in the list that appears on the next page.
  6. Note the values of the Client Identifier, Client Secret, and the Authorization URI fields.

D.8.7.2 Acquiring an Authorization Code

The first step in the Authorization Code protocol flow is to acquire an authorization code. An authorization code is a short lived token that when presented along with the application's client identifier and secret can be exchanged for an access token.

To acquire an access token, the user must be prompted to approve access. To initiate the approval process, direct the user to the approval page using a URI in the following format:

https://server:port/ords/resteasy/oauth2/auth?response_type=code&\
                                                client_id=CLIENT_IDENTIFIER&\
                                                state=STATE

where:

  • CLIENT_IDENTIFIER is the Client Identifier assigned to the application when it was registered.

  • STATE is a unique value generated by the application used to prevent Cross Site Request Forgery (CSRF) attacks.

If the user approves the request, then the browser will be redirected back to the registered redirect URI, and the access token will be encoded in the query string portion of the URI:

https://gallery.example.demo?code=AUTHORIZATION_CODE&state=STATE

where:

  • AUTHORIZATION_CODE is the authorization code value.

  • STATE is the unique value supplied by the application at the start of the authorization flow. If the returned state value does not match the initial state value, then there is an error condition, the authorization code must not be used. It is possible an attacker is attempting to subvert the authorization process via a CSRF attack.

    Because the registered https://gallery.example.demo redirect URI does not exist, the browser will report a server not found error, but for the purposes of this example, this does not matter, because you can still see the authorization code value encoded in the URI. Note the value of the code parameter, because it will be used while Exchanging an Authorization Code for an Access Token.

D.8.7.3 Exchanging an Authorization Code for an Access Token

In this section you will use cURL to exchange the authorization code for an access token. To exchange an authorization code the application must make an HTTP request to the Oracle REST Data Services OAuth 2.0 token endpoint, providing the authorization code and its client identifier and secret. If the credentials are correct, Oracle REST Data Services responds with a JSON document containing the access token. Note that the application makes the HTTP request from its server side (where the client identifier and secret are securely stored) directly to Oracle REST Data Services; the web-browser is not involved at all in this step of the protocol flow.

Use a cURL command in the following format to exchange the authorization code for an access token:

curl -i -d "grant_type=authorization_code&code=AUTHORIZATION_CODE" \
     --user CLIENT_IDENTIFER:CLIENT_SECRET \
     https://server:port/ords/resteasy/oauth2/token

where:

  • AUTHORIZATION_CODE is the authorization code value (which was encoded in the code parameter of the query string in the redirect URI in the previous section).

  • CLIENT_IDENTIFER is the client identifier value.

  • CLIENT_SECRET is the client secret value.

cURL translates the above commands into an HTTP request like the following:

POST /ords/resteasy/oauth2/token HTTP/1.1
Authorization: Basic Q0xJRU5UX0lERU5USUZJRVI6Q0xJRU5UX1NFQ1JFVA== 
Host: server:port 
Accept: */* 
Content-Length: 59 
Content-Type: application/x-www-form-urlencoded  

grant_type=authorization_code&code=AUTHORIZATION_CODE

where:

  • The request is an HTTP POST to the oauth2/token OAuth 2.0 token endpoint.

  • The Authorization header uses the HTTP BASIC authentication protocol to encode the client identifier and secret to assert the application's identity.

  • The Content-Type of the request is form data (application/x-www-form-urlencoded) and the content of the request is the form data asserting the OAuth 2.0 token grant type and the OAuth 2.0 authorization code value.

The preceding HTTP request will produce a response like the following:

HTTP/1.1 200 OK
ETag: "..."
Content-Type: application/json

{
 "access_token":"04tss-gM35uOeQzR_2ve4Q..",
 "token_type":"bearer",
 "expires_in":3600,
 "refresh_token":"UX4FVHhPFJl6GokvTXYw0A.."
}

The response is a JSON document containing the access token along with a refresh token. After the application has acquired an access token, the access token must be included with each request made to the protected RESTful Service. To do this an Authorization header is added to the HTTP request, with the following syntax:

Authorization: Bearer ACCESS_TOKEN

D.8.7.4 Extending OAuth 2.0 Session Duration

To extend the lifetime of an OAuth 2.0 session, a refresh token can be exchanged for a new access token with a new expiration time. Note that refresh tokens are only issued for the Authorization Code protocol flow.

The application makes a similar request to that used to exchange an authorization code for an access token. Use a cURL command in the following format to exchange the refresh token for an access token:

curl -i -d "grant_type=refresh_token&refresh_token=REFRESH_TOKEN" \      
     --user CLIENT_IDENTIFER:CLIENT_SECRET \
     https://server:port/ords/resteasy/oauth2/token

where:

  • REFRESH_TOKEN is the refresh token value returned when the access token was initially issued.

  • CLIENT_IDENTIFER is the client identifier value.

  • CLIENT_SECRET is the client secret value.

cURL translates the above commands into an HTTP request like the following:

POST /ords/resteasy/oauth2/token HTTP/1.1 
Authorization: Basic Q0xJRU5UX0lERU5USUZJRVI6Q0xJRU5UX1NFQ1JFVA==
Host: server:port
Accept: */*
Content-Length: 53 
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=REFRESH_TOKEN

where:

  • The request is an HTTP POST to the oauth2/token OAuth 2.0 token endpoint.

  • The Authorization header uses the HTTP BASIC authentication protocol to encode the client identifier and secret to assert the application's identity.

  • The Content-Type of the request is form data (application/x-www-form-urlencoded) and the content of the request is the form data asserting the OAuth 2.0 token grant type and the refresh token value.

The preceding HTTP request will produce a response like the following:

HTTP/1.1 200 OK
ETag: "..."
Content-Type: application/json  

{
 "access_token":"hECH_Fc7os2KtXT4pDfkzw..",
 "token_type":"bearer",
 "expires_in":3600,
 "refresh_token":"-7OBQKc_gUQG93ZHCi08Hg.."
}

The response is a JSON document containing the new access token along with a new refresh token. The existing access token and refresh token are invalidated, and any attempt to access a service using the old access token will fail.

D.8.8 About Securing the Access Token

In OAuth 2.0 the access token is the sole credential required to provide access to a protected service. It is, therefore, essential to keep the access token secure. Follow these guidelines to help keep the token secure:

  • It is strongly recommended to use HTTPS for all protected RESTful Services. This prevents snooping attacks where an attacker may be able to steal access tokens by eavesdropping on insecure channels. It also prevents attackers from viewing the sensitive data that may be present in the payload of the requests.

  • Ensure that the client application is not located in a browser origin with other applications or scripts that cannot be trusted. For example assume that user Alice has a client application hosted at the following location:

    https://sharedhosting.com/alice/application
    

    If another user (such as Fred) is also able to host his application in the same origin, for example, at:

    https://sharedhosting.com/fred/trouble
    

    then it will be easy for /fred/trouble to steal any access token acquired by /alice/application, because they share the same origin https://sharedhost.com, and thus the browser will not prevent either application from accessing the other's data.

    To protect against this scenario, Alice's application must be deployed in its own origin, for example:

    https://alice.sharedhosting.com/application
    

    or:

    https://application.alice.sharedhosting.com
    

    or:

    https://aliceapp.com