Introduction

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.

Overview

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

Authentication

Users can be authenticated using the following mechanisms:

First Party Authentication

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.

Third Party Authentication

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.

Two-legged and Three-legged OAuth

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.

About Privileges

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.

About Users & 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.

About the file based user repository

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

Tutorial

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.

When to use each flow

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.

Assumptions

For the ease of demonstration this example makes the following assumptions

Create Resource

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)
  );

Verify Resource

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.

Create Privilege

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.

Verify 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                                                                                                                                                                                                                                               

Now the Privilege exists, but it does not yet protect any resources, to do that we must configure a privilege mapping

Associate Privilege with Resources

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;

Verify Mapping

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/*

Verify Access Control

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.

Verify First Party Access

Create an End User

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.

Sign in as the End User

Enter the following URL in a web browser:

https://example.com/ords/resteasy/examples/employees/

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.

Register OAuth Client

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.

Verify OAuth Client

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:

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.

Grant OAuth Client required role

Execute the following PL/SQL commands, while connected as schema RESTEASY:

exec oauth.grant_client_role(
    'Client Credentials Example',
    'HR Administrator');
commit;

Verify Client Role

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               

Obtain OAuth access token using Client Credentials Flow

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

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.

Access protected resource using access token

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/

The following results should be shown:

HTTP/1.1 200 OK
Content-Type: application/json

{
 TBC
}

Register Client for Authorization Code Flow

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.

Verify OAuth Client

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:

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.

Obtain OAuth access token using Authorization Code Flow

Tip To complete this section you must previously have created an end user (hr_admin), see the section titled Create End User.

Obtain OAuth Authorization code

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 :

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:

If the client_id is recognised then a sign in prompt will be displayed.

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.

Obtain OAuth 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

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 protected resource using access token

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/

The following results should be shown:

HTTP/1.1 200 OK
Content-Type: application/json

{
 TBC
}

Extend session using Refresh Token

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

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:

Register Client for Implicit Flow

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.

Verify OAuth Client

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:

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.

Obtain OAuth access token using Implicit Flow

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 :

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:

If the client_id is recognised then a sign in prompt will be displayed.

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.

Access protected resource using access token

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/

The following results should be shown:

HTTP/1.1 200 OK
Content-Type: application/json

{
 TBC
}