This document describes how to configure secure access to RESTful Services
Editor's Note: This draft of the document serves the following purposes:
It may be subject to change as the API is implemented. Some responses are not fully defined yet, but the required information is described in the bullet points explaining the structure of each response. The missing content will be added in the next revision.
RESTful APIs consist of Resources, each Resource having a unique URI. A set of Resources can be protected by a Privilege. A Privilege defines the set of Roles, at least one of which an authenticated user MUST possess to access a Resource protected by a Privilege.
Configuring a resource to be protected by a particular Privilege requires creating a Privilege Mapping. A Privilege Mapping defines a set of patterns that identifies the Resources that a Privilege protects
Users can be authenticated using the following mechanisms:
A First Party is the author of a RESTful API. A First Party Application is a Web Application deployed on the same Web Origin, as the RESTful API. A First Party Application is able to authenticate and authorise itself to the RESTful API using the same cookie session that the Web Application is using. The first party application has full access to the RESTful API.
A Third Party is any party other than the author of a RESTful API. A third party application cannot be trusted in the same way as a first party application, therefore there must be a mediated means to selectively grant the third party application limited access to the RESTful API.
The OAuth 2.0 protocol defines a number of flows to provide conditional and limited access to a RESTful API. In short, the third party application must first be registered with the first party, and then the first party (or an end-user of the first party RESTful service) approves the third party application for limited access to the RESTful API, by issuing the third party application with a short lived access token.
Some flows in OAuth are defined as 'two-legged' and others as 'three-legged'.
'Two-legged' OAuth involves two parties, the party calling the RESTful API (the third party application), and the party providing the RESTful API. Two legged flows are used in server to server interactions where an end-user does not need to approve access to the RESTful API. In OAuth 2.0 this flow is called the client credentials flow. It is most typically used in business to business scenarios.
'Three-legged' OAuth involves three parties, the party calling the RESTful API, the party providing the RESTful API, and an end-user party, who owns/manages the data that the RESTful API provides access to. Three legged flows are used in client to server interactions where an end-user must approve access to the RESTful API. In OAuth 2.0 the authorization code flow and the implicit flow are three legged flows. These flows are typically used in business to consumer scenarios.
When an OAuth client is registering with a RESTful API it can safely indicate which resources protected by three-legged flows it requires access to, the end-user has the final approval decision about whether to grant the client access.
However for resources protected by two-legged flows, the owner of the RESTful API must approve which resources each client is authorized to access.
A Privilege consists of the following data:
For Two-legged OAuth the third party application (called a client in OAuth terminology) must possess at least one of the required roles.
For Three-legged OAuth the end user that approves the access request from the third party application must possess at least one of the required roles.
A Privilege enumerates a set of Roles, but where are these Roles defined? What about the users that possess these roles? Where are they defined?
ORDS delegates the task of user management to the Application Server on which ORDS is deployed. ORDS is able to authenticate users defined/managed by the Application Server and to identify the roles/groups the authenticated user belongs to. It is the responsibility of the party deploying ORDS on an Application Server to also configure the user repository on the Application Server.
Due to the large variety of ways by which an Application Server can be configured to define a user repository or integrate with an existing user repository, it is outside the scope of this document to describe how to configure a user repository in an Application Server.
Please consult your Application Server documentation for information on this topic.
To aid demonstrating and understanding the security features of ORDS, ORDS does include a simple file based user repository mechanism. This user repository is only intended for the purposes of demonstration and testing, and is not supported for production use.
Please consult the command line user
command for more information on how to create a user in this repository.
java -jar ords.war help user
java -jar ords.war user <user> <roles>
Arguments:
<user> The userid of the user
<roles>* The zero or more roles the user has
This tutorial demonstrates creating a privilege to protect a set of resources, and accessing the protected resource with the following OAuth flows:
We also demonstrate access the resource using First Party cookie based authentication.
Use First Party Cookie Authentication when accessing a RESTful API from a web application hosted on the same origin as the RESTful API.
Use the Authorization Code flow when you need to permit third party web applications to access a RESTful API and the third party application has it's own web server where it can keep it's client credentials secure. This is the typical situation for most web-applications, and so this flow should be the most preferred option, providing the most security and best user experience as the third party application can use refresh tokens to extend the life of a user session without having to prompt the user to reauthorize the application.
Use the Implicit flow when the third party application does not have a web server where it can keep it's credentials secure. This flow is useful for third party single page based applications. Since refresh tokens cannot be issued in the Implicit flow, the user will be prompted more frequently to authorise the application.
Native mobile or desktop applications should use the Authorization Code or Implicit flows. They will need to display the sign in and Authorization prompts in a web browser view, and capture the access token from the web browser view at the end of the Authorization process.
Use the Client Credentials flow when you need to give a third party application direct access to a RESTful API without requiring a user to approve access to the data managed by the RESTful API. The third party application must be a server based application that can keep it's credentials secret. The Client Credentials flow must not be used with a native application, as the client credentials can always be discovered in the native executable.
For the ease of demonstration this example makes the following assumptions
https://example.com/ords/
RESTEASY
has been enabled for use with ORDS and it's RESTful APIs are exposed under: https://example.com/ords/resteasy/
https://example.com/ords/resteasy/examples/employees/
Execute the following PL/SQL commands, while connected as schema RESTEASY
begin
ords.create_service(
p_module_name => 'examples.employees' ,
p_base_path => '/examples/employees/',
p_pattern => '.' ,
p_items_per_page => 7,
p_source => 'select * from emp order by empno desc');
commit;
end;
This command creates the example /examples/employees/
resource which we will protect with a Privilege in the following steps.
Note that this assumes the RESTEASY
schema contains a database table named EMP
. Below is the DDL for the EMP
table:
create table emp (
empno number(4,0),
ename varchar2(10 byte),
job varchar2(9 byte),
mgr number(4,0),
hiredate date,
sal number(7,2),
comm number(7,2),
deptno number(2,0),
constraint pk_emp primary key (empno)
);
Execute the following cURL command:
curl -i https://example.com/ords/resteasy/examples/employees/
The following results should be shown (_content formatted and some results elided for clarity_):
Content-Type: application/json
Transfer-Encoding: chunked
{
"items":
[
{"empno":7934,"ename":"MILLER","job":"CLERK","mgr":7782,"hiredate":"1982-01-23T00:00:00Z","sal":1300,"comm":null,"deptno":10},
...
],
"hasMore":true,
"limit":7,
"offset":0,
"count":7,
"links":
[
{"rel":"self","href":"https://example.com/ords/resteasy/examples/employees/"},
{"rel":"describedby","href":"https://example.com/ords/resteasy/metadata-catalog/examples/employees/"},
{"rel":"first","href":"https://example.com/ords/resteasy/examples/employees/"},
{"rel":"next","href":"https://example.com/ords/resteasy/examples/employees/?offset=7"}
]
}
Now the example resource exists, but it is available to any user, without any access control. To secure access to the resource we need to create a Privilege and associate it with the /examples/employees/
resource.
Execute the following PL/SQL commands, while connected as schema RESTEASY
begin
ords.create_role('HR Administrator');
ords.create_privilege(
p_name => 'example.employees',
p_role_name => 'HR Administrator',
p_label => 'Employee Data',
p_description => 'Provide access to employee HR data');
commit;
end;
This command creates a Role and a Privilege (belonging to the RESTEASY
schema).
The values should be plain text that identify the name and purpose of the privilege.
Ensure the Privilege was created correctly by querying the USER_ORDS_PRIVILEGES
view.
Execute the following SQL, while connected as schema RESTEASY
select id,name from user_ords_privileges where name = 'example.employees';
The following results should be shown:
ID NAME
----- -----------------
10260 example.employees
ID
value will vary from database to database, but the NAME
value should be as shown above.Now the Privilege exists, but it does not yet protect any resources, to do that we must configure a privilege mapping
Execute the following PL/SQL commands, while connected as schema RESTEASY
:
begin
ords.create_privilege_mapping(
p_privilege_name => 'example.employees',
p_pattern => '/examples/employees/*');
commit;
end;
Ensure the Privilege Mapping was created correctly by querying the USER_ORDS_PRIVILEGES
view.
Execute the following SQL, while connected as schema RESTEASY
select id, name, pattern from user_ords_privilege_mappings;
Output similar to the following should be shown:
PRIVILEGE_ID NAME PATTERN
------------ -------------------- ---------------------
10260 example.employees /examples/employees/*
ID
value will vary from database to database, but the NAME
and PATTERN
values should be as shown above.Execute the following cURL command:
curl -i https://example.com/ords/resteasy/examples/employees/
The following results should be shown (_body of the HTML response elided for clarity_):
HTTP/1.1 401 Unauthorized
Content-Type: text/html
Transfer-Encoding: chunked
<!DOCTYPE html>
<html>
...
</html>
This confirms the /examples/employees/
resource is now protected by the example.employees
Privilege.
Create a test user with the HR Administrator
role, required to access the examples.employees
Privilege using the file based user repository, execute the following command, in a command prompt
java -jar ords.war user "hr_admin" "HR Administrator"
You will be prompted for a password, enter a password and confirm the password.
Enter the following URL in a web browser:
https://example.com/ords/resteasy/examples/employees/
hr_admin
user and click the sign in button.https://example.com/ords/resteasy/examples/employees/
and the JSON document is displayed.This confirms that the protected resource can be accessed via first party authentication, next we will access the resource using third party authentication, using OAuth.
Execute the following PL/SQL commands, while connected as schema RESTEASY
:
begin
oauth.create_client(
p_name => 'Client Credentials Example',
p_grant_type => 'client_credentials',
p_privilege_names => 'example.employees',
p_support_email => 'support@example.com');
commit;
end;
This command registers a client named Client Credentials Example
, to access the examples.employees
privilege using the client credentials OAuth flow.
Execute the following SQL, while connected as schema RESTEASY
select client_id,client_secret from user_ords_clients where name = 'Client Credentials Example';
Output similar to the following should be shown:
CLIENT_ID CLIENT_SECRET
-------------------------------- ------------------------
o_CZBVkEMN23tTB-IddQsQ.. 4BJXceufbmTki-vruYNLIg..
Note:
client_id
and client_secret
values represent the secret credentials for the OAuth Client. These values must be noted and kept secure. You can think of them as the userid and password for the client application.The client is now registered and has requested access to the examples.employees
privilege. To grant access to the privilege, the client must be granted the role required by the privilege.
Execute the following PL/SQL commands, while connected as schema RESTEASY
:
exec oauth.grant_client_role(
'Client Credentials Example',
'HR Administrator');
commit;
Execute the following SQL, while connected as schema RESTEASY
select * from user_ords_client_roles where name = 'Client Credentials Example';
Output similar to the following should be shown:
CLIENT_ID CLIENT_NAME ROLE_ID ROLE_NAME
---------- ------------------------------ ---------- ------------------------------
10286 Client Credentials Example 10222 HR Administrator
The OAuth protocol specifies the HTTP request that must be used to create an access token using the [client credentials flow][rfc6749-4.4.].
The request must be made to a well known URL, called the token endpoint. For ORDS the path of the token endpoint is always oauth/token
, relative to the root path of the schema being accessed. The token endpoint for this example is:
https://example.com/ords/resteasy/oauth/token
Execute the following cURL command:
curl -i --user clientId:clientSecret --data "grant_type=client_credentials" https://example.com/ords/resteasy/oauth/token
clientId
with the value of the client_id
shown in USER_ORDS_CLIENTS
for Client Credentials Client
.clientSecret
with the value of the client_secret
shown in USER_ORDS_CLIENTS
for Client Credentials Client
.Output similar to the following should be shown:
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"token_type": "bearer",
"expires_in":3600
}
Note the access token is of type bearer
, and the value is specified by the access_token
field. This value will be different for every request, note it's value, as it will be used in the next section. The expires_in
value indicates the number of seconds until the access token expires, in this case the token will expire in one hour.
Now that we have an access token, we can use it to access the protected /examples/employees/
resource.
Execute the following cURL command:
curl -i -H"Authorization: Bearer accessToken" https://example.com/ords/resteasy/examples/employees/
accessToken
with the value of the access_token
field shown in the previous step.The following results should be shown:
HTTP/1.1 200 OK
Content-Type: application/json
{
TBC
}
Execute the following PL/SQL commands, while connected as schema RESTEASY
:
exec oauth.create_client(
'Authorization Code Example',
'client_credentials',
'example.employees',
'http://example.org/auth/code/example/',
'support@example.org',
'http://example.org/support',
'Sample Application for demonstrating Authorization Code Flow');
commit;
This command registers a client named Authorization Code Example
, to access the examples.employees
privilege using the Authorization code OAuth flow.
example.org
example web service.example.org
service.Execute the following SQL, while connected as schema RESTEASY
select * from user_ords_clients where name = 'Authorization Code Example';
The following results should be shown:
TBC
Note:
client_id
and client_secret
values represent the secret credentials for the OAuth Client. These values must be noted and kept secure.The client is now registered and has requested access to the examples.employees
privilege. To grant access to the privilege, an end user must approve access.
Tip To complete this section you must previously have created an end user (hr_admin
), see the section titled Create End User.
The first step in the Authorization code flow is for the end user to be prompted (via a web page) to sign in and approve access to the third party application. The third party application initiates this process by directing the user to the OAuth Authorization Endpoint. For ORDS the path of the authorization endpoint is always oauth/auth
, relative to the root path of the schema being accessed. The token endpoint for this example is:
https://example.com/ords/resteasy/oauth/auth
The OAuth 2.0 protocol specifies that the Authorization request URI must include certain parameters in the query string :
response_type
parameter must have a value of code
client_id
parameter must contain the value of the applications client identifier. This is the client_id
value determined in the previous sectionstate
parameter must contain a unique unguessable value. This value serves two purposes:To initiate the Authorization request enter the following URL in a web browser:
https://example.com/ords/resteasy/oauth/auth?response_type=code&client_id=cliendId&state=uniqueRandomValue
Note:
clientId
with the value of the client_id
column that was noted in the previous sectionuniqueRandromValue
with a unique unguessable value. The client application must remember this value and verify it against the state
parameter returned as part of the redirect at the end of the Authorization flow.If the client_id
is recognised then a sign in prompt will be displayed.
hr_admin
end user, and click the 'Sign In' button.The browser will now be redirected to the redirect URI specified when the client was registered. The redirect URI will include the Authorization code in the query string portion of the URI. It will also include the same state
parameter value that the client provided at the start of the flow. The redirect URI will look like the following:
http://example.org/auth/code/example/?code=D5doeTSIDgbxWiWkPl9UpA..&state=uniqueRandomValue
The client application must verify the value of the state
parameter and then note the value of the code
parameter, it will be used in the next section to obtain an access token.
Now that the third party application has an Authorization code, it must exchange it for an access token. The third party application's server must make a HTTPS request to the Token Endpoint.
We will mimic the server making this request using a cURL command, execute the following command in a command prompt:
curl --user clientId:clientSecret --data "grant_type=authorization_code&code=authorizationCode" https://example.com/ords/resteasy/oauth/token
clientId
with the value of the client_id
shown in USER_ORDS_CLIENTS
for Client Credentials Client
.clientSecret
with the value of the client_secret
shown in USER_ORDS_CLIENTS
for Client Credentials Client
.authorizationCode
with the value of the Authorization code noted in the previous section (the value of the code
parameter).The following results should be shown:
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "psIGSSEXSBQyib0hozNEdw..",
"token_type": "bearer",
"expires_in":3600,
"refresh_token": "aRMg7AdWPuDvnieHucfV3g.."
}
Note:
access_token
field.refresh_token
field. This value can be used to extend the user session without requiring the user to reauthorise the third party application.Now that we have an access token, we can use it to access the protected /examples/employees/
resource.
Execute the following cURL command:
curl -H"Authorization: Bearer accessToken" https://example.com/ords/resteasy/examples/employees/
accessToken
with the value of the access_token
field shown in the previous step.The following results should be shown:
HTTP/1.1 200 OK
Content-Type: application/json
{
TBC
}
At any time the third party application can use the refresh token value supplied at the same time as the access token to generate a new access token with a new lifetime. This enables the third party application to extend the user session at will. To do this the third party application's server must make a HTTPS request to the Token Endpoint.
We will mimic the server making this request using a cURL command, execute the following command in a command prompt:
curl --user clientId:clientSecret --data "grant_type=authorization_code&code=authorizationCode" https://example.com/ords/resteasy/oauth/token
clientId
with the value of the client_id
shown in USER_ORDS_CLIENTS
for Client Credentials Client
.clientSecret
with the value of the client_secret
shown in USER_ORDS_CLIENTS
for Client Credentials Client
.authorizationCode
with the value of the Authorization code noted in the previous section (the value of the code
parameter).The following results should be shown:
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "psIGSSEXSBQyib0hozNEdw..",
"token_type": "bearer",
"refresh_token": "aRMg7AdWPuDvnieHucfV3g..",
"expires_in": 3600
}
Note:
access_token
field.refresh_token
field. This value can be used to extend the user session without requiring the user to reauthorise the third party application.Execute the following PL/SQL commands, while connected as schema RESTEASY
:
exec oauth.create_client(
'Implicit Example',
'client_credentials',
'example.employees',
'http://example.org/implicit/example/',
'support@example.org',
'http://example.org/support',
'Sample Application for demonstrating Implicit Flow');
commit;
This command registers a client named Implicit Example
, to access the examples.employees
privilege using the implicit OAuth flow.
example.org
example web service.example.org
service.Execute the following SQL, while connected as schema RESTEASY
select * from user_ords_clients where name = 'Implicit Example';
The following results should be shown:
TBC
Note:
client_id
values identifies the client application. This value should be noted.The client is now registered and has requested access to the examples.employees
privilege. To grant access to the privilege, an end user must approve access.
Tip To complete this section you must previously have created an end user (hr_admin
), see the section titled Create End User.
The first step in the implicit flow is for the end user to be prompted (via a web page) to sign in and approve access to the third party application. The third party application initiates this process by directing the user to the OAuth Authorization Endpoint. For ORDS the path of the authorization endpoint is always oauth/auth
, relative to the root path of the schema being accessed. The token endpoint for this example is:
https://example.com/ords/resteasy/oauth/auth
The OAuth 2.0 protocol specifies that the Authorization request URI must include certain parameters in the query string :
response_type
parameter must have a value of token
client_id
parameter must contain the value of the applications client identifier. This is the client_id
value determined in the previous sectionstate
parameter must contain a unique unguessable value. This value serves two purposes:To initiate the Authorization request enter the following URL in a web browser:
https://example.com/ords/resteasy/oauth/auth?response_type=token&client_id=cliendId&state=uniqueRandomValue
Note:
clientId
with the value of the client_id
column that was noted in the previous sectionuniqueRandromValue
with a unique unguessable value. The client application must remember this value and verify it against the state
parameter returned as part of the redirect at the end of the Authorization flow.If the client_id
is recognised then a sign in prompt will be displayed.
hr_admin
end user, and click the 'Sign In' button.The browser will now be redirected to the redirect URI specified when the client was registered. The redirect URI will include the access token in the fragment portion of the URI. It will also include the same state
parameter value that the client provided at the start of the flow. The redirect URI will look like the following:
http://example.org/auth/code/example/#access_token=D5doeTSIDgbxWiWkPl9UpA..&type=bearer&expires_in=3600&state=uniqueRandomValue
The client application must verify the value of the state
parameter and then store the value of the access_token
parameter.
Now that we have an access token, we can use it to access the protected /examples/employees/
resource.
Execute the following cURL command:
curl -H"Authorization: Bearer accessToken" https://example.com/ords/resteasy/examples/employees/
accessToken
with the value of the access_token
field shown in the previous step.The following results should be shown:
HTTP/1.1 200 OK
Content-Type: application/json
{
TBC
}