Generating a bearer token with Curl using Oracle ORDS and the OAuth 2.0 Client Credentials flow
The manual step-by-step process

In this blog I cover the manual steps to resolve a valid bearer token from the OAuth Authentication Server that comes out-of-the-box with Oracle ORDS, a.k.a. Oracle REST Data Services. Nevertheless, the curl steps are almost identical to any OAuth 2.0 Authentication Server, although some value specifics might be a little different.
After the bearer token - or access token - has been generated by ORDS, it can be used in subsequent REST API requests using the same ORDS Authentication server.
Create an OAuth 2.0 client
For the purpose of this blog you generate an OAuth 2.0 client that uses the Client Credentials flow. Most parameters int the example below are free-text values and you can fill in any value you like, however, parameter p_grant_type and p_privilege_names should be set accordingly as shown below, to guarantee correct results for this demo.
begin
oauth.create_client(
p_name => 'OAuth 2.0 Client Name',
p_grant_type => 'client_credentials',
p_owner => 'Your company name',
p_description => 'An OAuth 2.0 client for demonstration',
p_support_email => 'user@email.com',
p_privilege_names => null
);
end;
/
commit
/
Run the next query to validate that the OAuth 2.0 client has been created, you can see the client_id - in this example abc - and client_secret - in this example def - in the results. In this blog I use abc and def, but in reality these are generated values by ORDS.
select name, client_id, client_secret
from user_ords_clients;
NAME CLIENT_ID CLIENT_SECRET
------------------------ ------------ ----------------
OAuth 2.0 Client Name abc def
Alright, now that the client has been created, we can continue to execute a request to resolve an access token.
Basic Authorization Header
To execute a request to an OAuth Authentication Server, a basic authorization header is needed.
The basic authentication header must contain a base64 encoded string, where the client_id and client_secret must be separated by a colon (:) and encoded using the base64 encoding mechanism.
Client ID: abc
Client Secret: def
Base64 encode: utl_encode.base64_encode()
RAW casting: The function utl_encode.base64_encode() uses RAW datatypes as input and output parameters, so we need to cast the source string to and from RAW - before and after encoding.
select utl_raw.cast_to_varchar2(
utl_encode.base64_encode(
utl_raw.cast_to_raw( 'abc:def')
)
) as encoded_string
from dual
/
ENCODED_STRING
_________________
YWJjOmRlZg==
Now that we have the base64 encoded string containing the client id and client secret, we must place it in the curl command as shown below.
Location: refers to the Oracle ORDS authentication server.
Header Content-Type: specifies the mimetype of the data we are submitting.
Header Authorization: specifies the Basic authentication type followed by the base64 encoded string.
Data: specifies that we want to use the OAuth Client Credentials flow to authenticate.
curl \
--locationĀ 'https://<ords_server>/ords/<schema_alias>/oauth/token'Ā \
--headerĀ 'Content-Type:Ā application/x-www-form-urlencoded'Ā \
--headerĀ 'Authorization:Ā BasicĀ YWJjOmRlZg=='Ā \
--data-urlencodeĀ 'grant_type=client_credentials'
When the curl command above is executed, it basically asks the token authentication server: āHello Authorization Server, I am client abc, Iām authenticating using my client credentials (using secret def). If valid and authorized, please issue me an access token for use in subsequent API requests.ā
If everything is correct, a response with the access token will be received by the issuer. It contains the access token itself, the token type and the duration in seconds that the access token is valid.
{
"access_token" : "bQ4z6urrNEbpk7-85M0nix",
"token_type" : "bearer",
"expires_in" : 3600
}
Send credentials with --user flag
Although sending credentials by the Authorization Basic header is highly recommended, if base64 encoding is too complex or isn't possible at the environment, an alternative solution can be to send the client:secret credentials with the --user flag.
curl \
--locationĀ 'https://<ords_server>/ords/<schema_alias>/oauth/token'Ā \
--headerĀ 'Content-Type:Ā application/x-www-form-urlencoded'Ā \
--user "abc:def" \
--data-urlencodeĀ 'grant_type=client_credentials'
In that case there is no manual base64 encoding necessary as Curl now automatically handles the base64 encoding for you, while sending the credentials in the Authorization: Basic header as many OAuth providers require.
Send credentials in the body
Another alternative is to send the credentials in the body as can be seen in the example below.
curl \
--location 'https://<ords_server>/ords/<schema_alias>/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=abc' \
--data-urlencode 'client_secret=def'
but this fails when Oracle ORDS is used as the authorization server, since ORDS doesn't support sending credentials in the body (yet?).
401 Unauthorized
If your credentials are not correct, the token request itself will result in a 401 Unauthorized response.
{
"code" : "Unauthorized",
"message" : "Unauthorized",
"type" : "tag:oracle.com,2020:error/Unauthorized",
"instance" : "tag:oracle.com,2020:ecid/Trx2zrNWzWt3S9op4Vu5kg"
}
Benefits
By using curl for the authentication part of the web request, the authentication step is separated from the actual data request and will now reveal errors or configuration mismatches during that specific phase. What happens under-the-hood is now visible, which makes debugging your REST call or OAuth2 authentication request way simpler than without.
More information on failures
To see more information about the cause of a failure, add --verbose or --trace-ascii . to the Curl command and it will show you the detailed information you are looking for to solve your issue.
* Host <ords_server>:443 was resolved.
* IPv6: (none)
* IPv4: 192.168.0.1
* Trying 192.168.0.1:443...
* schannel: disabled automatic use of client certificate
* ALPN: curl offers http/1.1
* ALPN: server accepted http/1.1
* Established connection to <ords_server> (192.168.0.1 port 443) from 192.168.0.2 port 58040
* using HTTP/1.x
> POST /ords/<schema_alias>/oauth/token
> HTTP/1.1
> Host: <ords_server>
> User-Agent: curl/8.18.0
> Accept: /
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 103
* upload completely sent off: 103 bytes
* schannel: server close notification received (close_notify)
< HTTP/1.1 401
< Date: Mon, 23 Mar 2026 12:00:00 GMT
< Server: Apache/2.4.37 (Oracle Linux Server) OpenSSL/1.1.1k
< Content-Type: application/problem+json
< Content-Length: 182
< Connection: close
<
{
"code": "Unauthorized",
"message": "Unauthorized",
"type": "tag:oracle.com,2020:error/Unauthorized",
"instance": "tag:oracle.com,2020:ecid/U2paxJBT4Moj06mu1-uQNQ"
}
* shutting down connection #0
Documentation
RFC Editor: RFC 6749: The OAuth 2.0 Authorization Framework
Datatracker: RFC 6749 - The OAuth 2.0 Authorization Framework