Oracle Application Express is a declarative, rapid web application development tool for the Oracle database. It is a fully supported, no cost option available with all editions of the Oracle database. Using only a web browser, you can develop and deploy professional applications that are both fast and secure.
Oracle REST Data Services (ORDS) is a Java EE-based alternative for Oracle HTTP Server (OHS) and mod_plsql. The Java EE implementation offers increased functionality including command line based configuration, enhanced security, file caching and RESTful Web Services. Oracle REST Data Services also provides increased flexibility by supporting deployments using Oracle WebLogic Server, Oracle Glassfish Server, Apache Tomcat, and a standalone mode.
Tip Oracle REST Data Services was previously known as Oracle Application Express Listener.
Representational State Transfer (REST) is a style of software architecture for distributed hypermedia systems such as the World Wide Web. An API is described as RESTful when it conforms to the tenets of REST. Although a full discussion of REST is outside the scope of this document, a RESTful API has the following characteristics:
Release 4.2 of Oracle Application Express leverages the capabilities of Oracle REST Data Services 2.0 to provide developers with an easy to use graphical user interface for defining and testing RESTful Web Services.
This document will provide an introduction to developing RESTful Web Services with Oracle Application Express and Oracle REST Data Services.
Tip Please ensure that you have installed and configured both Oracle Application Express 4.2 or later, and Oracle REST Data Services 2.0.6 or later before attempting to follow any of the examples in this document.
Tip This document assumes your are familiar with the usage of Oracle Application Express. If you are new to Oracle Application Express, please consult the Oracle Application Express documentation.
This section introduces some common terms that are used throughout this document:
GET
HTTP method for the above Resource Template might be: select content_type, contents from images where id = :id.GET:
Retrieve the resource contents.POST:
Store a new resource.PUT:
Update an existing resource.DELETE:
Remove a resource.To view, create, edit and test RESTul Services in Oracle Application Express, access the ‘RESTful Services’ option within ‘SQL Workshop’:
If your Application Express instance is configured to automatically add the sample application and sample database objects to workspaces, then a sample Resource Module named: oracle.example.hr
will be visible in the list of Resource Modules. If not then you can click the ‘Reset Sample Data’ task on the right hand side of the RESTful Services Page to create the sample Resource Module.
Click on the oracle.example.hr
icon to view the Resource Templates and Resource Handlers defined within the module. Note how the module has a URI prefix with the value: hr/.
This means that all URIs serviced by this module will start with the characters hr/
.
Click on the Resource Template named employees/{id}.
Note how the template has a URI Template with the value: employees/{id}.
This means that all URIS starting with hr/employees/
will be serviced by this Resource Template.
The HTTP methods supported by a Resource Template are listed beneath the Resource Template, in this case, the only supported method is the GET
method. Click on the GET
Resource Handler for hr/employees/{id}
to view it’s configuration. The Source Type for this handler is ‘Query One Row’. This means that the resource is expected to be mapped to a single row in the query result set. The ‘Source’ for this handler is:
select * from emp
where empno = :id
Assuming that the empno column is unique, the query should only produce a single result (or no result at all if no match is found for :id). Let’s try it out. Press the ‘Test’ button. The following error message should be displayed:
400 - Bad Request Request path contains unbound parameters: id
If you look at the URI displayed in the browser, it will look something like this:
https://server:port/ords/workspace/hr/employees/{id}
where:
Note the final part of the URI: hr/employees/{id},
the error message is telling us that this is not a valid URI, the problem is that we did not substitute in a concrete value for the parameter named {id}.
Let’s fix that, press the browser back button, then click ‘Set Bind Variables’.
In the page that appears you are prompted to enter a value for the bind variable named :id, enter the value 7369 and press the ‘Test’ button. A new browser window appears displaying the following JSON (JavaScript Object Notation):
{
"empno":7369,
"ename":"SMITH",
"job":"CLERK",
"mgr":7902,
"hiredate":"1980-12-17T08:00:00Z",
"sal":800,
"deptno":20
}
Note also the URI displayed in the browser for this resource:
https://server:port/ords/workspace/hr/employees/7369
The {id} URI Template parameter is bound to the SQL :id bind variable, and in this case it has been given the concrete value of 7369 so the query executed by the RESTful Service becomes:
select * from emp
where empno = 7369
The results of this query are then rendered as JSON as shown above.
Tip Reading JSON can be difficult, to make it easier to read, it is recommended to install a browser extension that pretty prints the JSON. For example, Mozilla Firefox and Google Chrome both have extensions named JSONView:
Now lets see what happens when we enter the URI of a resource that does not exist. In the ‘Set Bind Variables’ page, change the value of :id from 7369 to 1111, and press the ‘Test’ button. As before a new window pops up, but instead of displaying a JSON resource, it displays an error message reading:
404 - Not Found
This is the expected behaviour of this handler, when a value is bound to :id that does not exist in the emp table, the query produces no results and consequently the standard HTTP Status Code of 404 - Not Found
is returned.
We have a service that will provide us information about individual employees, if we know the id of an employee, but how do we discover the set of valid employee ids? Press the ‘Cancel’ button to return to the previous page displaying the contents of the Resource Module. Click on the template named employees/.
Let’s look at the resource it generates first, and then afterwards let’s understand it’s logic.
Click on the GET
handler beneath employees/,
and click the ‘Test’ button. A resource similar to the following is displayed (If you haven’t already done so, now would be a good time to install a JSON viewer extension in your browser to make it easier to view the output):
{
"next":
{"$ref":
"https://server:port/ords/workspace/hr/employees/?page=1"},
"items": [
{
"uri":
{"$ref":
"https://server:port/ords/workspace/hr/employees/7369"},
"empno": 7369,
"ename": "SMITH"
},
{
"uri":
{"$ref":
"https://server:port/ords/workspace/hr/employees/7499"},
"empno": 7499,
"ename": "ALLEN"
},
...
{
"uri":
{"$ref":
"https://server:port/ords/workspace/hr/employees/7782"},
"empno": 7782,
"ename": "CLARK"
}
]
}
This document contains a number of things worth noting:
employees/{id}.
In other words if a client accesses any of these URIs the request will be serviced by the employees/{id}
RESTful service we discussed previously.So this service addresses the problem of identifying valid employee ids by generating a resource that lists all valid employee resources. The key point to realise here is that it does not do this by just listing the id value by itself and expecting the client to be able to take the id and combine it with prior knowledge of the employees/{id}
service to produce an employee URI, instead it lists the URIs of each employee.
Since the list of valid employees may be large, the service also breaks the list into smaller pages, and again uses a URI to tell the client where to find the next page in the results.
Now let’s look at how this service is implemented. Press the back button in your browser to return to the GET
handler definition.
Note the Source Type is Query, this is the default Source Type, and indicates that the resource can contain zero or more results. The ‘Pagination Size’ is 7, this means that there will be seven items on each page of the results. Finally the Source for the handler looks like this:
select empno "$uri", empno, ename from (
select emp.*,
row_number() over (order by empno) rn
from emp
) tmp
where
rn between :row_offset and :row_count
Let’s explain this query:
{"uri":
{"$ref":
"https://server:port/ords/workspace/hr/employees/7369"
}
}
empno,
but aliased to a column name of $uri,
we’ll explain this in a moment, the second is again the employee id and the third is the employee name: ename.
https://server:port/ords/workspace/hr/employees/.
Assume the value of empno in the first row produced by the this service’s query is 7369, then the value of $uri becomes: https://server:port/ords/workspace/hr/employees/7369.
$ref,
and whose value is a string, is a URI. Thus the column: $uri,
and it’s value: https://server:port/ords/workspace/hr/employees/7369
is transformed to the following JSON object:The inner query uses the row_number() analytical function to count the number of rows in the result set, and the outer where clause constrains the result set to only return rows falling within the desired page of results. Oracle REST Data Services defines two implicit bind parameters, :row_offset
and :row_count
that always contain the indicies of the first and last rows that should be returned in a given page’s results.
For example if the current page is the first page and the pagination size is 7 then the value of :row_offset
will be 1 and the value of :row_count
will be 7.
Now let’s look at a simpler way to do both hyperlinks and paged results. Click on the GET
handler of the employeesfeed/
resource template, note the Source Type of this handler is Feed and Pagination Size is 25. Change the pagination size to 7 and click ‘Apply Changes’. Note the Source of the handler is just:
select empno, ename from emp
order by deptno, ename
As you can see, the query is much simpler than the previous example, however if you click the ‘Test’ button, you will see a result that is very similar to the result produced by the previous example.
Firstly the Feed Source Type is an enhanced version of the Query Source Type that automatically assumes the first column in a result set should be turned into a hyperlink, eliminating the need to alias columns with a name starting with $. In this example the empno column is automatically transformed into a hyperlink by the ‘Feed’ Source Type.
Secondly this example demonstrates Oracle REST Data Services’s ability to automatically paginate result sets if a ‘Pagination Size’ of greater than zero is defined, and the query does not explicitly dereference the :row_offset
or :row_count
bind parameters. Since both these conditions hold true for this example, Oracle REST Data Services enhances the query, wrapping it in clauses to count and constrain the number and offset of rows returned. Note that this ability to automatically paginate results also applies to the Query Source Type.
So far we have tested RESTful Services using a web browser. Another important and useful way to test RESTful Services is using the command line tool named: cURL.
This powerful tool is available for most platforms, and enables you to see and control exactly what data is being sent/received to/from a RESTful Service. Let’s try it out with the services mentioned above.
curl -i https://server:port/ords/workspace/hr/employees/7369
This will produce a response like the following:
HTTP/1.1 200 OK
Server: Oracle-REST-Data-Services/2.0.6.78.05.25
ETag: "..."
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 28 Mar 2014 16:49:34 GMT
{
"empno":7369,
"ename":"SMITH",
"job":"CLERK",
"mgr":7902,
"hiredate":"1980-12-17T08:00:00Z",
"sal":800,
"deptno":20
}
The -i option tells curl to display the HTTP headers returned by the server.
This example builds an image gallery service that provides the ability to store and retrieve images.
This section describes some common conventions used in this example as well as best practices regarding API entry points.
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://:/ords//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./apex
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.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.
First create a new Oracle Application Express Workspace (in Full Development mode), consult your 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.
Create the Gallery Database Table
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
/
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;
At this point we have created the module with a single service that can store new images. Let’s add a service to display the list of stored images:
select id,title,content_type from gallery order by id desc
At this point we have created the service to store and list images. Let’s add a service to display individual images:
select content_type, image from gallery where id = :id
About URIs
in the ‘Method’ field.GET
in the ‘Method’ field.Let’s try out the RESTful Service.
https://:/ords/resteasy/gallery/images/
{"next":
{"$ref":
"http://localhost:8080/ords/resteasy/gallery/images/?page=1"
},
"items":[]
}
GET
handler located under images/.Let’s create an APEX application to enable us to add and view images in the gallery.
Next we will create an Oracle Application Express Application to demonstrate using the gallery RESTful Services.
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 = $('
\
\
'); $(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 = $('
'); for ( i in data.items ) { var item = data.items[i]; var uri = item.uri['$ref']; var $image = $('
') .append('<a href="' + uri + '"><img src="'+ uri +
'" /></a>'); $images.append($image); } $('#images').append($images); } xhr.send(); }
createUploader();
getImages();
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;
}
It is not wise to allow public access to the image uploading service, and it’s probably not ideal to allow public access to the images in the gallery either. Therefore we need to protect access to the RESTful Services.
RESTful Services support two kinds of authentication:
Let’s check that access to the RESTful Service is now restricted.
https://:/ords/resteasy/gallery/images/
GET
handler located under images/.
401 Unauthorized.
This is the expected result, a protected RESTful Service cannot be accessed unless proper credentials are provided. In the following sections we’ll describe how to add the required credentials to the request.
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. The information is included 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 a 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
is added to the Request URI, containing the application id and session id separated by a comma delimiter, for example:
Note that this approach must only be used when using a custom header is not possible, it’s use otherwise is discouraged because of the increased risk of the session id being inadvertedly stored or disclosed due to it’s inclusion in the URI.
Now let’s modify the application to add the first party authentication information to each request:
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;
}
}
setApexSession(xhr);
setApexSession(xhr);
var uri = setApexSession(item.uri['$ref']);
setApexSession()
which will add the first party authentication information to an XMLHttpRequest object or a string containing a path. Now we need to modify the existing JavaScript code to add call this function when appropriate:xhr.open('POST',url,true);
add the following line xhr.open('GET', gallery_url)
add the following line: var uri = item.uri['$ref'];
to:If third parties wish to consume and use the Gallery RESTful Services, then they must register the 3rd party application in order to gain OAuth 2.0 credentials, which can then be used to initiate an interactive process where the user can authorize the 3rd 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 differerent protocol flows that can be used by applications to acquire an access token. Oracle REST Data Services supports two of these flows:
The first step then is to register the 3rd party application, to demonstrate this, we will create a user representing the third party developer, and then use that user to register an application.
Tip The steps below create a user in the RESTEASY workspace user repository. Tip In addition to authenticating users defined in workspace user repositories Oracle REST Data Services can also authenticate against any user repository accessible from WebLogic or GlassFish. See the section below titled: ‘Authenticating against WebLogic and GlassFish user repositories’.
This is the user account for the 3rd party developer who wishes to register an application to access the RESTful Services.
We will first register the application to use the Implicit Grant OAuth 2.0 protocol flow.
https://server:port/i/oauthdemo/gallery.html
in the ‘Redirect URI’ field.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 the following URI:
https://server:port/ords/resteasy/oauth2/auth?response_type=token&\
client_id=CLIENT_IDENTIFIER&\
state=STATE
where:
There are a few important points to note about the Oracle REST Data Services OAuth 2.0 implementation:
When the above 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, 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
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:
Once the application has acquired an access token, it 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:
For example, a JavaScript based browser application might invoke the Gallery service as shown below:
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 above 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 the previously described JSON document listing the images in the gallery.
One of the key security concepts of web browsers is the ‘Same Origin Policy’. The policy 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. Similarily //example.com
is a different origin to https://example.com
because the protocol differs. Finally//example.com
is a different origin to //example.com:8080
because the port differs.
If for example 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.
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 instruct the browser to permit gallery.html to make an XMLHttpRequest to https://example.com/ords/resteasy/gallery/images/.
Configuring a RESTful Service for Cross Origin Resource Sharing
In the previous sections we demonstrated acquiring an accces token using the OAuth 2.0 Implicit protocol flow. In this section we demonstrate 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 us to exchange an authorization code for an access token.
For this example we will mimic this exchange process using cURL.
Register the client application
https://gallery.example.demo
in the ‘Redirect URI’ field.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 the following URI:
https://server:port/ords/resteasy/oauth2/auth?response_type=code&\
client_id=CLIENT_IDENTIFIER&\
state=STATE
where:
If the user approves the request then the browser will be redirected back to the registered redirect URI, the access token will be encoded in the query string portion of the URI:
https://gallery.example.demo?code=AUTHORIZATION_CODE&state=STATE
where:
Since 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, we can still see the authorization code value encoded in the URI. Make a note of the value of the code parameter, we will use it in the next section.
In the previous step we acquired an authorization code, in this step we will use cURL to exchange the authorization code for an access token.
To exchange an authorization code the application must make a HTTP request to the Oracle REST Data Services OAuth 2.0 token endpoint providing the authorization code and it’s client identifier and secret. If the credentials are correct Oracle REST Data Services will respond with a JSON document containing the access token. Note the application makes the HTTP request from it’s 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.
curl -i -d "grant_type=authorization_code&code=AUTHORIZATION_CODE" \
--user CLIENT_IDENTIFER:CLIENT_SECRET \
https://server:port/ords/resteasy/oauth2/token
where:
cURL translates the above commands into a 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 above 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 (more on refresh tokens in the next section). Once the application has acquired an access token, it 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
To extend the lifetime of an OAuth 2.0 session, a refresh token can be exchanged for a new access token with a new expiry time. Note 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:
curl -i -d "grant_type=refresh_token&refresh_token=REFRESH_TOKEN" \
--user CLIENT_IDENTIFER:CLIENT_SECRET \
https://server:port/ords/resteasy/oauth2/token
where:
cURL translates the above commands into a 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 above 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, any attempt to access a service using the old access token will fail.
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:
https://sharedhosting.com/alice/application
If another party Chuck is also able to host his application in the same origin, for example at:
https://sharedhosting.com/chuck/trouble
then it will be trivial for /chuck/trouble
to steal any access token acquired by /alice/application,
since they share the same origin: https://sharedhost.com,
and hence the browser will not prevent either application accessing the other’s data.
To protect against this scenario it is imperative that Alice’s application is deployed in it’s own origin, for example:
https://alice.sharedhosting.com/application
or
https://application.alice.sharedhosting.com
or
https://aliceapp.com
Oracle REST Data Services defines a small number of predefined user roles:
By default Oracle Application Express Users do not have any of the above roles. This means that out of the box, Application Express Users cannot:
This applies to all Application Express users, including Application Express developers and administrators. It is therefore important to remember to follow the steps below to add Application Express users to the appropriate user groups, so that they can successfully perform the above actions.
Since the Listener Administrator role enables a user to configure an Oracle REST Data Services instance, and therefore has the capability to affect all Application Express workspaces served through that instance, Application Express users are not permitted to acquire the Listener Administrator role.
To give an Application Express User any of the roles above, the user must be added to the equivalent Application Express user group. For example to give the RESTEASY_ADMIN user the RESTful Services role, perform the following steps:
Adding Application Express users to the appropriate user groups can be an easily overlooked step, or can become a repetitive task if there are many users to be managed.
To address these issues, it is possible to configure Oracle REST Data Services to automatically grant Application Express Users a pre-defined set of RESTful Service roles via manual configuration of the defaults.xml configuration file.
Oracle REST Data Services defines three property settings to configure roles:
For example to automatically give all users the RESTful Services privilege and all developers and adminstrators the OAuth2 Client Developer and SQL Developer roles, add the following to the defaults.xml configuration file:
RESTful Services
OAuth2 Client Developer, SQL Developer
Oracle REST Data Services must be restarted after making the above changes.
The built in RESTful Service role is a useful default for indicating users permitted to access protected RESTful Services.
In many cases though it will be necessary to define finer grained roles to accurately limit the set of users that may access a particular RESTful Service.
A RESTful Service ‘role’ is an Application Express User Group. Let’s create a user group to control access to the Gallery RESTful Service.
Once a user group has been created, it can be associated with a RESTful Privilege. Follow the steps below to associate the ‘Gallery Users’ role with the example.gallery privilege.
With these changes, users must have the ‘Gallery Users’ role to be able to access the Gallery RESTful Service.
Oracle REST Data Services can use APIs provided by WebLogic and GlassFish to verify credentials (username and password) and retrieve the set of groups/roles that the user is a member of.
In the examples below we walk through creating a user in the built in user repositories provided by WebLogic and GlassFish, and verifying that we can authenticate against that user.
It is beyond the scope of this document to describe how to integrate WebLogic and GlassFish with the many popular user repository systems such as LDAP repositories, suffice it to say that Oracle REST Data Services can authenticate against such repositories once WebLogic or GlassFish has been correctly configured. Please consult your application server documentation for more information on what user repositories are supported by your application server and how to configure access to these repositories.
Create a User
Now we have created a user named 3rdparty_dev2 and made it a member of a group named OAuth2 Client Developer. This means the user will acquire the OAuth2 Client Developer role and therefore will be authorized to register OAuth 2.0 applications.
Let’s verify that the user can be successfully authenticated.
Authenticating against GlassFish
Create a User
Now we have created a user named 3rdparty_dev2 and made it a member of a group named OAuth2 Client Developer. This means the user will acquire the OAuth2 Client Developer role and therefore will be authorized to register OAuth 2.0 applications.
Let’s verify that the user can be successfully authenticated.
Verify the GlassFish user
The examples above demonstrate configuring the built in user repositories of WebLogic and GlassFish. In this scenario we have full control over how user groups are named.
If a user is a member of a group with the exact same (case sensitive) name as a role then it is considered that the user has that role.
When integrating with existing user repositories the RESTful Service developer will often not have any control over the naming and organization of user groups used in the user repository.
In this scenario we need a mechanism to map from existing ‘phyiscal’ user groups defined in the user repository to the ‘logical’ roles defined by Oracle REST Data Services and/or RESTful Services.
In Oracle REST Data Services, this ‘physical’ group to ‘logical’ role mapping is performed by configuring a configuration file name role-mapping.xml.
About role-mapping.xml
role-mapping.xml is a Java XML Properties file where each property key defines a pattern that matches against a set of ‘phyiscal’ user groups, and each property value enumerates the ‘logical’ roles that the matched user group should be mapped to. It must be located in the same folder as the defaults.xml configuration file. The file must be manually created and edited.
Consider this example:
RESTful Services
This is role mapping is very straightforward, it states that any user who is a member of a group named: webdevs, is given the role RESTful Services meaning that all members of the webdevs group can invoke RESTful Services.
A mapping can apply more than one role to a group, for example:
RESTful Services, SQL Developer
This is rule gives members of the ‘webdevs’ group both RESTful Services and SQL Developer roles.
Having to explicitly map from each group to each role may not be scalable if the number of groups or roles is large. To address this concern we can parameterize rules, consider this example:
RESTful Services
In this example we are saying that any group name that ends with .webdevs will be mapped to the RESTful Services role. For example a group named: HQ.webdevs would match this rule, as would a group named: EAST.webdevs.
The syntax for specifying parameters in rules is the exact same as that used for URI Templates, the parameter name is delimited by curly braces ({}).
Any parameter defined in the group rule can also be dereferenced in the role rule. Consider this example:
{group}
In this example we are attempting to map the organizational unit component of an LDAP distinguished name to a logical role. We are stating that the organizational unit name maps directly to a logical role with exact same name. Note that we refer to a {userid} parameter but never actually use it. This is fine, in effect we are using {userid} as a wildcard flag.
For example the distinguished name: cn=jsmith,ou=Developers,dc=MyDomain,dc=com will be mapped to the logical role named Developers.
Sometimes to accomplish the desired role mapping it may be necessary to apply multiple intermediate rules, consider this example:
{group}
{group}
RESTful Services, SQL Developer
In this example we are attempting to map the organizational unit component of an LDAP distinguished name to some logical roles. Complicating matters is the fact that users can come from two different organizations resulting in differing distinguised name patterns.
We want to map engineers in both organizations to the RESTful Services and SQL Developer roles
java -jar ords.war
user command will have this role.https://server:port/ords/resteasy/ui/oauth2/clients/
https://server:port/ords/resteasy/ui/oauth2/clients/