How to Enable CORS for ORDS APIs

Let web applications on other origins call your ORDS APIs, restrict a module to the origins you trust, and test preflights.

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 origin

The 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.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE, author of four books on Oracle APEX, SQL and PL/SQL, and Oracle Forms, and a software developer building Oracle database applications since 2001.

guest

0 Comments
Oldest
Newest Most Voted