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).
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
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
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
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:
begin
ords.create_role(p_role_name => 'MYAPI_ROLE');
commit;
end;
/Create privilege:
begin
ords.create_privilege(
p_name => 'MYAPI_PRIV',
p_role_name => 'MYAPI_ROLE',
p_label => 'My API Access'
);
commit;
end;
/Map privilege to URL:
begin
ords.create_privilege_mapping(
p_privilege_name => 'MYAPI_PRIV',
p_pattern => '/myapi/*'
);
commit;
end;
/Attach privilege to module:
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, or whoever operates your Oracle APEX and ORDS hosting, must grant:
grant execute on ords_metadata.oauth_admin to MYAPI;
grant execute on ords_metadata.oauth to MYAPI;Then create OAuth client:
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:
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