oracledb 7.0.0 → 26.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +16 -6
  2. package/build/Release/oracledb-26.0.0-darwin-arm64.node +0 -0
  3. package/build/Release/oracledb-26.0.0-darwin-arm64.node-buildinfo.txt +1 -0
  4. package/build/Release/oracledb-26.0.0-js-buildinfo.txt +1 -0
  5. package/build/Release/oracledb-26.0.0-linux-arm64.node +0 -0
  6. package/build/Release/oracledb-26.0.0-linux-arm64.node-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-26.0.0-linux-x64.node +0 -0
  8. package/build/Release/oracledb-26.0.0-linux-x64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-26.0.0-win32-x64.node +0 -0
  10. package/build/Release/oracledb-26.0.0-win32-x64.node-buildinfo.txt +1 -0
  11. package/lib/aqDeqOptions.js +4 -4
  12. package/lib/aqEnqOptions.js +2 -2
  13. package/lib/aqQueue.js +11 -6
  14. package/lib/connection.js +73 -18
  15. package/lib/constants.js +4 -0
  16. package/lib/endUserSecurityContext.js +30 -25
  17. package/lib/errors.js +92 -5
  18. package/lib/impl/connection.js +36 -0
  19. package/lib/impl/datahandlers/buffer.js +91 -18
  20. package/lib/impl/datahandlers/oson.js +3 -2
  21. package/lib/impl/parserHelpers.js +6 -2
  22. package/lib/impl/resultset.js +32 -11
  23. package/lib/oracledb.js +34 -0
  24. package/lib/pool.js +6 -2
  25. package/lib/queryStream.js +13 -5
  26. package/lib/resultset.js +10 -3
  27. package/lib/securityContextProvider.js +206 -0
  28. package/lib/thin/aq.js +2 -3
  29. package/lib/thin/connection.js +380 -24
  30. package/lib/thin/dbObject.js +20 -11
  31. package/lib/thin/pool.js +4 -3
  32. package/lib/thin/protocol/capabilities.js +37 -4
  33. package/lib/thin/protocol/constants.js +49 -1
  34. package/lib/thin/protocol/messages/aqArray.js +2 -2
  35. package/lib/thin/protocol/messages/aqBase.js +2 -2
  36. package/lib/thin/protocol/messages/auth.js +17 -6
  37. package/lib/thin/protocol/messages/base.js +75 -17
  38. package/lib/thin/protocol/messages/dataType.js +46 -0
  39. package/lib/thin/protocol/messages/directPathLoadStream.js +3 -2
  40. package/lib/thin/protocol/messages/execute.js +10 -4
  41. package/lib/thin/protocol/messages/index.js +4 -0
  42. package/lib/thin/protocol/messages/notification.js +281 -0
  43. package/lib/thin/protocol/messages/subscribe.js +181 -0
  44. package/lib/thin/protocol/messages/withData.js +23 -10
  45. package/lib/thin/protocol/packet.js +2 -4
  46. package/lib/thin/protocol/protocol.js +7 -4
  47. package/lib/thin/resultSet.js +40 -20
  48. package/lib/thin/sqlnet/ezConnectResolver.js +9 -2
  49. package/lib/thin/sqlnet/navNodes.js +7 -5
  50. package/lib/thin/sqlnet/networkSession.js +17 -11
  51. package/lib/thin/sqlnet/ntTcp.js +24 -8
  52. package/lib/thin/sqlnet/packet.js +24 -4
  53. package/lib/thin/sqlnet/sessionAtts.js +2 -2
  54. package/lib/thin/statement.js +6 -1
  55. package/lib/thin/statementCache.js +34 -6
  56. package/lib/thin/subscr.js +363 -0
  57. package/lib/transformer.js +60 -17
  58. package/lib/util.js +46 -8
  59. package/lib/version.js +1 -1
  60. package/package.json +1 -1
  61. package/plugins/configProviders/gcpstorage/index.js +121 -0
  62. package/plugins/configProviders/ociobject/index.js +1 -1
  63. package/plugins/token/cache.js +154 -0
  64. package/plugins/token/endUserSecurityProvider/index.js +377 -0
  65. package/plugins/token/extensionAzure/index.js +160 -28
  66. package/plugins/token/extensionOci/index.js +221 -16
  67. package/plugins/vectorsdk/OracleVecDB.js +346 -0
  68. package/plugins/vectorsdk/index.js +49 -0
  69. package/plugins/vectorsdk/utils.js +370 -0
  70. package/plugins/vectorsdk/vectorEmbedding.js +223 -0
  71. package/plugins/vectorsdk/vectorIndex.js +177 -0
  72. package/plugins/vectorsdk/vectorRecords.js +406 -0
  73. package/plugins/vectorsdk/vectorSchema.js +408 -0
  74. package/plugins/vectorsdk/vectorSearch.js +538 -0
  75. package/build/Release/oracledb-7.0.0-darwin-arm64.node +0 -0
  76. package/build/Release/oracledb-7.0.0-darwin-arm64.node-buildinfo.txt +0 -1
  77. package/build/Release/oracledb-7.0.0-js-buildinfo.txt +0 -1
  78. package/build/Release/oracledb-7.0.0-linux-arm64.node +0 -0
  79. package/build/Release/oracledb-7.0.0-linux-arm64.node-buildinfo.txt +0 -1
  80. package/build/Release/oracledb-7.0.0-linux-x64.node +0 -0
  81. package/build/Release/oracledb-7.0.0-linux-x64.node-buildinfo.txt +0 -1
  82. package/build/Release/oracledb-7.0.0-win32-x64.node +0 -0
  83. package/build/Release/oracledb-7.0.0-win32-x64.node-buildinfo.txt +0 -1
  84. package/eslint.config.js +0 -39
@@ -26,9 +26,27 @@
26
26
  'use strict';
27
27
  const oracledb = require('oracledb');
28
28
  const msal = require('@azure/msal-node');
29
+ const { TokenCache, hashValue } = require('../cache.js');
29
30
 
31
+ const OBO_DEFAULT_CLOCK_SKEW_MS = 60 * 1000;
32
+ const TOKEN_CACHE_MAX_ENTRIES = 100;
33
+ // Process-wide, bounded LRU cache of completed OBO exchanges. Connections
34
+ // safely share entries only when their cache key has the same caller-token
35
+ // hash and every Azure credential/request input that can change the result.
36
+ // Values are obfuscated at rest.
37
+ const oboTokenCache = new TokenCache(TOKEN_CACHE_MAX_ENTRIES);
38
+ // Process-wide, bounded cache of service-principal tokens. Its key includes
39
+ // every credential and request input that can change the resulting token, so
40
+ // pools can share only compatible entries. Values are obfuscated at rest.
41
+ const appTokenCache = new TokenCache(TOKEN_CACHE_MAX_ENTRIES);
30
42
 
31
- async function getToken(params) {
43
+
44
+ // Internal Deep Security API. The driver's ordinary token-authentication hook
45
+ // below returns this result's access-token string.
46
+ async function getTokenResult(params) {
47
+ if (typeof params?.authType !== 'string') {
48
+ throwErr('Azure authentication type is required.');
49
+ }
32
50
  switch (params.authType.toLowerCase()) {
33
51
  case 'azureserviceprincipal':
34
52
  return await servicePrincipalCredentials(params);
@@ -46,9 +64,7 @@ function throwErr(message) {
46
64
 
47
65
  //---------------------------------------------------------------------------
48
66
  // Returns the access token for authentication as a service principal. This
49
- // authentication method requires a client ID along with either a secret or
50
- // certificate. These values may be provided as config parameters read by
51
- // the Azure SDK.
67
+ // authentication method requires a client ID and client secret.
52
68
  // ---------------------------------------------------------------------------
53
69
  async function servicePrincipalCredentials(params) {
54
70
  const clientId = params.clientId ??
@@ -57,31 +73,24 @@ async function servicePrincipalCredentials(params) {
57
73
  throwErr("Token based authentication config parameter authority is missing for azureServicePrincipal in extensionAzure plugins.");
58
74
  const clientSecret = params.clientSecret ??
59
75
  throwErr("Token based authentication config parameter clientSecret is missing for azureServicePrincipal in extensionAzure plugins.");
60
- const msalConfig = {
61
- auth: {
62
- clientId: clientId,
63
- authority: authority,
64
- clientSecret: clientSecret,
65
- },
66
- system: {
67
- // Use it while generating azure tokens behind proxy
68
- // Otherwise can remove this option
69
- networkClient: params.proxy,
70
- },
71
- };
72
-
73
- if (!msalConfig.system.networkClient) {
74
- delete msalConfig.system.networkClient;
75
- }
76
-
77
76
  const scopes = params.scopes ??
78
77
  throwErr("Token based authentication config parameter scopes is missing for azureServicePrincipal in extensionAzure plugins.");
79
- const tokenRequest = {
80
- scopes: [scopes],
81
- };
82
- const cca = new msal.ConfidentialClientApplication(msalConfig);
83
- const authResponse = await cca.acquireTokenByClientCredential(tokenRequest);
84
- return authResponse.accessToken;
78
+ const normalizedScopes = normalizeScopes(scopes);
79
+ const config = { ...params, clientId, authority, clientSecret };
80
+ const cacheKey = computeAppCacheKey(config, normalizedScopes);
81
+ const result = await appTokenCache.get(cacheKey, async () => {
82
+ const authResponse = await createClient(config).acquireTokenByClientCredential({
83
+ scopes: normalizedScopes,
84
+ });
85
+ if (!authResponse?.accessToken) {
86
+ throwErr('Failed to acquire application access token from Azure.');
87
+ }
88
+ return {
89
+ value: authResponse.accessToken,
90
+ validUntil: calculateExpiry(authResponse, OBO_DEFAULT_CLOCK_SKEW_MS),
91
+ };
92
+ });
93
+ return { accessToken: result.value, isNewToken: !result.isCacheHit };
85
94
  }
86
95
 
87
96
  // ---------------------------------------------------------------------------
@@ -92,9 +101,132 @@ async function servicePrincipalCredentials(params) {
92
101
  function hookFn(options) {
93
102
  if (options.tokenAuthConfigAzure) {
94
103
  options.accessToken = async function callbackFn(refresh, config) {
95
- return await getToken(config);
104
+ return (await getTokenResult(config)).accessToken;
96
105
  };
97
106
  options.accessTokenConfig = options.tokenAuthConfigAzure;
98
107
  }
99
108
  }
100
109
  oracledb.registerProcessConfigurationHook(hookFn);
110
+
111
+ //---------------------------------------------------------------------------
112
+ // normalizeScopes()
113
+ //---------------------------------------------------------------------------
114
+ function normalizeScopes(scopes) {
115
+ const normalized = Array.isArray(scopes)
116
+ ? scopes
117
+ : (typeof scopes === 'string' ? [scopes] : null);
118
+ if (!normalized || normalized.some((scope) =>
119
+ typeof scope !== 'string' || !scope.trim())) {
120
+ throwErr('Azure scopes must be a string or an array of non-empty strings.');
121
+ }
122
+ return normalized.map((scope) => scope.trim()).sort();
123
+ }
124
+
125
+ //---------------------------------------------------------------------------
126
+ // buildNetworkClient()
127
+ //---------------------------------------------------------------------------
128
+ function buildNetworkClient(proxyConfig) {
129
+ if (!proxyConfig) {
130
+ return null;
131
+ }
132
+ if (typeof proxyConfig.sendGetRequestAsync === 'function' &&
133
+ typeof proxyConfig.sendPostRequestAsync === 'function') {
134
+ return proxyConfig;
135
+ }
136
+ throwErr('Azure proxy configuration must implement sendGetRequestAsync() and sendPostRequestAsync().');
137
+ }
138
+
139
+ //---------------------------------------------------------------------------
140
+ // computeOboCacheKey()
141
+ //---------------------------------------------------------------------------
142
+ function computeOboCacheKey(config, scopes, endUserToken) {
143
+ // Do not put the assertion or client secret itself in a process-wide Map.
144
+ // The secret hash separates entries if a client secret is rotated while the
145
+ // process is running.
146
+ return JSON.stringify([
147
+ config.authType?.toLowerCase(), config.clientId,
148
+ hashValue(config.clientSecret), config.authority, scopes,
149
+ hashValue(endUserToken),
150
+ ]);
151
+ }
152
+
153
+ function computeAppCacheKey(config, scopes) {
154
+ // Hash the secret rather than retaining it in the process-wide key. A
155
+ // changed secret creates a new entry, leaving the old one bounded and
156
+ // eligible for LRU eviction.
157
+ return JSON.stringify([
158
+ config.authType?.toLowerCase(), config.clientId,
159
+ hashValue(config.clientSecret), config.authority, scopes,
160
+ ]);
161
+ }
162
+
163
+ function createClient(config) {
164
+ const networkClient = buildNetworkClient(config.proxy);
165
+ const msalConfig = {
166
+ auth: {
167
+ clientId: config.clientId ?? throwErr("OBO configuration requires clientId."),
168
+ authority: config.authority ?? throwErr("OBO configuration requires authority."),
169
+ clientSecret: config.clientSecret ?? throwErr("OBO configuration requires clientSecret."),
170
+ }
171
+ };
172
+ if (networkClient) {
173
+ msalConfig.system = { networkClient };
174
+ }
175
+
176
+ return new msal.ConfidentialClientApplication(msalConfig);
177
+ }
178
+
179
+ //---------------------------------------------------------------------------
180
+ // calculateExpiry()
181
+ //---------------------------------------------------------------------------
182
+ function calculateExpiry(result, clockSkewMs) {
183
+ if (result?.expiresOn instanceof Date) {
184
+ return result.expiresOn.getTime() - clockSkewMs;
185
+ }
186
+ throwErr('Azure token response did not include an expiration time.');
187
+ }
188
+
189
+ //---------------------------------------------------------------------------
190
+ // getOnBehalfOfToken()
191
+ //---------------------------------------------------------------------------
192
+ async function getOnBehalfOfToken({ endUserToken, oboConfig, cacheOptions }) {
193
+ if (!endUserToken) {
194
+ throwErr('An end user access token must be supplied for OBO exchange.');
195
+ }
196
+ if (!oboConfig) {
197
+ throwErr('OBO configuration is required.');
198
+ }
199
+
200
+ const scopes = normalizeScopes(oboConfig.scopes);
201
+ const cacheEnabled = cacheOptions?.enabled !== false;
202
+ const clockSkewMs = cacheOptions?.clockSkewMs ?? OBO_DEFAULT_CLOCK_SKEW_MS;
203
+ // This process-wide bounded cache is authoritative. Do not use MSAL's
204
+ // per-client cache because clients are intentionally short-lived so they do
205
+ // not retain credentials in a long-lived object graph.
206
+
207
+ const cacheKey = computeOboCacheKey(oboConfig, scopes, endUserToken);
208
+ const result = await oboTokenCache.get(cacheKey, async () => {
209
+ const authResponse = await createClient(oboConfig).acquireTokenOnBehalfOf({
210
+ oboAssertion: endUserToken,
211
+ scopes,
212
+ skipCache: true
213
+ });
214
+ if (!authResponse?.accessToken) {
215
+ throwErr('Failed to acquire on-behalf-of access token from Azure.');
216
+ }
217
+ return {
218
+ value: authResponse.accessToken,
219
+ validUntil: calculateExpiry(authResponse, clockSkewMs),
220
+ };
221
+ }, { enabled: cacheEnabled });
222
+ return {
223
+ accessToken: result.value,
224
+ expiresOn: new Date(result.validUntil + clockSkewMs),
225
+ isNewToken: !result.isCacheHit,
226
+ };
227
+ }
228
+
229
+ module.exports = {
230
+ getTokenResult,
231
+ getOnBehalfOfToken
232
+ };
@@ -29,18 +29,59 @@ const identitydataplane = require("oci-identitydataplane");
29
29
  const common = require("oci-common");
30
30
  const { generateKeyPair } = require('crypto');
31
31
  const fs = require('fs');
32
+ const https = require('https');
33
+ const { URL } = require('url');
34
+ const { TokenCache, hashValue } = require('../cache.js');
35
+ const TOKEN_REQUEST_TIMEOUT_MS = 30000;
36
+ const APP_TOKEN_CACHE_CLOCK_SKEW_MS = 60 * 1000;
37
+ const APP_TOKEN_CACHE_MAX_ENTRIES = 100;
38
+ // Process-wide, bounded LRU cache for OCI client-credentials tokens. Its key
39
+ // includes the authentication inputs that can change a token. Other OCI
40
+ // authentication modes retain their pre-cache behavior.
41
+ const appTokenCache = new TokenCache(APP_TOKEN_CACHE_MAX_ENTRIES);
32
42
 
33
- async function getToken(params) {
43
+ // Internal Deep Security API. The driver's ordinary token-authentication hook
44
+ // below formats this result as a string or token/private-key pair.
45
+ async function getTokenResult(params) {
46
+ if (typeof params?.authType !== 'string') {
47
+ throwErr('OCI authentication type is required.');
48
+ }
49
+ const authType = params.authType.toLowerCase();
50
+ let result;
51
+ if (authType === 'clientcredentials') {
52
+ const cacheKey = computeAppCacheKey(params);
53
+ result = await appTokenCache.get(cacheKey, () => acquireToken(params));
54
+ } else {
55
+ result = { value: (await acquireToken(params)).value, isCacheHit: false };
56
+ }
57
+ if (typeof result.value === 'string') {
58
+ return { accessToken: result.value, isNewToken: !result.isCacheHit };
59
+ }
60
+ return { ...result.value, isNewToken: !result.isCacheHit };
61
+ }
62
+
63
+ async function acquireToken(params) {
64
+ let result;
34
65
  switch (params.authType.toLowerCase()) {
35
66
  case 'configfilebasedauthentication':
36
- return await configFileBasedAuthentication(params);
67
+ result = await configFileBasedAuthentication(params);
68
+ break;
37
69
  case 'simpleauthentication':
38
- return await simpleAuthentication(params);
70
+ result = await simpleAuthentication(params);
71
+ break;
39
72
  case 'instanceprincipal':
40
- return await instancePrincipalAuthentication(params);
73
+ result = await instancePrincipalAuthentication(params);
74
+ break;
75
+ case 'clientcredentials':
76
+ result = await clientCredentialsAuthentication(params);
77
+ break;
78
+ case 'resourceprincipal':
79
+ result = await resourcePrincipalAuthentication(params);
80
+ break;
41
81
  default:
42
82
  throwErr(`Invalid authentication type ${params.authType} in extensionOci plugins.`);
43
83
  }
84
+ return { value: result.value, validUntil: calculateValidUntil(result) };
44
85
  }
45
86
 
46
87
  //---------------------------------------------------------------------------
@@ -81,9 +122,16 @@ async function generateAccessToken(provider, scope) {
81
122
  const generateScopedAccessTokenResponse =
82
123
  await client.generateScopedAccessToken(generateScopedAccessTokenRequest);
83
124
 
125
+ const securityToken = generateScopedAccessTokenResponse.securityToken;
126
+ if (typeof securityToken?.token !== 'string' || !securityToken.token.trim()) {
127
+ throwErr('OCI scoped access token response is missing a token.');
128
+ }
84
129
  return {
85
- token: generateScopedAccessTokenResponse.securityToken.token,
86
- privateKey: keyPair.privateKey
130
+ value: {
131
+ token: securityToken.token,
132
+ privateKey: keyPair.privateKey
133
+ },
134
+ expiresOn: securityToken.expirationTime
87
135
  };
88
136
  }
89
137
 
@@ -153,21 +201,19 @@ async function simpleAuthentication(accessTokenConfig) {
153
201
  throwErr("Token based authentication config parameter user is missing for simpleauthentication in extensionOci plugins.");
154
202
  const fingerprint = accessTokenConfig.fingerprint ??
155
203
  throwErr("Token based authentication config parameter fingerprint is missing for simpleauthentication in extensionOci plugins.");
156
- const passphrase = accessTokenConfig.passphrase |= null; // optional
204
+ const passphrase = accessTokenConfig.passphrase ?? null; // optional
157
205
  const privateKeyLocation = accessTokenConfig.privateKeyLocation ??
158
206
  throwErr("Token based authentication config parameter privateKeyLocation is missing for simpleauthentication in extensionOci plugins.");
159
207
  const privateKey = fs.readFileSync(privateKeyLocation, 'utf-8'); // ~/.oci/oci_api_key.pem
160
208
  const regionId = accessTokenConfig.regionId ?? // ex : us-ashburn-1
161
209
  throwErr("Token based authentication config parameter regionId is missing for simpleauthentication in extensionOci plugins.");
162
210
 
163
- let region;
164
- const regionsList = common.Region.values();
165
- regionsList.forEach(regions => {
166
- if (regions.regionId === regionId) {
167
- region = regions;
168
- return;
169
- }
170
- });
211
+ const region = common.Region.values().find(
212
+ (item) => item.regionId === regionId
213
+ );
214
+ if (!region) {
215
+ throwErr(`Invalid OCI regionId ${regionId}.`);
216
+ }
171
217
  const provider = new common.SimpleAuthenticationDetailsProvider(
172
218
  tenancy,
173
219
  user,
@@ -194,6 +240,157 @@ async function instancePrincipalAuthentication(accessTokenConfig) {
194
240
  return await generateAccessToken(provider, accessTokenConfig.scope);
195
241
  }
196
242
 
243
+ //---------------------------------------------------------------------------
244
+ // clientCredentialsAuthentication()
245
+ //
246
+ // Requests an access token from an OCI Identity Domain using the OAuth 2.0
247
+ // client-credentials grant.
248
+ //---------------------------------------------------------------------------
249
+ async function clientCredentialsAuthentication(config) {
250
+ const authority = config.authority ??
251
+ throwErr("Token based authentication config parameter authority is missing for clientcredentials in extensionOci plugins.");
252
+ const clientId = config.clientId ??
253
+ throwErr("Token based authentication config parameter clientId is missing for clientcredentials in extensionOci plugins.");
254
+ const clientSecret = config.clientSecret ??
255
+ throwErr("Token based authentication config parameter clientSecret is missing for clientcredentials in extensionOci plugins.");
256
+ if (config.scopes !== undefined && typeof config.scopes !== 'string') {
257
+ throwErr('OCI OAuth scopes must be a string.');
258
+ }
259
+
260
+ const params = new URLSearchParams();
261
+
262
+ params.set("grant_type", "client_credentials");
263
+ if (config.scopes) {
264
+ // OAuth defines a singular form parameter; the driver configuration uses
265
+ // the same plural `scopes` spelling as the Azure token configuration.
266
+ params.set("scope", config.scopes);
267
+ }
268
+
269
+ const basicAuth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
270
+ const requestBody = params.toString();
271
+ const response = await postForm(authority, {
272
+ 'Authorization': `Basic ${basicAuth}`
273
+ }, requestBody, config.tokenRequestTimeoutMs);
274
+
275
+ let payload = null;
276
+ if (response && response.body) {
277
+ try {
278
+ payload = JSON.parse(response.body);
279
+ } catch {
280
+ payload = null;
281
+ }
282
+ }
283
+
284
+ const statusCode = Number(response?.statusCode ?? 0);
285
+ if (statusCode < 200 || statusCode >= 300) {
286
+ const statusText = response?.statusMessage || `HTTP ${statusCode}`;
287
+ const errorMessage =
288
+ payload?.error_description ||
289
+ payload?.error ||
290
+ statusText;
291
+ throwErr(`OCI client credentials token request failed: ${errorMessage}`);
292
+ }
293
+
294
+ const accessToken = payload?.access_token;
295
+ if (!accessToken) {
296
+ throwErr("OCI client credentials token response is missing access_token.");
297
+ }
298
+
299
+ return { value: accessToken, expiresIn: payload?.expires_in };
300
+ }
301
+
302
+ function calculateValidUntil(result) {
303
+ let expiresAt;
304
+ if (result.expiresOn instanceof Date) {
305
+ expiresAt = result.expiresOn.getTime();
306
+ } else if (typeof result.expiresOn === 'number') {
307
+ expiresAt = result.expiresOn;
308
+ } else if (typeof result.expiresOn === 'string') {
309
+ expiresAt = Date.parse(result.expiresOn);
310
+ } else if (Number.isFinite(Number(result.expiresIn)) &&
311
+ Number(result.expiresIn) > 0) {
312
+ expiresAt = Date.now() + (Number(result.expiresIn) * 1000);
313
+ }
314
+ return Number.isFinite(expiresAt) ?
315
+ expiresAt - APP_TOKEN_CACHE_CLOCK_SKEW_MS : undefined;
316
+ }
317
+
318
+ function computeAppCacheKey(config) {
319
+ // This function is called only for clientcredentials. Array positions are
320
+ // fixed, and JSON serialization makes the result unambiguous.
321
+ return JSON.stringify([
322
+ config.authType.toLowerCase(), config.authority, config.clientId,
323
+ hashValue(config.clientSecret), config.scopes,
324
+ ]);
325
+ }
326
+
327
+ //---------------------------------------------------------------------------
328
+ // postForm()
329
+ //
330
+ // Minimal HTTPS helper for posting x-www-form-urlencoded payloads.
331
+ //---------------------------------------------------------------------------
332
+ function postForm(endpoint, extraHeaders, body, timeoutMs = TOKEN_REQUEST_TIMEOUT_MS) {
333
+ return new Promise((resolve, reject) => {
334
+ try {
335
+ const url = new URL(endpoint);
336
+ if (url.protocol !== 'https:') {
337
+ throwErr('OCI token endpoint must use HTTPS.');
338
+ }
339
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 1) {
340
+ throwErr('OCI token request timeout must be a positive integer.');
341
+ }
342
+ const headers = Object.assign({
343
+ 'Content-Type': 'application/x-www-form-urlencoded',
344
+ 'Content-Length': Buffer.byteLength(body)
345
+ }, extraHeaders);
346
+
347
+ const requestOptions = {
348
+ method: 'POST',
349
+ protocol: url.protocol,
350
+ hostname: url.hostname,
351
+ port: url.port || (url.protocol === 'https:' ? 443 : undefined),
352
+ path: `${url.pathname}${url.search}`,
353
+ headers
354
+ };
355
+
356
+ const req = https.request(requestOptions, (res) => {
357
+ let responseBody = '';
358
+ res.setEncoding('utf8');
359
+ res.on('data', chunk => {
360
+ responseBody += chunk;
361
+ });
362
+ res.on('end', () => {
363
+ resolve({
364
+ statusCode: res.statusCode,
365
+ statusMessage: res.statusMessage,
366
+ body: responseBody
367
+ });
368
+ });
369
+ });
370
+
371
+ req.on('error', reject);
372
+ req.setTimeout(timeoutMs, () => {
373
+ req.destroy(new Error('OCI token request timed out.'));
374
+ });
375
+ req.write(body);
376
+ req.end();
377
+ } catch (err) {
378
+ reject(err);
379
+ }
380
+ });
381
+ }
382
+
383
+ // resourcePrincipalAuthentication()
384
+ //
385
+ // Authentication in an OCI resource-principal-enabled service. Credentials
386
+ // are supplied by the OCI runtime, so no API key material is needed.
387
+ //---------------------------------------------------------------------------
388
+ async function resourcePrincipalAuthentication(accessTokenConfig) {
389
+ const provider = common.ResourcePrincipalAuthenticationDetailsProvider.builder();
390
+
391
+ return await generateAccessToken(provider, accessTokenConfig.scope);
392
+ }
393
+
197
394
  //---------------------------------------------------------------------------
198
395
  // hookFn()
199
396
  // hookFn will get registerd to driver while loading plugins.
@@ -201,9 +398,17 @@ async function instancePrincipalAuthentication(accessTokenConfig) {
201
398
  function hookFn(options) {
202
399
  if (options.tokenAuthConfigOci) {
203
400
  options.accessToken = async function callbackFn(refresh, config) {
204
- return await getToken(config);
401
+ const result = await getTokenResult(config);
402
+ if (result.privateKey !== undefined) {
403
+ return { token: result.token, privateKey: result.privateKey };
404
+ }
405
+ return result.accessToken;
205
406
  };
206
407
  options.accessTokenConfig = options.tokenAuthConfigOci;
207
408
  }
208
409
  }
209
410
  oracledb.registerProcessConfigurationHook(hookFn);
411
+
412
+ module.exports = {
413
+ getTokenResult
414
+ };