How to Secure Oracle ORDS 25.4 APIs with OAuth (Client Credentials) – Step-by-Step Guide

If you are exposing REST endpoints using Oracle REST Data Services (ORDS), securing them properly is critical — especially for integrations, SaaS platforms, and enterprise APIs.

In this guide, we’ll walk through how to:

  • Create a REST module in ORDS 25.4

  • Protect it using ORDS Roles and Privileges

  • Enable OAuth client_credentials authentication

  • Generate tokens using curl

  • Call the secured API endpoint

This example uses safe placeholder values (no real credentials).

If you are running Oracle APEX in production, make sure your environment is professionally secured and managed — see our managed hosting options here:

👉 https://revion.com/oracle-apex-hosting/

Architecture Overview

We will create:

  • Module: myapi

  • Endpoint:
    POST https://api.example.com/ords/myapi/hello

  • ORDS Role: MYAPI_ROLE

  • ORDS Privilege: MYAPI_PRIV

  • OAuth client: myapi_client

  • Grant type: client_credentials

Step 1 – Enable the Schema for ORDS

🗃️
enable_schema.sql
begin
  ords.enable_schema(
    p_enabled             => true,
    p_schema              => 'MYAPI',
    p_url_mapping_type    => 'BASE_PATH',
    p_url_mapping_pattern => 'myapi',
    p_auto_rest_auth      => false
  );
  commit;
end;
/

Step 2 – Create the ORDS Module

🗃️
create_ords_module.sql
begin
  ords.define_module(
    p_module_name    => 'myapi',
    p_base_path      => '/myapi/',
    p_items_per_page => 25,
    p_status         => 'PUBLISHED'
  );
  commit;
end;
/

Step 3 – Create Template and POST Handler

🗃️
Create_Template_and_POST_Handler.sql
begin
  ords.define_template(
    p_module_name => 'myapi',
    p_pattern     => 'hello'
  );

  ords.define_handler(
    p_module_name => 'myapi',
    p_pattern     => 'hello',
    p_method      => 'POST',
    p_source_type => ords.source_type_plsql,
    p_source      => q'[
begin
  owa_util.mime_header('application/json', false);
  owa_util.http_header_close;
  htp.p('{"message":"Secure Hello World"}');
end;
]'
  );

  commit;
end;
/

At this point, the endpoint works  but it is not protected.

Step 4 – Protect the API with Role and Privilege

Create a role:

🗃️
create_role.sql
begin
  ords.create_role(p_role_name => 'MYAPI_ROLE');
  commit;
end;
/

Create privilege:

🗃️
create_priv.sql
begin
  ords.create_privilege(
    p_name        => 'MYAPI_PRIV',
    p_role_name   => 'MYAPI_ROLE',
    p_label       => 'My API Access'
  );
  commit;
end;
/

Map privilege to URL:

🗃️
map_priv.sql
begin
  ords.create_privilege_mapping(
    p_privilege_name => 'MYAPI_PRIV',
    p_pattern        => '/myapi/*'
  );
  commit;
end;
/

Attach privilege to module:

🗃️
attach_priv_2_module.sql
begin
  ords.set_module_privilege(
    p_module_name    => 'myapi',
    p_privilege_name => 'MYAPI_PRIV'
  );
  commit;
end;
/

Now, calling the endpoint without authentication returns:  HTTP/1.1 401 Unauthorized

Step 5 – Enable OAuth Client Credentials

Your DBA must grant:

grant execute on ords_metadata.oauth_admin to MYAPI;
grant execute on ords_metadata.oauth to MYAPI;

Then create OAuth client:

🗃️
create_oauth_client.sql
begin
  ords_metadata.oauth_admin.create_client(
    p_schema          => 'MYAPI',
    p_name            => 'myapi_client',
    p_grant_type      => 'client_credentials',
    p_owner           => 'MYAPI',
    p_description     => 'Client credentials for My API',
    p_origins_allowed => null,
    p_redirect_uri    => null,
    p_support_email   => 'support@example.com',
    p_support_uri     => 'https://example.com',
    p_privilege_names => 'MYAPI_PRIV'
  );
  commit;
end;
/

Grant role to the OAuth client:

🗃️
grant_role_2_oauth_client.sql
begin
  ords_metadata.oauth_admin.grant_client_role(
    p_schema      => 'MYAPI',
    p_client_name => 'myapi_client',
    p_role_name   => 'MYAPI_ROLE'
  );
  commit;
end;
/

Retrieve client_id and client_secret:

				
					select name, client_id, client_secret
from ords_metadata.user_ords_clients
where upper(name) = 'MYAPI_CLIENT';
				
			

Step 6 – Get OAuth Token Using curl

				
					curl -s -u '<client_id>:<client_secret>' \
  -d 'grant_type=client_credentials' \
  "https://api.example.com/ords/myapi/oauth/token"
				
			

Example response:

				
					{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600
}
				
			

Step 7 – Call the Secured Endpoint

				
					TOKEN="<access_token>"

curl -i -X POST \
  -H "Authorization: Bearer $TOKEN" \
  "https://api.example.com/ords/myapi/hello"
				
			

Expected response:

				
					HTTP/1.1 200
Content-Type: application/json

{"message":"Secure Hello World"}
				
			

Why Use OAuth Client Credentials for ORDS APIs?

  • No database password exposure
  • Token expiration control
  • Clean integration model for SaaS
  • Easy secret rotation
  • Industry-standard OAuth 2.0

Best Practices for Production ORDS Security

  • Always use HTTPS
  • Protect modules with privilege mapping
  • Rotate client secrets regularly
  • Avoid exposing raw database schemas
  • Use a managed ORDS + APEX hosting provider

If you’re running Oracle APEX or ORDS in production and want enterprise-grade security, monitoring, patching, and cloud hardening:

👉 https://revion.com/oracle-apex-hosting/

Scroll to Top