A web page can call an API on another origin only if the API allows it through CORS headers. ORDS answers cross-origin requests for REST modules, and each module can limit the origins it accepts. This guide checks the default behavior, restricts a module to one web application, and shows the preflight and refusal responses.
Before You Start
You need ORDS installed and running against your database, and a schema to work in. The examples use NIMBUS, the sample schema of a fictional airline, which you can install with the scripts in the setup/nimbus folder of the Oracle Database 26ai code repository on GitHub. The schema comes from Oracle Database 26ai SQL and PL/SQL Book.
ORDS in the examples answers at https://localhost:8443/ords/, and NIMBUS is REST-enabled with the URL alias nimbus. Replace the host and port with your own ORDS address. The curl commands use -k because the test server has a self-signed certificate; leave it out when your certificate is trusted. JSON responses are formatted for reading; ORDS returns them on one line.
The examples use the network module from How to Create a REST Module, Template, and Handler with ORDS.DEFINE_MODULE. curl sends the Origin header that a browser would send.
Syntax
ords.set_module_origins_allowed(p_module_name => 'module',
p_origins_allowed => 'https://app.example.com'); -- comma-separated
ords.set_module_origins_allowed(p_module_name => 'module', p_origins_allowed => null); -- any originThe Default: Any Origin
Without a list, the module answers any origin and echoes it back:
Example:
curl -k -D - -o /dev/null -H "Origin: https://app.nimbus.example" \ https://localhost:8443/ords/nimbus/network/routes/7
Output (CORS headers):
HTTP/1.1 200 OK Vary: Origin Access-Control-Expose-Headers: Content-Type, ETag, Vary, Access-Control-Allow-Origin, Access-Control-Allow-Credentials Access-Control-Allow-Origin: https://app.nimbus.example Access-Control-Allow-Credentials: true
Allow One Origin
Example:
begin
ords.set_module_origins_allowed(p_module_name => 'network',
p_origins_allowed => 'https://app.nimbus.example');
commit;
end;
/
select name, origins_allowed from user_ords_modules where name = 'network';Output:
PL/SQL procedure successfully completed. NAME ORIGINS_ALLOWED __________ _____________________________ network https://app.nimbus.example
The allowed origin still gets its CORS headers:
Output:
== allowed origin HTTP/1.1 200 OK Vary: Origin Access-Control-Expose-Headers: Content-Type, ETag, Vary, Access-Control-Allow-Origin, Access-Control-Allow-Credentials Access-Control-Allow-Origin: https://app.nimbus.example Access-Control-Allow-Credentials: true
Any other origin is refused:
Output:
== other origin
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Length: 829
{
"code": "Forbidden",
"title": "Forbidden",
"message": "The request cannot be processed because it failed cross origin request validation ",
"o:errorCode": "ORDS-13002",
"cause": "This resource does not support Cross Origin Sharing requests, or the request Origin is not authorized to access this resource. ",
"action": "If ords is being reverse proxied ensure the front end server is propagating the host name, scheme and port correctly. If using mod_proxy ensure ProxyPreserveHost is set to On. If using SAML with Oracle APEX, ensure security.externalSessionTrustedOrigins is correctly configured. If using a RESTful Service ensure the Origins Allowed value is correctly configured",
"type": "tag:oracle.com,2020:error/Forbidden",
"instance": "tag:oracle.com,2020:ecid/FZ_NRV23BO9kRmS8uJT4cw"
}The Preflight Request
Before some requests, browsers send an OPTIONS preflight asking which methods are allowed:
Example:
curl -k -D - -o /dev/null -X OPTIONS \ -H "Origin: https://app.nimbus.example" \ -H "Access-Control-Request-Method: GET" \ https://localhost:8443/ords/nimbus/network/routes/7
Output:
== preflight HTTP/1.1 200 OK Access-Control-Allow-Origin: https://app.nimbus.example Access-Control-Allow-Credentials: true Access-Control-Max-Age: 3600 Access-Control-Allow-Methods: GET
ORDS lists the methods the template has handlers for, GET here, and lets the browser cache the answer for an hour.
Remove the Restriction
Example:
begin
ords.set_module_origins_allowed(p_module_name => 'network',
p_origins_allowed => null); -- back to any origin
commit;
end;
/Things to Know
- CORS is enforced by browsers: server-side clients such as curl without an Origin header are not affected, so CORS is not access control.
- List exact origins, with scheme and port, such as https://app.example.com:8443.
- As with other ORDS changes, the new list takes effect after ORDS refreshes its cache, a few seconds in the test.
Related Guides
Conclusion
ORDS REST modules answer cross-origin requests from any origin until you restrict them with ORDS.SET_MODULE_ORIGINS_ALLOWED. List the web applications that may call the API, test with an Origin header and a preflight, and use privileges, not CORS, to control access.
