mongodb 6.6.2-dev.20240529.sha.d3031a5 → 6.7.0-dev.20240530.sha.f56938f

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 (91) hide show
  1. package/lib/client-side-encryption/providers/azure.js +21 -6
  2. package/lib/client-side-encryption/providers/azure.js.map +1 -1
  3. package/lib/cmap/auth/mongo_credentials.js +24 -16
  4. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  5. package/lib/cmap/auth/mongodb_oidc/automated_callback_workflow.js +78 -0
  6. package/lib/cmap/auth/mongodb_oidc/automated_callback_workflow.js.map +1 -0
  7. package/lib/cmap/auth/mongodb_oidc/azure_machine_workflow.js +74 -0
  8. package/lib/cmap/auth/mongodb_oidc/azure_machine_workflow.js.map +1 -0
  9. package/lib/cmap/auth/mongodb_oidc/callback_workflow.js +74 -135
  10. package/lib/cmap/auth/mongodb_oidc/callback_workflow.js.map +1 -1
  11. package/lib/cmap/auth/mongodb_oidc/command_builders.js +45 -0
  12. package/lib/cmap/auth/mongodb_oidc/command_builders.js.map +1 -0
  13. package/lib/cmap/auth/mongodb_oidc/gcp_machine_workflow.js +46 -0
  14. package/lib/cmap/auth/mongodb_oidc/gcp_machine_workflow.js.map +1 -0
  15. package/lib/cmap/auth/mongodb_oidc/human_callback_workflow.js +122 -0
  16. package/lib/cmap/auth/mongodb_oidc/human_callback_workflow.js.map +1 -0
  17. package/lib/cmap/auth/mongodb_oidc/machine_workflow.js +107 -0
  18. package/lib/cmap/auth/mongodb_oidc/machine_workflow.js.map +1 -0
  19. package/lib/cmap/auth/mongodb_oidc/token_cache.js +52 -0
  20. package/lib/cmap/auth/mongodb_oidc/token_cache.js.map +1 -0
  21. package/lib/cmap/auth/mongodb_oidc/token_machine_workflow.js +34 -0
  22. package/lib/cmap/auth/mongodb_oidc/token_machine_workflow.js.map +1 -0
  23. package/lib/cmap/auth/mongodb_oidc.js +26 -24
  24. package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
  25. package/lib/cmap/auth/providers.js +0 -1
  26. package/lib/cmap/auth/providers.js.map +1 -1
  27. package/lib/cmap/connect.js +4 -4
  28. package/lib/cmap/connect.js.map +1 -1
  29. package/lib/cmap/connection.js.map +1 -1
  30. package/lib/cmap/connection_pool.js +1 -1
  31. package/lib/cmap/connection_pool.js.map +1 -1
  32. package/lib/connection_string.js +3 -0
  33. package/lib/connection_string.js.map +1 -1
  34. package/lib/error.js +57 -2
  35. package/lib/error.js.map +1 -1
  36. package/lib/index.js +5 -3
  37. package/lib/index.js.map +1 -1
  38. package/lib/mongo_client.js +1 -1
  39. package/lib/mongo_client.js.map +1 -1
  40. package/lib/mongo_client_auth_providers.js +34 -4
  41. package/lib/mongo_client_auth_providers.js.map +1 -1
  42. package/lib/utils.js +32 -2
  43. package/lib/utils.js.map +1 -1
  44. package/mongodb.d.ts +105 -25
  45. package/package.json +5 -3
  46. package/src/client-side-encryption/providers/azure.ts +21 -10
  47. package/src/cmap/auth/mongo_credentials.ts +41 -34
  48. package/src/cmap/auth/mongodb_oidc/automated_callback_workflow.ts +82 -0
  49. package/src/cmap/auth/mongodb_oidc/azure_machine_workflow.ts +85 -0
  50. package/src/cmap/auth/mongodb_oidc/callback_workflow.ts +96 -204
  51. package/src/cmap/auth/mongodb_oidc/command_builders.ts +54 -0
  52. package/src/cmap/auth/mongodb_oidc/gcp_machine_workflow.ts +53 -0
  53. package/src/cmap/auth/mongodb_oidc/human_callback_workflow.ts +142 -0
  54. package/src/cmap/auth/mongodb_oidc/machine_workflow.ts +137 -0
  55. package/src/cmap/auth/mongodb_oidc/token_cache.ts +62 -0
  56. package/src/cmap/auth/mongodb_oidc/token_machine_workflow.ts +34 -0
  57. package/src/cmap/auth/mongodb_oidc.ts +79 -49
  58. package/src/cmap/auth/providers.ts +0 -1
  59. package/src/cmap/connect.ts +14 -4
  60. package/src/cmap/connection.ts +1 -0
  61. package/src/cmap/connection_pool.ts +2 -1
  62. package/src/connection_string.ts +3 -0
  63. package/src/error.ts +58 -1
  64. package/src/index.ts +8 -4
  65. package/src/mongo_client.ts +4 -1
  66. package/src/mongo_client_auth_providers.ts +44 -6
  67. package/src/utils.ts +33 -0
  68. package/lib/client-side-encryption/providers/utils.js +0 -35
  69. package/lib/client-side-encryption/providers/utils.js.map +0 -1
  70. package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js +0 -30
  71. package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js.map +0 -1
  72. package/lib/cmap/auth/mongodb_oidc/azure_service_workflow.js +0 -73
  73. package/lib/cmap/auth/mongodb_oidc/azure_service_workflow.js.map +0 -1
  74. package/lib/cmap/auth/mongodb_oidc/azure_token_cache.js +0 -49
  75. package/lib/cmap/auth/mongodb_oidc/azure_token_cache.js.map +0 -1
  76. package/lib/cmap/auth/mongodb_oidc/cache.js +0 -55
  77. package/lib/cmap/auth/mongodb_oidc/cache.js.map +0 -1
  78. package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js +0 -90
  79. package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js.map +0 -1
  80. package/lib/cmap/auth/mongodb_oidc/service_workflow.js +0 -43
  81. package/lib/cmap/auth/mongodb_oidc/service_workflow.js.map +0 -1
  82. package/lib/cmap/auth/mongodb_oidc/token_entry_cache.js +0 -62
  83. package/lib/cmap/auth/mongodb_oidc/token_entry_cache.js.map +0 -1
  84. package/src/client-side-encryption/providers/utils.ts +0 -37
  85. package/src/cmap/auth/mongodb_oidc/aws_service_workflow.ts +0 -29
  86. package/src/cmap/auth/mongodb_oidc/azure_service_workflow.ts +0 -86
  87. package/src/cmap/auth/mongodb_oidc/azure_token_cache.ts +0 -51
  88. package/src/cmap/auth/mongodb_oidc/cache.ts +0 -63
  89. package/src/cmap/auth/mongodb_oidc/callback_lock_cache.ts +0 -115
  90. package/src/cmap/auth/mongodb_oidc/service_workflow.ts +0 -49
  91. package/src/cmap/auth/mongodb_oidc/token_entry_cache.ts +0 -77
@@ -1,9 +1,12 @@
1
1
  import { type Document } from '../../bson';
2
- import { MongoCryptAzureKMSRequestError, MongoCryptKMSRequestNetworkTimeoutError } from '../errors';
2
+ import { MongoNetworkTimeoutError } from '../../error';
3
+ import { get } from '../../utils';
4
+ import { MongoCryptAzureKMSRequestError } from '../errors';
3
5
  import { type KMSProviders } from './index';
4
- import { get } from './utils';
5
6
 
6
7
  const MINIMUM_TOKEN_REFRESH_IN_MILLISECONDS = 6000;
8
+ /** Base URL for getting Azure tokens. */
9
+ export const AZURE_BASE_URL = 'http://169.254.169.254/metadata/identity/oauth2/token?';
7
10
 
8
11
  /**
9
12
  * The access token that libmongocrypt expects for Azure kms.
@@ -113,6 +116,19 @@ export interface AzureKMSRequestOptions {
113
116
  url?: URL | string;
114
117
  }
115
118
 
119
+ /**
120
+ * @internal
121
+ * Get the Azure endpoint URL.
122
+ */
123
+ export function addAzureParams(url: URL, resource: string, username?: string): URL {
124
+ url.searchParams.append('api-version', '2018-02-01');
125
+ url.searchParams.append('resource', resource);
126
+ if (username) {
127
+ url.searchParams.append('client_id', username);
128
+ }
129
+ return url;
130
+ }
131
+
116
132
  /**
117
133
  * @internal
118
134
  *
@@ -123,13 +139,8 @@ export function prepareRequest(options: AzureKMSRequestOptions): {
123
139
  headers: Document;
124
140
  url: URL;
125
141
  } {
126
- const url = new URL(
127
- options.url?.toString() ?? 'http://169.254.169.254/metadata/identity/oauth2/token'
128
- );
129
-
130
- url.searchParams.append('api-version', '2018-02-01');
131
- url.searchParams.append('resource', 'https://vault.azure.net');
132
-
142
+ const url = new URL(options.url?.toString() ?? AZURE_BASE_URL);
143
+ addAzureParams(url, 'https://vault.azure.net');
133
144
  const headers = { ...options.headers, 'Content-Type': 'application/json', Metadata: true };
134
145
  return { headers, url };
135
146
  }
@@ -152,7 +163,7 @@ export async function fetchAzureKMSToken(
152
163
  const response = await get(url, { headers });
153
164
  return await parseResponse(response);
154
165
  } catch (error) {
155
- if (error instanceof MongoCryptKMSRequestNetworkTimeoutError) {
166
+ if (error instanceof MongoNetworkTimeoutError) {
156
167
  throw new MongoCryptAzureKMSRequestError(`[Azure KMS] ${error.message}`);
157
168
  }
158
169
  throw error;
@@ -3,12 +3,11 @@
3
3
  import type { Document } from '../../bson';
4
4
  import {
5
5
  MongoAPIError,
6
- MongoAzureError,
7
6
  MongoInvalidArgumentError,
8
7
  MongoMissingCredentialsError
9
8
  } from '../../error';
10
9
  import { GSSAPICanonicalizationValue } from './gssapi';
11
- import type { OIDCRefreshFunction, OIDCRequestFunction } from './mongodb_oidc';
10
+ import type { OIDCCallbackFunction } from './mongodb_oidc';
12
11
  import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism } from './providers';
13
12
 
14
13
  // https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst
@@ -32,12 +31,17 @@ function getDefaultAuthMechanism(hello: Document | null): AuthMechanism {
32
31
  return AuthMechanism.MONGODB_CR;
33
32
  }
34
33
 
35
- const ALLOWED_PROVIDER_NAMES: AuthMechanismProperties['PROVIDER_NAME'][] = ['aws', 'azure'];
34
+ const ALLOWED_ENVIRONMENT_NAMES: AuthMechanismProperties['ENVIRONMENT'][] = [
35
+ 'test',
36
+ 'azure',
37
+ 'gcp'
38
+ ];
36
39
  const ALLOWED_HOSTS_ERROR = 'Auth mechanism property ALLOWED_HOSTS must be an array of strings.';
37
40
 
38
41
  /** @internal */
39
42
  export const DEFAULT_ALLOWED_HOSTS = [
40
43
  '*.mongodb.net',
44
+ '*.mongodb-qa.net',
41
45
  '*.mongodb-dev.net',
42
46
  '*.mongodbgov.net',
43
47
  'localhost',
@@ -46,8 +50,8 @@ export const DEFAULT_ALLOWED_HOSTS = [
46
50
  ];
47
51
 
48
52
  /** Error for when the token audience is missing in the environment. */
49
- const TOKEN_AUDIENCE_MISSING_ERROR =
50
- 'TOKEN_AUDIENCE must be set in the auth mechanism properties when PROVIDER_NAME is azure.';
53
+ const TOKEN_RESOURCE_MISSING_ERROR =
54
+ 'TOKEN_RESOURCE must be set in the auth mechanism properties when ENVIRONMENT is azure or gcp.';
51
55
 
52
56
  /** @public */
53
57
  export interface AuthMechanismProperties extends Document {
@@ -56,16 +60,16 @@ export interface AuthMechanismProperties extends Document {
56
60
  SERVICE_REALM?: string;
57
61
  CANONICALIZE_HOST_NAME?: GSSAPICanonicalizationValue;
58
62
  AWS_SESSION_TOKEN?: string;
59
- /** @experimental */
60
- REQUEST_TOKEN_CALLBACK?: OIDCRequestFunction;
61
- /** @experimental */
62
- REFRESH_TOKEN_CALLBACK?: OIDCRefreshFunction;
63
- /** @experimental */
64
- PROVIDER_NAME?: 'aws' | 'azure';
65
- /** @experimental */
63
+ /** A user provided OIDC machine callback function. */
64
+ OIDC_CALLBACK?: OIDCCallbackFunction;
65
+ /** A user provided OIDC human interacted callback function. */
66
+ OIDC_HUMAN_CALLBACK?: OIDCCallbackFunction;
67
+ /** The OIDC environment. Note that 'test' is for internal use only. */
68
+ ENVIRONMENT?: 'test' | 'azure' | 'gcp';
69
+ /** Allowed hosts that OIDC auth can connect to. */
66
70
  ALLOWED_HOSTS?: string[];
67
- /** @experimental */
68
- TOKEN_AUDIENCE?: string;
71
+ /** The resource token for OIDC auth in Azure and GCP. */
72
+ TOKEN_RESOURCE?: string;
69
73
  }
70
74
 
71
75
  /** @public */
@@ -179,45 +183,48 @@ export class MongoCredentials {
179
183
  }
180
184
 
181
185
  if (this.mechanism === AuthMechanism.MONGODB_OIDC) {
182
- if (this.username && this.mechanismProperties.PROVIDER_NAME) {
186
+ if (
187
+ this.username &&
188
+ this.mechanismProperties.ENVIRONMENT &&
189
+ this.mechanismProperties.ENVIRONMENT !== 'azure'
190
+ ) {
183
191
  throw new MongoInvalidArgumentError(
184
- `username and PROVIDER_NAME may not be used together for mechanism '${this.mechanism}'.`
192
+ `username and ENVIRONMENT '${this.mechanismProperties.ENVIRONMENT}' may not be used together for mechanism '${this.mechanism}'.`
185
193
  );
186
194
  }
187
195
 
188
- if (
189
- this.mechanismProperties.PROVIDER_NAME === 'azure' &&
190
- !this.mechanismProperties.TOKEN_AUDIENCE
191
- ) {
192
- throw new MongoAzureError(TOKEN_AUDIENCE_MISSING_ERROR);
196
+ if (this.username && this.password) {
197
+ throw new MongoInvalidArgumentError(
198
+ `No password is allowed in ENVIRONMENT '${this.mechanismProperties.ENVIRONMENT}' for '${this.mechanism}'.`
199
+ );
193
200
  }
194
201
 
195
202
  if (
196
- this.mechanismProperties.PROVIDER_NAME &&
197
- !ALLOWED_PROVIDER_NAMES.includes(this.mechanismProperties.PROVIDER_NAME)
203
+ (this.mechanismProperties.ENVIRONMENT === 'azure' ||
204
+ this.mechanismProperties.ENVIRONMENT === 'gcp') &&
205
+ !this.mechanismProperties.TOKEN_RESOURCE
198
206
  ) {
199
- throw new MongoInvalidArgumentError(
200
- `Currently only a PROVIDER_NAME in ${ALLOWED_PROVIDER_NAMES.join(
201
- ','
202
- )} is supported for mechanism '${this.mechanism}'.`
203
- );
207
+ throw new MongoInvalidArgumentError(TOKEN_RESOURCE_MISSING_ERROR);
204
208
  }
205
209
 
206
210
  if (
207
- this.mechanismProperties.REFRESH_TOKEN_CALLBACK &&
208
- !this.mechanismProperties.REQUEST_TOKEN_CALLBACK
211
+ this.mechanismProperties.ENVIRONMENT &&
212
+ !ALLOWED_ENVIRONMENT_NAMES.includes(this.mechanismProperties.ENVIRONMENT)
209
213
  ) {
210
214
  throw new MongoInvalidArgumentError(
211
- `A REQUEST_TOKEN_CALLBACK must be provided when using a REFRESH_TOKEN_CALLBACK for mechanism '${this.mechanism}'`
215
+ `Currently only a ENVIRONMENT in ${ALLOWED_ENVIRONMENT_NAMES.join(
216
+ ','
217
+ )} is supported for mechanism '${this.mechanism}'.`
212
218
  );
213
219
  }
214
220
 
215
221
  if (
216
- !this.mechanismProperties.PROVIDER_NAME &&
217
- !this.mechanismProperties.REQUEST_TOKEN_CALLBACK
222
+ !this.mechanismProperties.ENVIRONMENT &&
223
+ !this.mechanismProperties.OIDC_CALLBACK &&
224
+ !this.mechanismProperties.OIDC_HUMAN_CALLBACK
218
225
  ) {
219
226
  throw new MongoInvalidArgumentError(
220
- `Either a PROVIDER_NAME or a REQUEST_TOKEN_CALLBACK must be specified for mechanism '${this.mechanism}'.`
227
+ `Either a ENVIRONMENT, OIDC_CALLBACK, or OIDC_HUMAN_CALLBACK must be specified for mechanism '${this.mechanism}'.`
221
228
  );
222
229
  }
223
230
 
@@ -0,0 +1,82 @@
1
+ import { MONGODB_ERROR_CODES, MongoError, MongoOIDCError } from '../../../error';
2
+ import { Timeout, TimeoutError } from '../../../timeout';
3
+ import { type Connection } from '../../connection';
4
+ import { type MongoCredentials } from '../mongo_credentials';
5
+ import {
6
+ OIDC_VERSION,
7
+ type OIDCCallbackFunction,
8
+ type OIDCCallbackParams,
9
+ type OIDCResponse
10
+ } from '../mongodb_oidc';
11
+ import { AUTOMATED_TIMEOUT_MS, CallbackWorkflow } from './callback_workflow';
12
+ import { type TokenCache } from './token_cache';
13
+
14
+ /**
15
+ * Class implementing behaviour for the non human callback workflow.
16
+ * @internal
17
+ */
18
+ export class AutomatedCallbackWorkflow extends CallbackWorkflow {
19
+ /**
20
+ * Instantiate the human callback workflow.
21
+ */
22
+ constructor(cache: TokenCache, callback: OIDCCallbackFunction) {
23
+ super(cache, callback);
24
+ }
25
+
26
+ /**
27
+ * Execute the OIDC callback workflow.
28
+ */
29
+ async execute(connection: Connection, credentials: MongoCredentials): Promise<void> {
30
+ // If there is a cached access token, try to authenticate with it. If
31
+ // authentication fails with an Authentication error (18),
32
+ // invalidate the access token, fetch a new access token, and try
33
+ // to authenticate again.
34
+ // If the server fails for any other reason, do not clear the cache.
35
+ if (this.cache.hasAccessToken) {
36
+ const token = this.cache.getAccessToken();
37
+ try {
38
+ return await this.finishAuthentication(connection, credentials, token);
39
+ } catch (error) {
40
+ if (
41
+ error instanceof MongoError &&
42
+ error.code === MONGODB_ERROR_CODES.AuthenticationFailed
43
+ ) {
44
+ this.cache.removeAccessToken();
45
+ return await this.execute(connection, credentials);
46
+ } else {
47
+ throw error;
48
+ }
49
+ }
50
+ }
51
+ const response = await this.fetchAccessToken(credentials);
52
+ this.cache.put(response);
53
+ connection.accessToken = response.accessToken;
54
+ await this.finishAuthentication(connection, credentials, response.accessToken);
55
+ }
56
+
57
+ /**
58
+ * Fetches the access token using the callback.
59
+ */
60
+ protected async fetchAccessToken(credentials: MongoCredentials): Promise<OIDCResponse> {
61
+ const controller = new AbortController();
62
+ const params: OIDCCallbackParams = {
63
+ timeoutContext: controller.signal,
64
+ version: OIDC_VERSION
65
+ };
66
+ if (credentials.username) {
67
+ params.username = credentials.username;
68
+ }
69
+ const timeout = Timeout.expires(AUTOMATED_TIMEOUT_MS);
70
+ try {
71
+ return await Promise.race([this.executeAndValidateCallback(params), timeout]);
72
+ } catch (error) {
73
+ if (TimeoutError.is(error)) {
74
+ controller.abort();
75
+ throw new MongoOIDCError(`OIDC callback timed out after ${AUTOMATED_TIMEOUT_MS}ms.`);
76
+ }
77
+ throw error;
78
+ } finally {
79
+ timeout.clear();
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,85 @@
1
+ import { addAzureParams, AZURE_BASE_URL } from '../../../client-side-encryption/providers/azure';
2
+ import { MongoAzureError } from '../../../error';
3
+ import { get } from '../../../utils';
4
+ import type { MongoCredentials } from '../mongo_credentials';
5
+ import { type AccessToken, MachineWorkflow } from './machine_workflow';
6
+ import { type TokenCache } from './token_cache';
7
+
8
+ /** Azure request headers. */
9
+ const AZURE_HEADERS = Object.freeze({ Metadata: 'true', Accept: 'application/json' });
10
+
11
+ /** Invalid endpoint result error. */
12
+ const ENDPOINT_RESULT_ERROR =
13
+ 'Azure endpoint did not return a value with only access_token and expires_in properties';
14
+
15
+ /** Error for when the token audience is missing in the environment. */
16
+ const TOKEN_RESOURCE_MISSING_ERROR =
17
+ 'TOKEN_RESOURCE must be set in the auth mechanism properties when ENVIRONMENT is azure.';
18
+
19
+ /**
20
+ * Device workflow implementation for Azure.
21
+ *
22
+ * @internal
23
+ */
24
+ export class AzureMachineWorkflow extends MachineWorkflow {
25
+ /**
26
+ * Instantiate the machine workflow.
27
+ */
28
+ constructor(cache: TokenCache) {
29
+ super(cache);
30
+ }
31
+
32
+ /**
33
+ * Get the token from the environment.
34
+ */
35
+ async getToken(credentials?: MongoCredentials): Promise<AccessToken> {
36
+ const tokenAudience = credentials?.mechanismProperties.TOKEN_RESOURCE;
37
+ const username = credentials?.username;
38
+ if (!tokenAudience) {
39
+ throw new MongoAzureError(TOKEN_RESOURCE_MISSING_ERROR);
40
+ }
41
+ const response = await getAzureTokenData(tokenAudience, username);
42
+ if (!isEndpointResultValid(response)) {
43
+ throw new MongoAzureError(ENDPOINT_RESULT_ERROR);
44
+ }
45
+ return response;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Hit the Azure endpoint to get the token data.
51
+ */
52
+ async function getAzureTokenData(tokenAudience: string, username?: string): Promise<AccessToken> {
53
+ const url = new URL(AZURE_BASE_URL);
54
+ addAzureParams(url, tokenAudience, username);
55
+ const response = await get(url, {
56
+ headers: AZURE_HEADERS
57
+ });
58
+ if (response.status !== 200) {
59
+ throw new MongoAzureError(
60
+ `Status code ${response.status} returned from the Azure endpoint. Response body: ${response.body}`
61
+ );
62
+ }
63
+ const result = JSON.parse(response.body);
64
+ return {
65
+ access_token: result.access_token,
66
+ expires_in: Number(result.expires_in)
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Determines if a result returned from the endpoint is valid.
72
+ * This means the result is not nullish, contains the access_token required field
73
+ * and the expires_in required field.
74
+ */
75
+ function isEndpointResultValid(
76
+ token: unknown
77
+ ): token is { access_token: unknown; expires_in: unknown } {
78
+ if (token == null || typeof token !== 'object') return false;
79
+ return (
80
+ 'access_token' in token &&
81
+ typeof token.access_token === 'string' &&
82
+ 'expires_in' in token &&
83
+ typeof token.expires_in === 'number'
84
+ );
85
+ }