Add OAuth 2.0 token based authentication

This commit is contained in:
Christopher Jones 2022-09-08 16:45:12 +10:00
parent 539a9f1266
commit 0d328cf09a
17 changed files with 832 additions and 2928 deletions

View File

@ -4,6 +4,21 @@
**This release is under development and information may be incomplete**
- Token-based Authentication changes:
- Added support for [token-based
authentication](https://oracle.github.io/node-oracledb/doc/api.html#oauthtokenbasedauthentication)
using Microsoft Azure Active Directory OAuth 2.0 tokens.
- The `accessToken` attribute for connection and connection pool creation
can now be a string, a callback function, or an object.
- Fixed a bug that prevented async functions from being used in token callbacks.
- Deprecated the connection pool creation attribute `accessTokenCallback`.
- Deprecated `pool.setAccessToken()`.
- ResultSets now implement the `asyncIterator()` symbol to support asynchonous
iteration.
@ -18,7 +33,7 @@
- Stated compatibility is now for Node.js 14, 16 and 18. Older releases
back to Node.js 10.16 should still work.
- Added support for [token based
- Added support for [token-based
authentication](https://oracle.github.io/node-oracledb/doc/api.html#tokenbasedauth)
when establishing pool based connections and standalone connections.

View File

@ -112,8 +112,8 @@ For installation information, see the [Node-oracledb Installation Instructions][
- 3.3.1.1 [`createPool()`: Parameters and Attributes](#createpoolpoolattrs)
- 3.3.1.1.1 [`accessToken`](#createpoolpoolattrsaccesstoken)
- [`token`](#createpoolpoolattrsaccesstoken), [`privateKey`](#createpoolpoolattrsaccesstoken)
- 3.3.1.1.2 [`connectString`](#createpoolpoolattrsconnectstring), [`connectionString`](#createpoolpoolattrsconnectstring)
- 3.3.1.1.3 [`accessTokenCallback`](#createpoolpoolattrsaccesstokencallback)
- 3.3.1.1.2 [`accessTokenCallback`](#createpoolpoolattrsaccesstokencallback)
- 3.3.1.1.3 [`connectString`](#createpoolpoolattrsconnectstring), [`connectionString`](#createpoolpoolattrsconnectstring)
- 3.3.1.1.4 [`edition`](#createpoolpoolattrsedition)
- 3.3.1.1.5 [`enableStatistics`](#createpoolpoolattrsstats)
- 3.3.1.1.6 [`events`](#createpoolpoolattrsevents)
@ -349,12 +349,12 @@ For installation information, see the [Node-oracledb Installation Instructions][
- 8.1.21 [`stmtCacheSize`](#proppoolstmtcachesize)
- 8.1.22 [`user`](#proppooluser)
- 8.2 [Pool Methods](#poolmethods)
- 8.2.1 [`setAccessToken()`](#poolsetaccesstoken)
- 8.2.2 [`close()`](#poolclose), [`terminate()`](#poolclose)
- 8.2.3 [`getConnection()`](#getconnectionpool)
- 8.2.4 [`getStatistics()`](#poolgetstatistics)
- 8.2.5 [`logStatistics()`](#poollogstatistics)
- 8.2.6 [`reconfigure()`](#poolreconfigure)
- 8.2.1 [`close()`](#poolclose), [`terminate()`](#poolclose)
- 8.2.2 [`getConnection()`](#getconnectionpool)
- 8.2.3 [`getStatistics()`](#poolgetstatistics)
- 8.2.4 [`logStatistics()`](#poollogstatistics)
- 8.2.5 [`reconfigure()`](#poolreconfigure)
- 8.2.6 [`setAccessToken()`](#poolsetaccesstoken)
9. [PoolStatistics Class](#poolstatisticsclass)
- 9.1 [PoolStatistics Methods](#poolstatisticsmethods)
- 9.1.1 [`logStatistics()`](#poolstatisticslogstatistics)
@ -463,7 +463,6 @@ For installation information, see the [Node-oracledb Installation Instructions][
- 16.1.2 [Embedded Connect Descriptor Strings](#embedtns)
- 16.1.3 [Net Service Names for Connection Strings](#tnsnames)
- 16.1.4 [JDBC and Oracle SQL Developer Connection Strings](#notjdbc)
- 16.1.5 [Token Based Authentication Connection Strings](#tokenbasedconnstrings)
- 16.2 [Connections, Threads, and Parallelism](#numberofthreads)
- 16.2.1 [Parallelism on a Connection](#parallelism)
- 16.3 [Connection Pooling](#connpooling)
@ -479,12 +478,18 @@ For installation information, see the [Node-oracledb Installation Instructions][
- 16.3.7.3 [PL/SQL Session Tagging Callback](#sessiontaggingplsql)
- 16.3.8 [Heterogeneous Connection Pools and Pool Proxy Authentication](#connpoolproxy)
- 16.4 [External Authentication](#extauth)
- 16.5 [Token Based Authentication](#tokenbasedauth)
- 16.5.1 [Token Generation using the OCI CLI](#tokengen)
- 16.5.2 [Token and Private Key Extraction from a File](#tokenread)
- 16.5.3 [Standalone Connection Creation with Access Tokens](#tokenbasedstandaloneconn)
- 16.5.4 [Connection Pool Creation with Access Tokens](#tokenbasedpool)
- 16.5.5 [Explicitly Refreshing Pool Access Tokens](#settingpooltokens)
- 16.5 [Token-Based Authentication](#tokenbasedauthentication)
- 16.5.1 [OAuth 2.0 Token-Based Authentication](#oauthtokenbasedauthentication)
- 16.5.1.1 [OAuth 2.0 Token generation](#oauthtokengeneration)
- 16.5.1.2 [OAuth 2.0 Standalone Connections](#oauthstandalone)
- 16.5.1.3 [OAuth 2.0 Connection Pooling](#oauthpool)
- 16.5.1.4 [OAuth 2.0 Connection Strings](#oauthconnectstring)
- 16.5.2 [IAM Token-Based Authentication](#iamtokenbasedauthentication)
- 16.5.2.1 [IAM Token Generation](#iamtokengeneration)
- 16.5.2.2 [IAM Token and Private Key Extraction](#iamtokenextraction)
- 16.5.2.3 [IAM Standalone Connections](#iamstandalone)
- 16.5.2.4 [IAM Connection Pooling](#iampool)
- 16.5.2.5 [IAM Connection Strings](#iamconnectstring)
- 16.6 [Database Resident Connection Pooling (DRCP)](#drcp)
- 16.7 [Privileged Connections](#privconn)
- 16.8 [Securely Encrypting Network Traffic to Oracle Database](#securenetwork)
@ -2237,31 +2242,92 @@ The properties of `poolAttrs` are described below.
###### <a name="createpoolpoolattrsaccesstoken"></a> 3.3.1.1.1 `accessToken`: Attributes
```
Object accessToken
function accessToken(boolean refresh) | String accessToken | Object accessToken
```
The `accessToken` parameter object provides token based authentication
configuration properties.
- For Microsoft Azure Active Directory OAuth 2.0 token-based authentication
`accessToken` can be:
- a callback function returning the token as a string
- an object with a `token` attribute containing the token as a string
- or the token as a string
The properties of the `accessToken` object are described below. Both properties
must be set. The values can be obtained using the Oracle Cloud Infrastructure
(OCI) Command Line Interface (CLI).
Tokens can be obtained using various approaches. For example, using the
Azure Active Directory API.
Attribute | Description
------------------- |------------------------------------------------
`token` | The database authentication token string.
`privateKey` | The database authentication private key string.
- For Oracle Cloud Infrastructure Identity and Access Management (IAM)
token-based authentication `accessToken` can be:
- a callback function returning an object containing `token` and `privateKey` attributes
- or an object containing `token` and `privateKey` attributes
The `externalAuth` and `homogeneous` connection or pool attributes must be set
to *true* while using token based authentication. The `user` (or `username`)
and `password` properties should not be set.
The properties of the `accessToken` object are described below.
See [Token Based Authentication](#tokenbasedauth) for more information.
Attribute | Description
------------------- |------------------------------------------------
`token` | The database authentication token.
`privateKey` | The database authentication private key.
The `accessToken` parameter was added in node-oracledb 5.4. It should be used
with Oracle Client libraries 19.14 (or later), or 21.5 (or later).
The `token` and `privateKey` values can be obtained using various approaches.
For example the Oracle Cloud Infrastructure Command Line Interface can be
used.
###### <a name="createpoolpoolattrsconnectstring"></a> 3.3.1.1.2 `connectString`, `connectionString`
When `accessToken` is a callback function, it will be invoked at the time the
pool is created (even if `poolMin` is 0). It is also called when the pool
needs to expand (causing new connections to be created) and the current token
has expired. The returned token is used by node-oracledb for authentication.
The `refresh` parameter is described below.
`refresh` value | Description
-------------------|------------------------------------------------
*false* | The application can return a token from an application-specific cache. If there is no cached token, the application must externally acquire one.
*true* | The token previously passed to driver is known to be expired, the application should externally acquire a new token.
When the callback is first invoked, the `refresh` parameter will be set to
*false*. This indicates that the application can provide a token from its own
application managed cache, or it can generate a new token if there is no cached
value. Node-oracledb checks whether the returned token has expired. If it has
expired, then the callback function will be invoked a second time with
`refresh` set to *true*. In this case the function must externally acquire a
token, optionally add it to the application's cache, and return the token.
For token-based authentication, the `externalAuth` and `homogeneous` pool
attributes must be set to *true* . The `user` (or `username`) and `password`
attributes should not be set.
See [Token-Based Authentication](#tokenbasedauthentication) for more information.
The `accessToken` attribute was added in node-oracledb 5.4 to support IAM
token-based authentication. In this release the attribute must be an Object.
Oracle Client libraries 19.14 (or later), or 21.5 (or later) must be used for
IAM token-based authentication.
The `accessToken` attribute was extended to allow OAuth 2.0 token-based
authentication in node-oracledb 5.5. For OAuth 2.0, the attribute should be a
string, or a callback. Also Oracle Client libraries 19.15 (or later), or 21.7
(or later) must be used. The callback usage supports both OAuth 2.0 and IAM
token-based authentication.
###### <a name="createpoolpoolattrsaccesstokencallback"></a> 3.3.1.1.2 `accessTokenCallback`
```
Object accessTokenCallback
```
The optional `accessTokenCallback` attribute is a Node.js callback function. It
gets called by the connection pool if the pool needs to grow and create new
connections but the current token has expired.
The callback function must return a JavaScript object with attributes `token`
and `privateKey` for IAM. See [Connection Pool Creation with Access Tokens for
IAM](#tokenbasedpool).
The `accessTokenCallback` attribute was added in node-oracledb 5.4. It should be
used with Oracle Client libraries 19.14 (or later), or 21.5 (or later).
The `accessTokenCallback` attribute is deprecated from node-oracledb 5.5. Use
[`accessToken`](#createpoolpoolattrsaccesstoken) instead, which was enhanced to
support a callback.
###### <a name="createpoolpoolattrsconnectstring"></a> 3.3.1.1.3 `connectString`, `connectionString`
```
String connectString
@ -2277,24 +2343,6 @@ See [Connection Strings](#connectionstrings) for examples.
The alias `connectionString` was added in node-oracledb 2.1.
###### <a name="createpoolpoolattrsaccesstokencallback"></a> 3.3.1.1.3 `accessTokenCallback`
```
Object accessTokenCallback
```
The optional `accessTokenCallback` attribute is a Node.js callback function. It
gets called by the connection pool if the pool needs to grow and create new
connections but the current token has expired.
The callback function must return a JavaScript object with attributes `token`
and `privateKey`, equivalent to the
[`accessToken`](#createpoolpoolattrsaccesstoken) object. See [Connection Pool
Creation with Access Tokens](#tokenbasedpool).
The `accessTokenCallback` attribute was added in node-oracledb 5.4. It should be
used with Oracle Client libraries 19.14 (or later), or 21.5 (or later).
###### <a name="createpoolpoolattrsedition"></a> 3.3.1.1.4 `edition`
```
@ -2762,29 +2810,65 @@ The properties of the `connAttrs` object are described below.
###### <a name="getconnectiondbattrsaccesstoken"></a> 3.3.2.1.2.1 `accessToken`: Attributes
```
Object accessToken
function accessToken(boolean refresh) | String accessToken | Object accessToken
```
The `accessToken` parameter object provides token based authentication
configuration properties.
- For Microsoft Azure Active Directory OAuth 2.0 token-based authentication
`accessToken` can be:
- a callback function returning the token as a string
- or the token as a string
The properties of the `accessToken` object are described below. Both properties
must be set. The values can be obtained using the Oracle Cloud Infrastructure
(OCI) Command Line Interface (CLI).
Tokens can be obtained using various approaches. For example, using the
Azure Active Directory API.
Attribute | Description
------------------- |------------------------------------------------
`token` | The database authentication token string.
`privateKey` | The database authentication private key string.
- For Oracle Cloud Infrastructure Identity and Access Management (IAM)
token-based authentication `accessToken` can be:
- an object containing `token` and `privateKey` attributes
- or a callback function returning an object containing `token` and `privateKey` attributes
The `externalAuth` connection attribute must be set to *true* while using token
based authentication. The `user` (or `username`) and `password` properties
should not be set.
The `token` and `privateKey` values can be obtained using various approaches.
For example the Oracle Cloud Infrastructure Command Line Interface can be
used.
See [Token Based Authentication](#tokenbasedauth) for more information.
The properties of the `accessToken` object are described below.
The `accessToken` parameter was added in node-oracledb 5.4. It should be used
with Oracle Client libraries 19.14 (or later), or 21.5 (or later).
Attribute | Description
------------------- |------------------------------------------------
`token` | The database authentication token.
`privateKey` | The database authentication private key.
When `accessToken` is a callback function, the returned token is used by
node-oracledb for authentication. The `refresh` parameter is described below.
`refresh` value | Description
-------------------|------------------------------------------------
*false* | The application can return a token from an application-specific cache. If there is no cached token, the application must externally acquire one.
*true* | The token previously passed to driver is known to be expired, the application should externally acquire a new token.
For each connection, the callback is invoked with the `refresh` parameter set
to *false*. This indicates that the application can provide a token from its
own application managed cache, or it can generate a new token if there is no
cached value. Node-oracledb checks whether the returned token has expired. If
it has expired, then the callback function will be invoked a second time with
`refresh` set to *true*. In this case the function must externally acquire a
token, optionally add it to the application's cache, and return the token.
For token-based authentication, the `externalAuth` connection attribute must be
set to *true* . The `user` (or `username`) and `password` attributes should
not be set.
See [Token-Based Authentication](#tokenbasedauthentication) for more information.
The `accessToken` attribute was added in node-oracledb 5.4 to support IAM
token-based authentication. In this release the attribute must be an Object.
Oracle Client libraries 19.14 (or later), or 21.5 (or later) must be used for
IAM token-based authentication.
The `accessToken` attribute was extended to allow OAuth 2.0 token-based
authentication in node-oracledb 5.5. For OAuth 2.0, the attribute should be a
string, or a callback. Also Oracle Client libraries 19.15 (or later), or 21.7
(or later) must be used. The callback usage supports both OAuth 2.0 and IAM
token-based authentication.
###### <a name="getconnectiondbattrsconnectstring"></a> 3.3.2.1.2.2 `connectString`, `connectionString`
@ -6553,40 +6637,7 @@ The database username for connections in the pool.
### <a name="poolmethods"></a> 8.2 Pool Methods
#### <a name="poolsetaccesstoken"></a> 8.2.1 `pool.setAccessToken()`
##### Description
This method can be used to set an access token and private key after pool
creation. It is useful if the token is known to have expired, and you are not
using [`accessTokenCallback`](#createpoolpoolattrsaccesstokencallback).
It can also be useful in tests to set an expired token so that token expiry
code paths can be tested.
See [Explicitly Refreshing Pool Access Tokens](#settingpooltokens) for an example.
##### Parameters
```
Object tokenAttrs
```
The `tokenAttrs` parameter object provides token based authentication
configuration properties.
The properties of the `tokenAttrs` object are described below. Both properties
must be set. The values can be obtained using the Oracle Cloud Infrastructure
(OCI) Command Line Interface (CLI).
Attribute | Description
------------------- |------------------------------------------------
`token` | The database authentication token string.
`privateKey` | The database authentication private key string.
This function was added in node-oracledb 5.4.
#### <a name="poolclose"></a> <a name="terminate"></a> 8.2.2 `pool.close()`
#### <a name="poolclose"></a> <a name="terminate"></a> 8.2.1 `pool.close()`
##### Prototype
@ -6662,7 +6713,7 @@ The `drainTime` parameter was added in node-oracledb 3.0.
*Error error* | If `close()` succeeds, `error` is NULL. If an error occurs, then `error` contains the [error message](#errorobj).
#### <a name="getconnectionpool"></a> 8.2.3 `pool.getConnection()`
#### <a name="getconnectionpool"></a> 8.2.2 `pool.getConnection()`
##### Prototype
@ -6750,7 +6801,7 @@ pools.
*Error error* | If `getConnection()` succeeds, `error` is NULL. If an error occurs, then `error` contains the [error message](#errorobj).
*Connection connection* | The newly created connection. If `getConnection()` fails, `connection` will be NULL. See [Connection class](#connectionclass) for more details.
#### <a name="poolgetstatistics"></a> 8.2.4 `pool.getStatistics()`
#### <a name="poolgetstatistics"></a> 8.2.3 `pool.getStatistics()`
##### Prototype
@ -6775,7 +6826,7 @@ If `getStatistics()` is called while the pool is closed, draining, or
This function was added in node-oracledb 5.2.
#### <a name="poollogstatistics"></a> 8.2.5 `pool.logStatistics()`
#### <a name="poollogstatistics"></a> 8.2.4 `pool.logStatistics()`
##### Prototype
@ -6801,7 +6852,7 @@ This function was added in node-oracledb 5.2. The obsolete function
`_logStats()` can still be used, but it will be removed in a future version of
node-oracledb.
#### <a name="poolreconfigure"></a> 8.2.6 `pool.reconfigure()`
#### <a name="poolreconfigure"></a> 8.2.5 `pool.reconfigure()`
##### Prototype
@ -6898,6 +6949,37 @@ await pool.reconfigure({poolMin: 5, poolMax: 10, increment: 5});
----------------------------|-------------
*Error error* | If `reconfigure()` succeeds, `error` is null. If an error occurs, then `error` contains the [error message](#errorobj).
#### <a name="poolsetaccesstoken"></a> 8.2.6 `pool.setAccessToken()`
##### Description
This method can be used to set an IAM access token and private key after pool
creation. It is useful if the IAM token is known to have expired, and you are
not using [`accessTokenCallback`](#createpoolpoolattrsaccesstokencallback).
It can also be useful in tests to set an expired token so that token expiry
code paths can be tested.
##### Parameters
```
Object tokenAttrs
```
The `tokenAttrs` parameter object provides IAM token-based authentication
properties.
The properties of the `tokenAttrs` object are described below. Both properties
must be set. The values can be obtained, for example, using the Oracle Cloud
Infrastructure Command Line Interface (OCI CLI).
Attribute | Description
------------------- |------------------------------------------------
`token` | The database authentication token string.
`privateKey` | The database authentication private key string.
This function was added in node-oracledb 5.4 and deprecated in node-oracledb 5.5.
## <a name="poolstatisticsclass"></a> 9. PoolStatistics Class
@ -9532,64 +9614,6 @@ const connection = await oracledb.getConnection(
);
```
#### <a name="tokenbasedconnstrings"></a> 16.1.5 Token Based Authentication Connection Strings
Token based authentication allows Oracle Cloud Infrastruction users to
authenticate to Oracle Database with Oracle Identity Access Manager (IAM)
tokens. Token authentication can be performed when node-oracledb uses Oracle
Client libraries 19.14 (or later), or 21.5 (or later).
The Oracle Cloud Infrastructure command line interface (OCI-CLI) can be used
externally to get tokens and private keys from IAM, for example with `oci iam
db-token get`.
The connection string used by node-oracledb can specify the directory where the
token and private key files are located. This syntax is usable with older
versions of node-oracledb however it is recommended to use connection and pool
creation parameters introduced in node-oracledb 5.4 instead, see [Token Based
Authentication](#tokenbasedauth).
If you need to use the connection string syntax then the Oracle Net parameter
`TOKEN_AUTH` must be set. Also the `PROTOCOL` parameter must be `tcps` and
`SSL_SERVER_CERT_DN` should be `ON`.
You can set `TOKEN_AUTH=OCI_TOKEN` in a [sqlnet.ora](#tnsadmin) file.
Alternatively you can specify it in a connect descriptor, for example:
```
db_alias =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCPS)(PORT = 1522)(HOST = adb.us-ashburn-1.oraclecloud.com))
(CONNECT_DATA = (SERVICE_NAME = adb.oraclecloud.com))
(SECURITY =
(SSL_SERVER_CERT_DN = "CN=adwc.uscom-east-1.oraclecloud.com, OU=Oracle BMCS US,
O=Oracle Corporation, L=Redwood City, ST=California, C=US")
(TOKEN_AUTH = OCI_TOKEN)))
```
The default location for the token and private key is the same default location
that the OCI-CLI tool writes to. For example `~/.oci/db-token/` on Linux.
If the token and private key files are not in the default location then their
directory must be specified with the `TOKEN_LOCATION` parameter in a
`sqlnet.ora` file or in a connection string. For example in a `tnsnames.ora`
file:
```
db_alias =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCPS)(PORT = 1522)(HOST = adb.us-ashburn-1.oraclecloud.com))
(CONNECT_DATA = (SERVICE_NAME = adb.oraclecloud.com))
(SECURITY =
(SSL_SERVER_CERT_DN = "CN=adwc.uscom-east-1.oraclecloud.com, OU=Oracle BMCS US,
O=Oracle Corporation, L=Redwood City, ST=California, C=US")
(TOKEN_AUTH = OCI_TOKEN)
(TOKEN_LOCATION = '~/.oci/db-token')))
```
The `TOKEN_AUTH` and `TOKEN_LOCATION` values in a connection string take
precedence over the `sqlnet.ora` settings.
### <a name="numberofthreads"></a> 16.2 Connections, Threads, and Parallelism
If you open more than four connections, such as via increasing
@ -10982,24 +11006,233 @@ of open connections exceeds `poolMin` and connections are idle for
more than the [`poolTimeout`](#propdbpooltimeout) seconds, then the
number of open connections does not fall below `poolMin`.
### <a name="tokenbasedauth"></a> 16.5 Token Based Authentication
### <a name="tokenbasedauthentication"></a> 16.5 Token-Based Authentication
Token based authentication allows Oracle Cloud Infrastruction users to
authenticate to Oracle Database with Oracle Identity Access Manager (IAM)
tokens. Token authentication can be performed when node-oracledb uses Oracle
Client libraries 19.14 (or later), or 21.5 (or later).
Token-Based Authentication allows users to connect to a database by using an
encrypted authentication token without having to enter a database username and
password. The authentication token must be valid and not expired for the
connection to be successful. Users already connected will be able to continue
work after their token has expired but they will not be able to reconnect
without getting a new token.
Token based authentication can be used for both standalone connections and
connection pools. Tokens can be specified using connection attributes
introduced in node-oracledb 5.4. Users of earlier node-oracledb versions can
alternatively use [Token Based Authentication Connection
Strings](#tokenbasedconnstrings).
The two authentication methods supported by node-oracledb are Open
Authorization [OAuth 2.0](#oauthtokenbasedauthentication) and Oracle Cloud
Infrastructure (OCI) Identity and Access Management
[IAM](#iamtokenbasedauthentication).
#### <a name="tokengen"></a> 16.5.1 Token Generation using the OCI CLI
Token-based authentication can be used for both standalone connections and
connection pools.
Authentication tokens are generated through execution of an Oracle Cloud
Infrastructure command line interface (OCI-CLI) command run externally to
Node.js:
#### <a name="oauthtokenbasedauthentication"></a> 16.5.1 OAuth 2.0 Token-Based Authentication
Oracle Cloud Infrastructure (OCI) users can be centrally managed in a Microsoft
Azure Active Directory (Azure AD) service. Open Authorization (OAuth 2.0)
token-based authentication allows users to authenticate to Oracle Database
using Azure AD OAuth 2.0 tokens. Your Oracle Database must be registered with
Azure AD.
See [Authenticating and Authorizing Microsoft Azure Active Directory Users for
Oracle Autonomous Databases][206] for more information.
OAuth 2.0 token authentication can be performed when node-oracledb uses Oracle
Client libraries 19.15 (or later), or 21.7 (or later).
##### <a name="oauthtokengeneration"></a> 16.5.1.1 OAuth 2.0 Token Generation
Authentication tokens can be obtained in several ways. For example you can use
a curl command against the Azure Active Directory API such as:
```
curl -X POST -H 'Content-Type: application/x-www-form-urlencoded'
https://login.microsoftonline.com/[<TENANT_ID>]/oauth2/v2.0/token
-d 'client_id = <APP_ID>'
-d 'scope = <SCOPES>'
-d 'username = <USER_NAME>'
-d 'password = <PASSWORD>'
-d 'grant_type = password'
-d 'client_secret = <SECRET_KEY>'
```
Substitute your own values as appropriate for each argument.
This returns a JSON response containing an `access_token` attribute. See
[Microsoft identity platform and OAuth 2.0 authorization code flow][208] for
more details. This attribute can be passed as the `oracledb.getConnection()`
attribute [`accessToken`](#getconnectiondbattrsaccesstoken) or as the
`oracledb.createPool()` attribute
[`accessToken`](#createpoolpoolattrsaccesstoken).
Alternatively authentication tokens can be generated by calling the Azure
Active Directory REST API, for example:
```javascript
function getOauthToken() {
const requestParams = {
client_id : <CLIENT_ID>,
client_secret : <CLIENT_SECRET>,
grant_type : 'client_credentials',
scope : <SCOPES>,
};
const tenantId = <TENANT_ID>;
const url = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
return new Promise(function(resolve, reject) {
request.post({
url : url,
body : queryString.stringify(requestParams),
headers : { 'Content-Type': 'application/x-www-form-urlencoded' }
}, function(err, response, body) {
if (err) {
reject(err);
} else {
resolve(JSON.parse(body).access_token);
}
});
});
}
```
Substitute your own values as appropriate for each argument.
Use of `getOauthToken()` is shown in subsequent examples.
##### <a name="oauthstandalone"></a> 16.5.1.2 OAuth 2.0 Standalone Connections
Standalone connections can be created using OAuth2 token-based authentication,
for example:
```javascript
let accessTokenStr; // the token string. In this app it is also the token "cache"
async function tokenCallback(refresh) {
if (refresh || !acccessTokenStr) {
accessTokenStr = await getOauthToken(); // getOauthToken() was shown earlier
}
return acccessTokenStr;
}
async function init() {
try {
await oracledb.getConnection({
accessToken : tokenCallback, // the callback returning the token
externalAuth : true, // must specify external authentication
connectString : connect_string // Oracle Autonomous Database connection string
});
} catch (err) {
console.error(err);
}
}
```
In this example, the global variable `accessTokenStr` is used to "cache" the
access token string so any subsequent callback invocation will not necessarily
have to incur the expense of externally getting a token. For example, if the
application opens two connections for the same user, the token acquired for the
first connection can be reused without needing to make a second REST call.
The `getConnection()` function's
[`accessToken`](#getconnectiondbattrsaccesstoken) attribute in this example is
set to the callback function that returns an OAuth 2.0 token used by
node-oracledb for authentication. This function `tokenCallback()` will be
invoked when `getConnection()` is called. If the returned token is found to
have expired, then `tokenCallback()` will be called a second time. If the
second invocation of `tokenCallback()` also returns an expired token, then the
connection will fail. The value of the `refresh` parameter will be different
each of the times the callback is invoked:
- When `refresh` is *true*, the token is known to have expired so the
application must get a new token. This is then stored in the global variable
`accessTokenStr` and returned.
- When `refresh` is *false*, the application can return the token stored in
`accessTokenStr`, if it is set. But if it is not set (meaning there is no
token cached), then the application externally acquires a token, stores it in
`accessTokenStr`, and returns it.
##### <a name="oauthpool"></a> 16.5.1.3 OAuth 2.0 Connection Pooling
Pooled connections can be created using OAuth 2.0 token-based authentication,
for example:
```javascript
let accessTokenStr; // The token string. In this app it is also the token "cache"
async function tokenCallback(refresh) {
if (refresh || !acccessTokenStr) {
accessTokenStr = await getOauthToken(); // getOauthToken() was shown earlier
}
return acccessToken;
}
async function init() {
try {
await oracledb.createPool({
accessToken : tokenCallback, // the callback returning the token
externalAuth : true, // must specify external authentication
homogeneous : true, // must use an homogeneous pool
connectString : '...' // Oracle Autonomous Database connection string
});
} catch (err) {
console.error(err);
}
}
```
See [OAuth 2.0 Standalone Connections](#oauthstandalone) for a description of
the callback and `refresh` parameter. With connection pools, the
[`accessToken`](#createpoolpoolattrsaccesstoken) attribute sets a callback
function which will be invoked at the time the pool is created (even if
`poolMin` is 0). It is also called when the pool needs to expand (causing new
connections to be created) and the current token has expired.
##### <a name="oauthconnectstring"></a> 16.5.1.4 OAuth 2.0 Connection Strings
Applications built with node-oracledb 5.5, or later, should use the connection
or pool creation parameters described earlier. However, if you cannot use
them, you can use OAuth 2.0 Token Authentication by configuring Oracle Net
options. This still requires Oracle Client libraries 19.15 (or later), or 21.7
(or later).
Save the generated access token to a file and set the connect descriptor
`TOKEN_LOCATION` option to the directory containing the token file. The
connect descriptor parameter `TOKEN_AUTH` must be set to `OAUTH`, the
`PROTOCOL` value must be `TCPS`, the `SSL_SERVER_DN_MATCH` value should be
`ON`, and the parameter `SSL_SERVER_CERT_DN` should be set. For example, your
[`tnsnames.ora`](#tnsnames) file might contain:
```
db_alias =
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCPS)(PORT=1522)(HOST=abc.oraclecloud.com))
(CONNECT_DATA=(SERVICE_NAME=db_low.adb.oraclecloud.com))
(SECURITY=
(SSL_SERVER_DN_MATCH=ON)
(SSL_SERVER_CERT_DN="CN=efg.oraclecloud.com, OU=Oracle BMCS US, O=Oracle Corporation, L=Redwood City, ST=California, C=US")
(TOKEN_AUTH=OAUTH)
(TOKEN_LOCATION='/opt/oracle/token')
))
```
You can alternatively set `TOKEN_AUTH` and `TOKEN_LOCATION` in a
[`sqlnet.ora`](#tnsadmin) file. The `TOKEN_AUTH` and `TOKEN_LOCATION` values
in a connection string take precedence over the `sqlnet.ora` settings.
See [Oracle Net Services documentation][37] for more information.
#### <a name="tokenbasedauth"></a> <a name="iamtokenbasedauthentication"></a> 16.5.2 IAM Token-Based Authentication
Token-based authentication allows Oracle Cloud Infrastructure users to
authenticate to Oracle Database with Oracle Identity Access Management (IAM)
tokens. This token authentication can be performed when node-oracledb uses
Oracle Client libraries 19.14 (or later), or 21.5 (or later).
See [Configuring the Oracle Autonomous Database for IAM Integration][207] for
more information.
##### <a name="tokengen"></a> <a name="iamtokengeneration"></a> 16.5.2.1 IAM Token Generation
Authentication tokens can be obtained in several ways. For example you can use
the Oracle Cloud Infrastructure command line interface (OCI CLI) command run
externally to Node.js:
```
oci iam db-token get
@ -11008,22 +11241,25 @@ oci iam db-token get
On Linux a folder `.oci/db-token` will be created in your home directory. It
will contain the token and private key files needed by node-oracledb.
#### <a name="tokenread"></a> 16.5.2 Token and Private Key Extraction from a File
See [Working with the Command Line Interface ][209] for more information on the
OCI CLI.
Token and private key files created externally can be read by your application,
for example like:
##### <a name="tokenread"></a> <a name="iamtokenextraction"></a> 16.5.2.2 IAM Token and Private Key Extraction
Token and private key files created externally can be read by Node.js
applications, for example like:
```javascript
function getToken() {
function getIAMToken() {
const tokenPath = '/home/cjones/.oci/db-token/token';
const privateKeyPath = '/home/cjones/.oci/db-token/oci_db_key.pem';
let token = '';
let privateKey = '';
try {
// Read token file
// Read the token file
token = fs.readFileSync(tokenPath, 'utf8');
// Read private key file
// Read the private key file
const privateKeyFileContents = fs.readFileSync(privateKeyPath, 'utf-8');
privateKeyFileContents.split(/\r?\n/).forEach(line => {
if (line != '-----BEGIN PRIVATE KEY-----' &&
@ -11034,31 +11270,37 @@ function getToken() {
console.error(err);
} finally {
const tokenBasedAuthData = {
token : token,
privateKey : privateKey
token : token,
privateKey : privateKey
};
return tokenBasedAuthData;
}
}
```
The token and key can be used subsequently during authentication.
The token and key can be used during subsequent authentication.
#### <a name="tokenbasedstandaloneconn"></a> 16.5.3 Standalone Connection Creation with Access Tokens
##### <a name="tokenbasedstandaloneconn"></a> <a name="iamstandalone"></a> 16.5.2.3 IAM Standalone Connections
Standalone connections can be created using token based authentication, for
Standalone connections can be created using IAM token-based authentication, for
example:
```javascript
let accessTokenObj; // the token object. In this app it is also the token "cache"
function tokenCallback(refresh) {
if (refresh || !acccessTokenObj) {
accessTokenObj = getIAMToken(); // getIAMToken() was shown earlier
}
return acccessTokenObj;
}
async function init() {
const accessTokenData = getToken();
try {
await oracledb.getConnection({
accessToken : accessTokenData,
externalAuth : true,
connectString : connect_string
accessToken : tokenCallback, // the callback returns the token object
externalAuth : true, // must specify external authentication
connectString : '...' // Oracle Autonomous Database connection string
});
} catch (err) {
console.error(err);
@ -11066,24 +11308,54 @@ async function init() {
}
```
For a runnable example, see [`tokenbasedauth.js`][206].
In this example, the global object `accessTokenObj` is used to "cache" the IAM
access token and private key (using the attributes `token` and `privateKey`) so
any subsequent callback invocation will not necessarily have to incur the
expense of externally getting them. For example, if the application opens two
connections for the same user, the token and private key acquired for the first
connection can be reused without needing to make a second REST call.
#### <a name="tokenbasedpool"></a> 16.5.4 Connection Pool Creation with Access Tokens
The `getConnection()` function's
[`accessToken`](#getconnectiondbattrsaccesstoken) attribute in this example is
set to the callback function that returns an IAM token and private key used by
node-oracledb for authentication. This function `tokenCallback()` will be
invoked when `getConnection()` is called. If the returned token is found to
have expired, then `tokenCallback()` will be called a second time. If the
second invocation of `tokenCallback()` also returns an expired token, then the
connection will fail. The value of the `refresh` parameter will be different
each of the times the callback is invoked:
An example creating a pool using token based authentication is:
- When `refresh` is *true*, the token is known to have expired so the
application must get a new token and private key. These are then stored in
the global object `accessTokenObj` and returned.
- When `refresh` is *false*, the application can return the token and private
key stored in `accessTokenObj`, if it is set. But if it is not set (meaning
there is no token or key cached), then the application externally acquires a
token and private key, stores them in `accessTokenObj`, and returns it.
##### <a name="settingpooltokens"></a> <a name="tokenbasedpool"></a> <a name="iampool"></a> 16.5.2.4 IAM Connection Pooling
Pooled connections can be created using IAM token-based authentication, for
example:
```javascript
let accessTokenObj; // The token string. In this app it is also the token "cache"
function tokenCallback(refresh) {
if (refresh || !acccessTokenObj) {
accessTokenObj = getIAMToken(); // getIAMToken() was shown earlier
}
return acccessToken;
}
async function init() {
const accessTokenData = getToken();
try {
await oracledb.createPool({
accessToken : accessTokenData,
accessTokenCallback : tokenCallback
externalAuth : true,
homogeneous : true,
connectString : connect_string,
accessToken : tokenCallback, // the callback returning the token
externalAuth : true, // must specify external authentication
homogeneous : true, // must use an homogeneous pool
connectString : connect_string // Oracle Autonomous Database connection string
});
} catch (err) {
console.error(err);
@ -11091,55 +11363,64 @@ async function init() {
}
```
The callback will be called if the connection pool needs to grow and create new
connections but the current token is invalid. The callback should return the
new, valid token:
See [IAM Standalone Connections](#iamstandalone) for a description of the
callback and `refresh` parameter. With connection pools, the
[`accessToken`](#createpoolpoolattrsaccesstoken) attribute sets a callback
function which will be invoked at the time the pool is created (even if
`poolMin` is 0). It is also called when the pool needs to expand (causing new
connections to be created) and the current token has expired.
```javascript
function tokenCallback() {
...
// Code to get the refreshed token and private key should be here.
// For example, make an external call to 'oci iam db-token get' and then
// read the token and private key values, see getToken()
...
##### <a name="tokenbasedconnstrings"></a> <a name="iamconnectstring"></a> 16.5.2.5 IAM Connection Strings
const refreshTokenData = {
token : token,
privateKey : privateKey
};
Applications built with node-oracledb 5.4, or later, should use the connection
or pool creation parameters described earlier. However, if you cannot use
them, you can use IAM Token Authentication by configuring Oracle Net options.
This still requires Oracle Client libraries 19.14 (or later), or 21.5 (or
later).
return refreshTokenData;
}
Save the generated access token to a file and set the connect descriptor
`TOKEN_LOCATION` option to the directory containing the token file. The
connect descriptor parameter `TOKEN_AUTH` must be set to `OCI_TOKEN`, the
`PROTOCOL` value must be `TCPS`, the `SSL_SERVER_DN_MATCH` value should be
`ON`, and the parameter `SSL_SERVER_CERT_DN` should be set. For example, if
the token and private key are in the default location used by the [OCI
CLI][209], your [`tnsnames.ora`](#tnsnames) file might contain:
```
db_alias =
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCPS)(PORT=1522)(HOST=abc.oraclecloud.com))
(CONNECT_DATA=(SERVICE_NAME=db_low.adb.oraclecloud.com))
(SECURITY=
(SSL_SERVER_DN_MATCH=ON)
(SSL_SERVER_CERT_DN="CN=efg.oraclecloud.com, OU=Oracle BMCS US, O=Oracle Corporation, L=Redwood City, ST=California, C=US")
(TOKEN_AUTH=OCI_TOKEN)
))
```
For a runnable example, see [`tokenbasedauthpool.js`][207].
This reads the IAM token and private key from the default location, for example
`~/.oci/db-token/` on Linux.
#### <a name="settingpooltokens"></a> 16.5.5 Explicitly Refreshing Pool Access Tokens
If the token and private key files are not in the default location then their
directory must be specified with the `TOKEN_LOCATION` parameter. For example
in a `tnsnames.ora` file:
If you did not set an access token callback with
[`accessTokenCallback`](#createpoolpoolattrsaccesstokencallback) to
automatically update an expired token, you can explicitly call
[`pool.setAccessToken()`](#poolsetaccesstoken). For example:
```javascript
let accessTokenData = getToken();
let pool = await oracledb.createPool({
accessToken : accessTokenData,
externalAuth : true,
homogeneous : true,
connectString : connect_string,
});
. . . // do some work, during which the token expires
// Get and set an updated token
accessTokenData = getToken();
pool.setAccessToken({
token : accessTokenData.token,
privateKey : accessTokenData.privateKey
)};
```
db_alias =
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCPS)(PORT=1522)(HOST=abc.oraclecloud.com))
(CONNECT_DATA=(SERVICE_NAME=db_low.adb.oraclecloud.com))
(SECURITY=
(SSL_SERVER_DN_MATCH=ON)
(SSL_SERVER_CERT_DN="CN=efg.oraclecloud.com, OU=Oracle BMCS US, O=Oracle Corporation, L=Redwood City, ST=California, C=US")
(TOKEN_AUTH=OCI_TOKEN)
(TOKEN_LOCATION='/opt/oracle/token')
))
```
You can alternatively set `TOKEN_AUTH` and `TOKEN_LOCATION` in a
[`sqlnet.ora`](#tnsadmin) file. The `TOKEN_AUTH` and `TOKEN_LOCATION` values
in a connection string take precedence over the `sqlnet.ora` settings.
See [Oracle Net Services documentation][37] for more information.
### <a name="drcp"></a> 16.6 Database Resident Connection Pooling (DRCP)
@ -19115,5 +19396,7 @@ can be asked at [AskTom][158].
[203]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-39C521D4-5C6E-44B1-B7C7-DEADD7D9CAF0
[204]: https://www.oracle.com/a/tech/docs/application-checklist-for-continuous-availability-for-maa.pdf
[205]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-8152084F-4760-4B89-A91C-9A84F81C23D1
[206]: https://github.com/oracle/node-oracledb/tree/main/examples/tokenbasedauth.js
[207]: https://github.com/oracle/node-oracledb/tree/main/examples/tokenbasedauthpool.js
[206]: https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/authenticating-and-authorizing-microsoft-azure-active-directory-users-oracle-autonomous-datab.html#GUID-2712902B-DD07-4A61-B336-31C504781D0F
[207]: https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/authenticating-and-authorizing-iam-users-oracle-autonomous-databases.html#GUID-466A8800-5AF1-4202-BAFF-5AE727D242E8
[208]: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
[209]: https://docs.oracle.com/en-us/iaas/Content/API/Concepts/cliconcepts.htm

View File

@ -109,7 +109,5 @@ File Name | Description
[`sessiontagging1.js`](sessiontagging1.js) | Simple pooled connection tagging for setting session state
[`sessiontagging2.js`](sessiontagging2.js) | More complex example of pooled connection tagging for setting session state
[`soda1.js`](soda1.js) | Basic Simple Oracle Document Access (SODA) example
[`tokenbasedauth.js`](tokenbasedauth.js) | Shows standalone connection using token based authentication
[`tokenbasedauthpool.js`](tokenbasedauthpool.js) | Shows connection pooling using token based authentication
[`version.js`](version.js) | Shows the node-oracledb version attributes
[`webapp.js`](webapp.js) | A simple web application using a connection pool

View File

@ -1,125 +0,0 @@
/* Copyright (c) 2022, Oracle and/or its affiliates. */
/******************************************************************************
*
* You may not use the identified files except in compliance with the Apache
* License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
* NAME
* tokenbasedauth.js
*
* DESCRIPTION
* This script shows standalone connection using token based authentication
* to Oracle Autonomous Database from Oracle Compute Infrastructure.
*
* For more information refer to
* https://oracle.github.io/node-oracledb/doc/api.html#tokenbasedauth
*
* PREREQUISITES
* - node-oracledb 5.4 or later.
*
* - Oracle Client libraries 19.14 (or later), or 21.5 (or later).
*
* - The Oracle Cloud Infrastructure command line interface (OCI-CLI). The
* command line interface (CLI) is a tool that enables you to work with
* Oracle Cloud Infrastructure objects and services at a command line, see
* https://docs.oracle.com/en-us/iaas/Content/API/Concepts/cliconcepts.htm
*
* - Set these environment variables (see the code explanation):
* NODE_ORACLEDB_ACCESS_TOKEN_LOC
* NODE_ORACLEDB_CONNECTIONSTRING
*
*****************************************************************************/
const fs = require('fs');
const oracledb = require('oracledb');
const { execSync } = require('child_process');
// Execute the OCI-CLI command to generate a token.
// This should create two files "token" and "oci_db_key.pem".
// On Linux the default file location is "~/.oci/db-token". You should set
// NODE_ORACLEDB_ACCESS_TOKEN_LOC to this directory, or to the directory where
// you move the files.
try {
const cmdResult = execSync('oci iam db-token get', { encoding: 'utf-8' });
console.log(cmdResult);
} catch (err) {
console.log(err);
}
// User defined function for reading token and private key values generated by
// the OCI-CLI.
function getToken() {
const tokenPath = process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC + '/token';
const privateKeyPath = process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC +
'/oci_db_key.pem';
let token = '';
let privateKey = '';
try {
// Read token file
token = fs.readFileSync(tokenPath, 'utf8');
// Read private key file
const privateKeyFileContents = fs.readFileSync(privateKeyPath, 'utf-8');
privateKeyFileContents.split(/\r?\n/).forEach(line => {
if (line != '-----BEGIN PRIVATE KEY-----' &&
line != '-----END PRIVATE KEY-----')
privateKey = privateKey.concat(line);
});
} catch (err) {
console.error(err);
}
const tokenBasedAuthData = {
token : token,
privateKey : privateKey
};
return tokenBasedAuthData;
}
async function run() {
let connection;
// Get token and private key.
let accessTokenObj = getToken();
// Configuration for token based authentication:
// accessToken: The token values
// externalAuth: Must be set to true for token based authentication.
// connectString: The NODE_ORACLEDB_CONNECTIONSTRING environment variable
// set to the Oracle Net alias or connect descriptor of your
// Oracle Autonomous Database.
const config = {
accessToken : accessTokenObj,
externalAuth : true,
connectString : process.env.NODE_ORACLEDB_CONNECTIONSTRING
};
try {
connection = await oracledb.getConnection(config);
const sql = `SELECT TO_CHAR(current_date, 'DD-Mon-YYYY HH24:MI') AS D
FROM DUAL`;
const result = await connection.execute(sql);
console.log("Result is:\n", result);
} catch (err) {
console.error(err);
} finally {
try {
if (connection)
await connection.close();
} catch (err) {
console.error(err.message);
}
}
}
run();

View File

@ -1,170 +0,0 @@
/* Copyright (c) 2022, Oracle and/or its affiliates. */
/******************************************************************************
*
* You may not use the identified files except in compliance with the Apache
* License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
* NAME
* tokenbasedauthpool.js
*
* DESCRIPTION
* This script shows connection pooling with token based authentication to
* Oracle Autonomous Database from Oracle Compute Infrastructure. It shows
* how to create a connection pool and update expired tokens.
*
* For more information refer to
* https://oracle.github.io/node-oracledb/doc/api.html#tokenbasedauth
*
* PREREQUISITES
* - node-oracledb 5.4 or later.
*
* - Oracle Client libraries 19.14 (or later), or 21.5 (or later).
*
* - The Oracle Cloud Infrastructure command line interface (OCI-CLI). The
* command line interface (CLI) is a tool that enables you to work with
* Oracle Cloud Infrastructure objects and services at a command line, see
* https://docs.oracle.com/en-us/iaas/Content/API/Concepts/cliconcepts.htm
*
* - Set these environment variables (see the code explanation):
* NODE_ORACLEDB_ACCESS_TOKEN_LOC
* NODE_ORACLEDB_CONNECTIONSTRING
*
*****************************************************************************/
const fs = require('fs');
const oracledb = require('oracledb');
const { execSync } = require('child_process');
// Execute the OCI-CLI command to generate a token.
// This should create two files "token" and "oci_db_key.pem"
// On Linux the default file location is "~/.oci/db-token". You should set
// NODE_ORACLEDB_ACCESS_TOKEN_LOC to this directory, or to the directory where
// you move the files.
try {
const cmdResult = execSync('oci iam db-token get', { encoding: 'utf-8' });
console.log(cmdResult);
} catch (err) {
console.log(err);
}
// User defined function for reading token and private key values generated by
// the OCI-CLI.
function getToken() {
const tokenPath = process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC + '/token';
const privateKeyPath = process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC +
'/oci_db_key.pem';
let token = '';
let privateKey = '';
try {
// Read token file
token = fs.readFileSync(tokenPath, 'utf8');
// Read private key file
const privateKeyFileContents = fs.readFileSync(privateKeyPath, 'utf-8');
privateKeyFileContents.split(/\r?\n/).forEach(line => {
if (line != '-----BEGIN PRIVATE KEY-----' &&
line != '-----END PRIVATE KEY-----')
privateKey = privateKey.concat(line);
});
} catch (err) {
console.error(err);
}
const tokenBasedAuthData = {
token : token,
privateKey : privateKey
};
return tokenBasedAuthData;
}
// User defined callback function which is invoked by node-oracledb when a
// token expires and the pool needs to create new connections
function tokenCallback() {
return getToken();
}
async function run() {
// Get token and private key for initial pool creation.
const accessTokenData = getToken();
// Configuration for token based authentication:
// accessToken: The initial token values
// accessTokenCallback: A callback function to provide a refreshed token
// externalAuth: Must be set to true for token based authentication
// homogeneous: Must be set to true for token based authentication
// connectString: The NODE_ORACLEDB_CONNECTIONSTRING environment
// variable set to the Oracle Net alias or connect
// descriptor of your Oracle Autonomous Database
const config = {
accessToken : accessTokenData,
accessTokenCallback : tokenCallback,
externalAuth : true,
homogeneous : true,
connectString : process.env.NODE_ORACLEDB_CONNECTIONSTRING,
};
try {
// Create pool using token based authentication
await oracledb.createPool(config);
// A real app would call createConnection() multiple times over a long
// period of time. During this time the pool may grow. If the initial
// token has expired, node-oracledb will automatically call the
// accessTokenCallback function allowing you to update the token.
await createConnection();
} catch (err) {
console.error(err);
} finally {
await closePoolAndExit();
}
}
async function createConnection() {
let connection;
try {
// Get a connection from the default pool
connection = await oracledb.getConnection();
const sql = `SELECT TO_CHAR(current_date, 'DD-Mon-YYYY HH24:MI') AS D
FROM DUAL`;
const result = await connection.execute(sql);
console.log("Result is:\n", result);
} catch (err) {
console.error(err);
} finally {
if (connection) {
// Put the connection back in the pool
await connection.close();
}
}
}
async function closePoolAndExit() {
console.log('\nTerminating');
try {
// Get the pool from the pool cache and close it
await oracledb.getPool().close(0);
process.exit(0);
} catch (err) {
console.error(err.message);
process.exit(1);
}
}
process
.once('SIGTERM', closePoolAndExit)
.once('SIGINT', closePoolAndExit);
run();

View File

@ -106,6 +106,7 @@ class OracleDb {
this.shutdown = nodbUtil.callbackify(shutdown).bind(_oracledb);
this.startup = nodbUtil.callbackify(startup).bind(_oracledb);
this.initOracleClient = this.initOracleClient.bind(_oracledb);
this._accessTokenHandler = this._accessTokenHandler.bind(_oracledb);
}
// temporary method for determining if an object is a date until
@ -114,6 +115,22 @@ class OracleDb {
return util.isDate(val);
}
// handler for access token callbacks
async _accessTokenHandler(userFn, externalObj, refresh) {
let accessToken;
try {
let result = userFn(refresh);
if (result instanceof Promise) {
result = await result;
}
_checkToken(result);
accessToken = result;
} finally {
this._returnAccessToken(externalObj, accessToken);
}
}
// retrieves a pool from the pool cache (synchronous method)
getPool(poolAlias) {
let pool;
@ -176,15 +193,6 @@ async function createPool(poolAttrs) {
}
if (poolAttrs.accessToken !== undefined) {
// token and privateKey must be set for token based
// authentication
if (poolAttrs.accessToken.token === undefined ||
poolAttrs.accessToken.token === '' ||
poolAttrs.accessToken.privateKey === undefined ||
poolAttrs.accessToken.privateKey === '') {
throw new Error(nodbUtil.getErrorMessage('NJS-084'));
}
// cannot set username or password for token based authentication
if (poolAttrs.user !== undefined ||
poolAttrs.password !== undefined) {
@ -199,11 +207,6 @@ async function createPool(poolAttrs) {
}
}
if (poolAttrs.accessToken === undefined &&
poolAttrs.accessTokenCallback !== undefined) {
throw new Error(nodbUtil.getErrorMessage('NJS-084'));
}
// create an adjusted set of pool attributes to pass to the C layer; the
// session callback must be removed if it is a JavaScript function and the
// queue timeout is used to specify the maximum amount of time that the C
@ -220,9 +223,27 @@ async function createPool(poolAttrs) {
if (adjustedPoolAttrs.queueTimeout === undefined) {
adjustedPoolAttrs.queueTimeout = this.queueTimeout;
}
// token based authentication
if (poolAttrs.accessToken !== undefined) {
adjustedPoolAttrs.token = poolAttrs.accessToken.token;
adjustedPoolAttrs.privateKey = poolAttrs.accessToken.privateKey;
// accessTokenCallback is depricated from node-oracledb 5.5
if (poolAttrs.accessTokenCallback !== undefined &&
typeof poolAttrs.accessToken === 'function') {
throw new Error(nodbUtil.getErrorMessage('NJS-088'));
}
let accessToken;
if (typeof poolAttrs.accessToken === 'function') {
adjustedPoolAttrs.accessTokenCallback = poolAttrs.accessToken;
accessToken = await poolAttrs.accessToken(false);
if (_isTokenExpired(accessToken)) {
accessToken = await poolAttrs.accessToken(true);
}
} else {
accessToken = poolAttrs.accessToken;
}
_checkToken(accessToken, adjustedPoolAttrs);
}
// Need to prevent another call in the same stack from succeeding, otherwise
@ -305,15 +326,6 @@ async function getConnection(a1) {
// otherwise, create a new standalone connection
} else {
if (connAttrs.accessToken !== undefined) {
// token and privateKey must be set for token based
// authentication
if (connAttrs.accessToken.token === undefined ||
connAttrs.accessToken.token === '' ||
connAttrs.accessToken.privateKey === undefined ||
connAttrs.accessToken.privateKey === '') {
throw new Error(nodbUtil.getErrorMessage('NJS-084'));
}
// cannot set username or password for token based authentication
if (connAttrs.user !== undefined ||
connAttrs.password !== undefined) {
@ -325,9 +337,17 @@ async function getConnection(a1) {
throw new Error(nodbUtil.getErrorMessage('NJS-086'));
}
connAttrs.token = connAttrs.accessToken.token;
connAttrs.privateKey =
connAttrs.accessToken.privateKey;
let accessToken;
if (typeof connAttrs.accessToken === 'function') {
accessToken = await connAttrs.accessToken(false);
if (_isTokenExpired(accessToken)) {
accessToken = await connAttrs.accessToken(true);
}
} else {
accessToken = connAttrs.accessToken;
}
_checkToken(accessToken, connAttrs);
}
try {
@ -427,6 +447,78 @@ async function startup(a1, a2) {
}
//-----------------------------------------------------------------------------
// _isTokenExpiredUtil()
// Function to check validity and expiry of token
//-----------------------------------------------------------------------------
function _isTokenExpiredUtil(accessToken) {
nodbUtil.assert(typeof accessToken === 'string', 'NJS-084', 1);
if (accessToken.split('.')[1] === undefined) {
throw new Error(nodbUtil.getErrorMessage('NJS-084'));
}
const base64Url = accessToken.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const buff = Buffer.from(base64, 'base64');
const payloadInit = buff.toString('ascii');
let expiry = JSON.parse(payloadInit).exp;
nodbUtil.assert(expiry != undefined, 'NJS-084');
expiry = expiry * 1000;
return (new Date().getTime() > expiry);
}
//-----------------------------------------------------------------------------
// _isTokenExpired()
// Function to check validity of token parameter
//-----------------------------------------------------------------------------
function _isTokenExpired(accessToken) {
switch (typeof accessToken) {
case 'string':
if (accessToken === '') {
throw new Error(nodbUtil.getErrorMessage('NJS-084'));
}
return _isTokenExpiredUtil(accessToken);
case 'object':
if (accessToken.token === undefined ||
accessToken.token === '' ||
accessToken.privateKey === undefined ||
accessToken.privateKey === '') {
throw new Error(nodbUtil.getErrorMessage('NJS-084'));
}
return _isTokenExpiredUtil(accessToken.token);
default:
throw new Error(nodbUtil.getErrorMessage('NJS-084'));
}
}
//-----------------------------------------------------------------------------
// _checkToken()
// Function to check validity of token parameter
//-----------------------------------------------------------------------------
function _checkToken(accessToken, attrs) {
if (_isTokenExpired(accessToken)) {
throw new Error(nodbUtil.getErrorMessage('NJS-087'));
}
if (attrs === undefined)
return;
if (typeof accessToken === 'string') {
attrs.token = accessToken;
} else {
attrs.token = accessToken.token;
attrs.privateKey = accessToken.privateKey;
}
}
// create instance which will be exported
let oracleDbInst = new OracleDb();

View File

@ -67,9 +67,11 @@ const errorMessages = {
'NJS-081': 'NJS-081: concurrent operations on a connection are disabled',
'NJS-082': 'NJS-082: connection pool is being reconfigured',
'NJS-083': 'NJS-083: pool statistics not enabled',
'NJS-084': 'NJS-084: invalid or missing parameter with token based authentication. The token and privateKey attributes must contain values. Other credentials cannot be specified',
'NJS-084': 'NJS-084: invalid access token',
'NJS-085': 'NJS-085: invalid connection pool configuration with token based authentication. The homogeneous and externalAuth attributes must be set to true',
'NJS-086': 'NJS-086: invalid standalone configuration with token based authentication. The externalAuth attribute must be set to true'
'NJS-086': 'NJS-086: invalid standalone configuration with token based authentication. The externalAuth attribute must be set to true',
'NJS-087': 'NJS-087: access token has expired',
'NJS-088': 'NJS-088: accessTokenCallback cannot be specified when accessToken is a function'
};
// getInstallURL returns a string with installation URL

View File

@ -37,7 +37,6 @@
},
"scripts": {
"test": "mocha --config test/opts/.mocharc.yaml",
"testtoken": "mocha --config test/opts/.mocharctoken.yaml",
"posttest": "node test/opts/version.js",
"buildbinary": "node package/buildbinary.js",
"buildpackage": "node package/buildpackage.js"

View File

@ -90,9 +90,11 @@ static const char *njsErrorMessages[] = {
"NJS-081: concurrent operations on a connection are disabled", //errConcurrentOps
"NJS-082: connection pool is being reconfigured", // errPoolReconfiguring
"NJS-083: pool statistics not enabled", // errPoolStatisticsDisabled
"NJS-084: invalid or missing parameter with token based authentication. The token and privateKey attributes must contain values. Other credentials cannot be specified", // errTokenBasedAuth
"NJS-084: invalid access token", // errTokenBasedAuth
"NJS-085: invalid connection pool configuration with token based authentication. The homogeneous and externalAuth attributes must be set to true", // errPoolTokenBasedAuth
"NJS-086: invalid standalone configuration with token based authentication. The externalAuth attribute must be set to true", // errStandaloneTokenBasedAuth
"NJS-087: access token has expired", //errExpiredToken
"NJS-088: accessTokenCallback cannot be specified when accessToken is a function" //errAccessTokenCallback
};

View File

@ -209,6 +209,8 @@ typedef enum {
errTokenBasedAuth,
errPoolTokenBasedAuth,
errStandaloneTokenBasedAuth,
errExpiredToken,
errAccessTokenCallback,
// New ones should be added here
@ -678,6 +680,8 @@ struct njsOracleDb {
napi_ref jsSodaDocumentConstructor;
napi_ref jsSodaOperationConstructor;
napi_ref jsSubscriptions;
napi_ref jsTokenCallbackHandler;
};
// data for class Pool exposed to JS.
@ -838,6 +842,7 @@ struct njsDbObjectAttr {
// data for managing callback
struct njsTokenCallback {
dpiAccessToken *accessToken;
njsOracleDb *oracleDb;
uv_async_t async;
uv_mutex_t mutex;
uv_barrier_t barrier;
@ -1139,5 +1144,7 @@ int njsTokenCallback_eventHandler(njsTokenCallback *callback,
bool njsTokenCallback_new(njsBaton *baton, napi_env env);
bool njsTokenCallback_startNotifications(njsTokenCallback *callback,
napi_env env);
bool njsTokenCallback_returnAccessToken(njsTokenCallback *callback,
napi_env env, napi_value accessToken);
bool njsTokenCallback_stopNotifications(njsTokenCallback *callback);
#endif /* __NJSMODULE_H__ */

View File

@ -29,6 +29,7 @@
static NJS_NAPI_METHOD(njsOracleDb_createPool);
static NJS_NAPI_METHOD(njsOracleDb_getConnection);
static NJS_NAPI_METHOD(njsOracleDb_initOracleClient);
static NJS_NAPI_METHOD(njsOracleDb_returnAccessToken);
// asynchronous methods
static NJS_ASYNC_METHOD(njsOracleDb_createPoolAsync);
@ -349,6 +350,8 @@ static const napi_property_descriptor njsClassProperties[] = {
napi_default, NULL },
{ "_initOracleClient", NULL, njsOracleDb_initOracleClient, NULL, NULL,
NULL, napi_default, NULL },
{ "_returnAccessToken", NULL, njsOracleDb_returnAccessToken, NULL, NULL,
NULL, napi_default, NULL },
{ NULL, NULL, NULL, NULL, NULL, NULL, napi_default, NULL }
};
@ -500,7 +503,7 @@ static bool njsOracleDb_createPoolAsync(njsBaton *baton)
commonParams.stmtCacheSize = baton->stmtCacheSize;
// set token based auth parameters
if (baton->token && baton->privateKey) {
if (baton->token) {
accessToken.token = baton->token;
accessToken.tokenLength = baton->tokenLength;
accessToken.privateKey = baton->privateKey;
@ -623,6 +626,7 @@ static void njsOracleDb_finalize(napi_env env, void *finalizeData,
NJS_DELETE_REF_AND_CLEAR(oracleDb->jsSodaDocumentConstructor);
NJS_DELETE_REF_AND_CLEAR(oracleDb->jsSodaOperationConstructor);
NJS_DELETE_REF_AND_CLEAR(oracleDb->jsSubscriptions);
NJS_DELETE_REF_AND_CLEAR(oracleDb->jsTokenCallbackHandler);
if (oracleDb->context) {
dpiContext_destroy(oracleDb->context);
oracleDb->context = NULL;
@ -706,7 +710,7 @@ static bool njsOracleDb_getConnectionAsync(njsBaton *baton)
commonParams.stmtCacheSize = baton->stmtCacheSize;
// set token based auth parameters
if (baton->token && baton->privateKey) {
if (baton->token) {
accessToken.token = baton->token;
accessToken.tokenLength = baton->tokenLength;
accessToken.privateKey = baton->privateKey;
@ -1237,6 +1241,43 @@ static napi_value njsOracleDb_initOracleClient(napi_env env,
}
//-----------------------------------------------------------------------------
// njsOracleDb_returnAccessTokenHelper()
// Helper method that performs the work of njsOracleDb_returnAccessToken().
//-----------------------------------------------------------------------------
static bool njsOracleDb_returnAccessTokenHelper(napi_env env,
napi_callback_info info)
{
njsBaseInstance *callingInstance;
napi_value callingObj, args[2];
njsTokenCallback *callback;
if (!njsUtils_validateArgs(env, info, 2, args, &callingObj,
&callingInstance))
return false;
NJS_CHECK_NAPI(env, napi_get_value_external(env, args[0],
(void**) &callback))
return njsTokenCallback_returnAccessToken(callback, env, args[1]);
}
//-----------------------------------------------------------------------------
// njsOracleDb_returnAccessToken()
// Returns the access token through to the callback. This needs to be done
// independently in order to handle possible asynchronous Javascript code.
//
// PARAMETERS
// - externalObj (contains native njsAccessToken structure)
// - accessToken (value to be returned through callback)
//-----------------------------------------------------------------------------
static napi_value njsOracleDb_returnAccessToken(napi_env env,
napi_callback_info info)
{
njsOracleDb_returnAccessTokenHelper(env, info);
return NULL;
}
//-----------------------------------------------------------------------------
// njsOracleDb_initCommonCreateParams()
// Initialize common creation parameters for pools and standalone
@ -1444,6 +1485,13 @@ bool njsOracleDb_prepareClass(njsOracleDb *oracleDb, napi_env env,
// keep a reference to it, if requested
if (clsRef) {
NJS_CHECK_NAPI(env, napi_create_reference(env, cls, 1, clsRef))
// otherwise, acquire access token callback handler and store reference
} else {
NJS_CHECK_NAPI(env, napi_get_named_property(env, instance,
"_accessTokenHandler", &tempResult))
NJS_CHECK_NAPI(env, napi_create_reference(env, tempResult, 1,
&oracleDb->jsTokenCallbackHandler))
}
return true;

View File

@ -31,7 +31,7 @@ static void njsTokenCallback_onStopNotifications(uv_handle_t *handle);
static bool njsTokenCallback_onStopNotificationsHelper(napi_env env,
njsTokenCallback *callback);
static void njsTokenCallback_processNotification(uv_async_t *handle);
static bool njsTokenCallback_processNotificationHelper(napi_env env,
static bool njsTokenCallback_processNotificationHelper(
njsTokenCallback *callback);
static void njsTokenCallback_waitOnBarrier(njsTokenCallback *callback);
@ -48,23 +48,18 @@ int njsTokenCallback_eventHandler(njsTokenCallback *callback,
{
uv_mutex_lock(&callback->mutex);
uv_barrier_init(&callback->barrier, 2);
uv_mutex_unlock(&callback->mutex);
if (uv_async_send(&callback->async) < 0) {
njsTokenCallback_waitOnBarrier(callback);
return -1;
}
callback->result = false;
uv_async_send(&callback->async);
njsTokenCallback_waitOnBarrier(callback);
if (!callback->result)
return -1;
return DPI_FAILURE;
tokenRefresh->token = callback->accessToken->token;
tokenRefresh->tokenLength = callback->accessToken->tokenLength;
tokenRefresh->privateKey =
callback->accessToken->privateKey;
tokenRefresh->privateKeyLength
= callback->accessToken->privateKeyLength;
return 0;
tokenRefresh->privateKey = callback->accessToken->privateKey;
tokenRefresh->privateKeyLength = callback->accessToken->privateKeyLength;
uv_mutex_unlock(&callback->mutex);
return DPI_SUCCESS;
}
@ -81,6 +76,7 @@ bool njsTokenCallback_new(njsBaton *baton, napi_env env)
if (!baton->accessTokenCallback->accessToken)
return njsBaton_setError(baton, errInsufficientMemory);
baton->accessTokenCallback->env = env;
baton->accessTokenCallback->oracleDb = baton->oracleDb;
return true;
}
@ -118,14 +114,13 @@ static void njsTokenCallback_processNotification(uv_async_t *handle)
{
njsTokenCallback *callback = (njsTokenCallback*) handle->data;
napi_handle_scope scope;
bool result;
if (napi_open_handle_scope(callback->env, &scope) != napi_ok)
return;
uv_mutex_lock(&callback->mutex);
callback->result = njsTokenCallback_processNotificationHelper(callback->env,
callback);
njsTokenCallback_waitOnBarrier(callback);
uv_mutex_unlock(&callback->mutex);
result = njsTokenCallback_processNotificationHelper(callback);
if (!result)
njsTokenCallback_waitOnBarrier(callback);
napi_close_handle_scope(callback->env, scope);
}
@ -135,38 +130,90 @@ static void njsTokenCallback_processNotification(uv_async_t *handle)
// Helper method for processing notifications so that the scope that is
// opened can be easily destroyed.
//-----------------------------------------------------------------------------
static bool njsTokenCallback_processNotificationHelper(napi_env env,
static bool njsTokenCallback_processNotificationHelper(
njsTokenCallback *callback)
{
dpiAccessToken *accessToken = callback->accessToken;
napi_value callbackRef, global, message, result, payloadObj;
size_t tempLength;
napi_value jsCallback, jsCallbackHandler, global, refresh, result;
napi_value jsCallbackArgs[3], externalObj;
napi_env env = callback->env;
// acquire callback and refreshed tokens
NJS_CHECK_NAPI(env, napi_get_global(env, &global))
NJS_CHECK_NAPI(env, napi_get_reference_value(env, callback->jsCallback,
&callbackRef))
&jsCallback))
NJS_CHECK_NAPI(env, napi_get_reference_value(env,
callback->oracleDb->jsTokenCallbackHandler, &jsCallbackHandler))
NJS_CHECK_NAPI(env, napi_create_external(env, callback, NULL, NULL,
&externalObj))
NJS_CHECK_NAPI(env, napi_get_boolean(env, true, &refresh))
jsCallbackArgs[0] = jsCallback;
jsCallbackArgs[1] = externalObj;
jsCallbackArgs[2] = refresh;
NJS_CHECK_NAPI(env, napi_make_callback(env, NULL, global,
jsCallbackHandler, 3, jsCallbackArgs, &result));
// perform callback
NJS_CHECK_NAPI(env, napi_make_callback(env, NULL, global, callbackRef,
1, &message, &result));
return true;
}
// Read "token" property
NJS_CHECK_NAPI(env, napi_get_named_property(env, result, "token",
&payloadObj))
if (!njsUtils_setPropString(env, payloadObj, "token",
(char**) &accessToken->token, &tempLength))
return false;
accessToken->tokenLength = (uint32_t) tempLength;
// Read "privateKey" property
NJS_CHECK_NAPI(env, napi_get_named_property(env, result, "privateKey",
&payloadObj))
if (!njsUtils_setPropString(env, payloadObj, "privateKey",
(char**) &accessToken->privateKey, &tempLength))
return false;
accessToken->privateKeyLength = (uint32_t) tempLength;
//-----------------------------------------------------------------------------
// njsTokenCallback_returnAccessTokenHelper()
// Helper function for njsTokenCallback_returnAccessToken().
//-----------------------------------------------------------------------------
static bool njsTokenCallback_returnAccessTokenHelper(njsTokenCallback *callback,
napi_env env, napi_value payloadObj)
{
dpiAccessToken *accessToken = callback->accessToken;
napi_valuetype actualType;
size_t tempLength;
napi_value temp;
NJS_CHECK_NAPI(env, napi_typeof(env, payloadObj, &actualType))
if (actualType == napi_undefined) {
accessToken->token = NULL;
accessToken->tokenLength = 0;
accessToken->privateKey = NULL;
accessToken->privateKeyLength = 0;
} else if (actualType == napi_string) {
if (!njsUtils_setPropString(env, payloadObj, "token",
(char**) &accessToken->token, &tempLength))
return false;
accessToken->tokenLength = (uint32_t) tempLength;
} else if (actualType == napi_object) {
// Read "token" property
NJS_CHECK_NAPI(env, napi_get_named_property(env, payloadObj, "token",
&temp))
if (!njsUtils_setPropString(env, temp, "token",
(char**) &accessToken->token, &tempLength))
return false;
accessToken->tokenLength = (uint32_t) tempLength;
// Read "privateKey" property
NJS_CHECK_NAPI(env, napi_get_named_property(env, payloadObj,
"privateKey", &temp))
if (!njsUtils_setPropString(env, temp, "privateKey",
(char**) &accessToken->privateKey, &tempLength))
return false;
accessToken->privateKeyLength = (uint32_t) tempLength;
}
return true;
}
//-----------------------------------------------------------------------------
// njsTokenCallback_returnAccessToken()
// Called by the JavaScript callback handler when it has completed. If an
// error has taken place, the value returned is the "undefined" Javascript
// value.
//-----------------------------------------------------------------------------
bool njsTokenCallback_returnAccessToken(njsTokenCallback *callback,
napi_env env, napi_value payloadObj)
{
callback->result = njsTokenCallback_returnAccessTokenHelper(callback, env,
payloadObj);
njsTokenCallback_waitOnBarrier(callback);
return true;
}

View File

@ -23,12 +23,9 @@ limitations under the License.
- 1.3 [Build](#build)
- 1.4 [Configure Database credentials](#credentials)
- 1.5 [Set NODE_PATH](#nodepath)
- 1.6 [Configure OCI-CLI](#ocicli)
- 1.7 [Configuration for token based authentication](#tokenbasedauth)
2. [Run tests](#runtests)
- 2.1 [Run the complete test suite](#runall)
- 2.2 [Run specified tests](#runspecified)
- 2.3 [Run token based authentication tests](#runtokenbasedtest)
3. [Enable tests that require extra configuration](#enablespecified)
- 3.1 [externalProxyAuth.js](#externalproxyauth)
4. [Contribute New Tests](#addtests)
@ -108,10 +105,6 @@ Set the following environment variables to provide credentials for the test suit
* `NODE_ORACLEDB_QA`. This boolean environment variable serves as the toggle switch of certain tests. Some tests, such as `callTimeout.js`, use hard-coded variables as assertion condition. The test results may be inconsistent in different network situations.
* `NODE_ORACLEDB_ACCESS_TOKEN_LOC` provides the directory path where the valid Oracle Identity and Access Manager (IAM) database token and private key files are. These files are generated by the Oracle Cloud Infrastructure Command Line Interface (OCI-CLI) command oci iam db-token get. This is required in token based authentication.
* `NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC` provides the directory path where expired Oracle Identity and Access Manager (IAM) database token and private key files are. These files are generated by the Oracle Cloud Infrastructure Command Line Interface (OCI-CLI) command oci iam db-token get. This is required in token based authentication testing.
Note: the test suite requires the schema to have these privileges: CREATE TABLE, CREATE SESSION,
CREATE PROCEDURE, CREATE SEQUENCE, CREATE TRIGGER, and CREATE TYPE.
@ -121,154 +114,6 @@ CREATE PROCEDURE, CREATE SEQUENCE, CREATE TRIGGER, and CREATE TYPE.
export NODE_PATH=<some-directory>/node-oracledb/lib
```
### <a name="ocicli"></a> 1.6 Configure OCI-CLI
This tool is used in token based authentication for generating token.
Install OCI-CLI tool from below link:
https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliinstall.htm
* Configuration steps for OCI-CLI
- Execute command `oci setup config` through command line terminal.
This command provides a walkthrough of creating a valid CLI config file.
- Enter a location for your config :
Location where you want to place config in local machine.
Default location is ~/.oci
- Enter user OCID:
OCID of IAM user from autonomous database.
- Enter a tenancy OCID:
tenancy OCID of admin account from autonomous database.
- Enter a region by index or name
Index or region name from dropdown as shown in terminal.
Region name can be found in top right corner of oracle cloud webpage.
Example: US East (Ashburn)
- Do you want to generate a new API Signing RSA key pair?
(If you decline you will be asked to supply the path to an existing key.) [Y/n]: Y
- Enter a directory for your keys to be created [~/.oci]:
Location where you want private/public key files to be generated.
- Enter a name for your key [oci_api_key]:
Your public/private key filename. Default is "oci_api_key"
- Public key written to: ~/.oci/oci_api_key_public.pem
- Enter a passphrase for your private key (empty for no passphrase):
Can keep it as blank
- Private key written to: ~/.oci/oci_api_key.pem
- Config written to ~/.oci/conf
- Upload public key to OCI cloud.
Copy public key generated to console->User->API Keys->Add API Keys.
- Command to generate db-token
`oci iam db-token get`
### <a name="tokenbasedauth"></a> 1.7 Configuration for token based authentication
Steps to configure autonomous database for token based authentication.
The steps below summarize the Oracle Autonomous Database
[Use Identity and Access Management (IAM)
Authentication with Autonomous
Database](https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/manage-users-iam.html#GUID-4E206209-4E3B-4387-9364-BDCFB4E16E2E)
documentation.
* Enable Identity and Access Management (IAM) Authentication on Autonomous Database
As the privileged database user, Enable IAM token support on Autonomous
Database:
```
BEGIN
DBMS_CLOUD_ADMIN.ENABLE_EXTERNAL_AUTHENTICATION (type => 'OCI_IAM');
END;
/
```
Confirm the `identity_provider_type` is `OCI_IAM`:
```
SELECT name, value
FROM V$PARAMETER
WHERE name = 'identity_provider_type';
```
* Create Identity and Access Management (IAM) Groups and Policies for IAM Users
To enable Autonomous Database to allow IAM users to connect using IAM
tokens, perform the Oracle Cloud Infrastructure Identity and Access
Management prerequisites by creating a group and add the IAM user to the
newly created group.
Use the cloud console to create a group, for example `MyIAMGroupName`.
Create a policy in the cloud console to allow any user belonging to the new group to
access Oracle Autonomous Database, for example:
```
allow group MyIAMGroupName to use autonomous-database-family in tenancy
```
* Create an IAM User
Create an IAM user using the Oracle Cloud console.
* Add the IAM User to Autonomous Database
Map a database global user to the IAM group, for example:
```
CREATE USER mydbuser IDENTIFIED GLOBALLY AS 'IAM_GROUP_NAME = MyIAMGroupName';
```
5. Add IAM Roles on Autonomous Database
Create a role for database authorization:
```
CREATE ROLE myrole IDENTIFIED GLOBALLY AS 'IAM_GROUP_NAME = MyIAMGroupName';
```
6. Grant permissions
```
GRANT CREATE SESSION TO myrole;
GRANT DWROLE to myrole;
```
7. Download a Wallet
Download the wallet from the Oracle Cloud console. Unzip the file.
8. Edit the Network Configuration file
Update the sqlnet.ora file:
```
WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="?/network/admin")))
SSL_SERVER_DN_MATCH = yes
SQLNET.AUTHENTICATION_SERVICE = (TCPS)
SSL_VERSION = 0
SSL_CLIENT_AUTHENTICATION = TRUE
```
9. Wallet location
If the files extracted from the wallet zip are NOT in a default location
then also change the WALLET_LOCATION directory. Default locations include
the `network/admin` subdirectory of Oracle Instant Client.
For example if your files are in `/opt/oracle/wallet`, then change the first
line to:
```
WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="/opt/oracle/wallet")))
```
## <a name="runtests"></a> 2. Run tests
See [mochajs.org](http://mochajs.org/) for more information on running tests with mocha.
@ -287,13 +132,6 @@ cd node_oracledb
./node_modules/.bin/mocha test/<test-names>
```
### <a name="runtokenbasedtest"></a> 2.3 Run token based authentication tests
```
cd node-oracledb
npm testtoken
```
## <a name="enablespecified"></a> 3. Enable tests that requires extra configuration
The following test(s) are automatically skipped if their required environment variable(s) are not properly set.

View File

@ -5318,70 +5318,15 @@ oracledb.OUT_FORMAT_OBJECT and resultSet = true
264. methodName.js
264.1 check for methodName getConnection
265. tokenbasedAuthentication.js
265.1 pool
265.1.1 create pool connection
265.1.2 acquire multiple sessions from pool
265.1.3 acquire multiple sessions from multiple pool
265.1.4 query execution
265.1.5 homogeneous should be true
265.1.6 externalAuth should be false
265.1.7 token should be set in accessToken
265.1.8 token length should be not be 0 in accessToken
265.1.9 privateKey should be set in accessToken
265.1.10 privateKey length should not be 0 in accessToken
265.1.11 with callback having valid token
265.1.12 with no callback and expired token
265.1.13 with callback having invalid token
265.1.14 with callback having missing token
265.1.15 with callback having 0 length token
265.1.16 with callback having missing privateKey
265.1.17 with callback having 0 length privateKey
265.1.18 user/password without tokens with externalAuth set to true
265.1.19 user/password with tokens
265.1.20 invalidate tokens using setAccessToken with missing token
265.1.21 invalidate tokens using setAccessToken with empty token
265.1.22 invalidate tokens using setAccessToken with empty privateKey
265.1.23 invalidate tokens using setAccessToken with missing privateKey
265.1.24 with callback having valid tokens in multiple pool
265.1.25 with callback having valid and invalid tokens in multiple pool
265.1.26 acquiring released session after token expiry
265.1.27 acquiring released session with existing tag, with token expiry
265.1.28 acquiring released session without existing tag, with token expiry
265.1.29 user/password without tokens with externalAuth set to false
265.1.30 token and private key should be private
265.1.31 token and private key value should not be shown in logs
265.1.32 random token and private key value
265.1.33 pool creation with 0 connection
265.1.34 token expires before creating connection
265.1.35 undefined token and private key value
265.1.36 user/password with accessTokenCallback
265.1.37 create pool connection using expired token
265.2 standalone
265.2.1 create standalone connection
265.2.2 standalone connection with invalid data
265.2.3 standalone connection with missing data
265.2.4 standalone connection with missing data
265.2.5 standalone connection with empty data
265.2.6 standalone connection with empty data
265.2.7 externalAuth must be set to true
265.2.8 user/password with tokens
265.2.9 user/password without tokens
265.2.10 standalone connection with undefined private key and token
265.2.11 standalone connection with random token and private key
265.2.12 token and private key are private
265.2.13 standalone connection with expired token
267. aq4.js
267.1 empty array or no recipients
267.2 single element in array
267.3 Negative - numbers as recipients
267.4 Negative - number, string, date as recipients
267.5 Negative - null value for recipient
267.6 Negative - undefined value for recipeint
267.7 Negative - dequeue non-existent name
267.8 empty recipient list with enqMany
267.9 recipient list with enqMany
267.10 recipient list with enqMany non-existent in dequeue
267.11 recipient list with enqMany invalid datatype in dequeue
267. aq4.js
267.1 empty array or no recipients
267.2 single element in array
267.3 Negative - numbers as recipients
267.4 Negative - number, string, date as recipients
267.5 Negative - null value for recipient
267.6 Negative - undefined value for recipeint
267.7 Negative - dequeue non-existent name
267.8 empty recipient list with enqMany
267.9 recipient list with enqMany
267.10 recipient list with enqMany non-existent in dequeue
267.11 recipient list with enqMany invalid datatype in dequeue

View File

@ -1,8 +0,0 @@
# Mocha options
timeout: 0
require: ['assert']
retries: 1
trace-warnings: true
spec:
- test/tokenBasedAuthentication.js

File diff suppressed because it is too large Load Diff

View File

@ -1,195 +0,0 @@
/* Copyright (c) 2022, Oracle and/or its affiliates. */
/******************************************************************************
*
* You may not use the identified files except in compliance with the Apache
* License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
* NAME
* dbConfigTokenBasedAuth.js
*
* PREREQUISITES
* Install Oracle cloud infrastructure command line interface (OCI-CLI).
* The command line interface (CLI) is a tool that enables you to work with
* Oracle Cloud Infrastructure objects and services at a command line.
* https://docs.oracle.com/en-us/iaas/Content/API/Concepts/cliconcepts.htm
* Oracle Client libraries 19.14 (or later), or 21.5 (or later).
* node-oracledb 5.4 or later.
*
* DESCRIPTION
* This file conduct the configuration work for token based authentication
* tests.
*
* Environment variables used:
* NODE_ORACLEDB_USER,
* NODE_ORACLEDB_PASSWORD,
* NODE_ORACLEDB_CONNECTIONSTRING,
* NODE_ORACLEDB_ACCESS_TOKEN_LOC
* NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC
*
******************************************************************************/
const fs = require('fs');
const { execSync } = require('child_process');
var config = {
test : {
printDebugMsg : false
}
};
// Execute the OCI-CLI command to generate a token.
// This should create two files "token" and "oci_db_key.pem"
// Default file location is ~/.oci/db-token
execSync('oci iam db-token get', { encoding: 'utf-8' });
if (process.env.NODE_ORACLEDB_CONNECTIONSTRING) {
config.connectString = process.env.NODE_ORACLEDB_CONNECTIONSTRING;
} else {
throw new Error('Database Connect String is not Set! Try Set Environment Variable NODE_ORACLEDB_CONNECTIONSTRING.');
}
if (process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC) {
config.tokenLocation = process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC;
} else {
throw new Error('Db-token file location is not set! Try Set Environment Variable NODE_ORACLEDB_ACCESS_TOKEN_LOC');
}
if (process.env.NODE_ORACLEDB_USER) {
config.user = process.env.NODE_ORACLEDB_USER;
} else {
throw new Error('User is not Set! Try Set Environment Variable NODE_ORACLEDB_USER.');
}
if (process.env.NODE_ORACLEDB_PASSWORD) {
config.password = process.env.NODE_ORACLEDB_PASSWORD;
} else {
throw new Error('Password is not Set! Try Set Environment Variable NODE_ORACLEDB_PASSWORD.');
}
if (process.env.NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC) {
config.expiredToken = process.env.NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC;
} else {
throw new Error('Expired token location is not Set! Try Set Environment Variable NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC.');
}
if (process.env.NODE_PRINT_DEBUG_MESSAGE) {
var printDebugMsg = process.env.NODE_PRINT_DEBUG_MESSAGE;
printDebugMsg = String(printDebugMsg);
printDebugMsg = printDebugMsg.toLowerCase();
if (printDebugMsg == 'true') {
config.test.printDebugMsg = true;
}
}
// User defined function for reading token and private key values generated by
// the OCI-CLI.
// NODE_ORACLEDB_DBTOKEN_LOC environment variable is used to provide directory
// path where token and private key files are stored.
function getToken(envName) {
const tokenPath = process.env[envName] + '/' + 'token';
const privateKeyPath = process.env[envName] + '/' + 'oci_db_key.pem';
let token = '';
let privateKey = '';
try {
// Read token file
token = fs.readFileSync(tokenPath, 'utf8');
// Read private key file
const privateKeyFileContents = fs.readFileSync(privateKeyPath, 'utf-8');
privateKeyFileContents.split(/\r?\n/).forEach(line => {
if (line != '-----BEGIN PRIVATE KEY-----' &&
line != '-----END PRIVATE KEY-----')
privateKey = privateKey.concat(line);
});
} catch (err) {
console.error(err);
}
const tokenBasedAuthData = {
token : token,
privateKey : privateKey
};
return tokenBasedAuthData;
}
config.accessToken = getToken('NODE_ORACLEDB_ACCESS_TOKEN_LOC');
config.expiredAccessToken = getToken('NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC');
// callback function with vlid data
// privateKey and token having valid data
function callbackValid() {
return config.accessToken;
}
// callback function with invalid data
// privateKey and token having invalid data
function callbackInvalid1() {
const obj1 = config.accessToken;
const obj2 = {
token : obj1.privateKey,
privateKey : obj1.token
};
return obj2;
}
// callback function with invalid data
// dbtoken attribute is missing
function callbackInvalid2() {
const obj1 = config.accessToken;
const obj2 = {
privateKey : obj1.privateKey
};
return obj2;
}
// callback function with invalid data
// dbtoken is empty
function callbackInvalid3() {
const obj1 = config.accessToken;
const obj2 = {
token : '',
privateKey : obj1.privateKey
};
return obj2;
}
// callback function with invalid data
// dbtokenPrivateKeyattribute is missing
function callbackInvalid4() {
const obj1 = config.accessToken;
const obj2 = {
token: obj1.token
};
return obj2;
}
// callback function with invalid data
// dbtokenPrivateKey is empty
function callbackInvalid5() {
const obj1 = config.accessToken;
const obj2 = {
token : obj1.token,
privateKey : ''
};
return obj2;
}
config.callbackValid = callbackValid();
config.callbackInvalid1 = callbackInvalid1();
config.callbackInvalid2 = callbackInvalid2();
config.callbackInvalid3 = callbackInvalid3();
config.callbackInvalid4 = callbackInvalid4();
config.callbackInvalid5 = callbackInvalid5();
module.exports = config;