mongodb 6.6.2 → 6.7.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 (97) 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/sdam/server_description.js +10 -4
  43. package/lib/sdam/server_description.js.map +1 -1
  44. package/lib/sessions.js +10 -0
  45. package/lib/sessions.js.map +1 -1
  46. package/lib/utils.js +32 -2
  47. package/lib/utils.js.map +1 -1
  48. package/mongodb.d.ts +115 -25
  49. package/package.json +5 -4
  50. package/src/client-side-encryption/providers/azure.ts +21 -10
  51. package/src/cmap/auth/mongo_credentials.ts +41 -34
  52. package/src/cmap/auth/mongodb_oidc/automated_callback_workflow.ts +82 -0
  53. package/src/cmap/auth/mongodb_oidc/azure_machine_workflow.ts +85 -0
  54. package/src/cmap/auth/mongodb_oidc/callback_workflow.ts +96 -204
  55. package/src/cmap/auth/mongodb_oidc/command_builders.ts +54 -0
  56. package/src/cmap/auth/mongodb_oidc/gcp_machine_workflow.ts +53 -0
  57. package/src/cmap/auth/mongodb_oidc/human_callback_workflow.ts +142 -0
  58. package/src/cmap/auth/mongodb_oidc/machine_workflow.ts +137 -0
  59. package/src/cmap/auth/mongodb_oidc/token_cache.ts +62 -0
  60. package/src/cmap/auth/mongodb_oidc/token_machine_workflow.ts +34 -0
  61. package/src/cmap/auth/mongodb_oidc.ts +79 -49
  62. package/src/cmap/auth/providers.ts +0 -1
  63. package/src/cmap/connect.ts +14 -4
  64. package/src/cmap/connection.ts +1 -0
  65. package/src/cmap/connection_pool.ts +2 -1
  66. package/src/connection_string.ts +3 -0
  67. package/src/error.ts +58 -1
  68. package/src/index.ts +8 -4
  69. package/src/mongo_client.ts +4 -1
  70. package/src/mongo_client_auth_providers.ts +44 -6
  71. package/src/sdam/server_description.ts +13 -4
  72. package/src/sessions.ts +10 -0
  73. package/src/utils.ts +33 -0
  74. package/lib/client-side-encryption/providers/utils.js +0 -35
  75. package/lib/client-side-encryption/providers/utils.js.map +0 -1
  76. package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js +0 -30
  77. package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js.map +0 -1
  78. package/lib/cmap/auth/mongodb_oidc/azure_service_workflow.js +0 -73
  79. package/lib/cmap/auth/mongodb_oidc/azure_service_workflow.js.map +0 -1
  80. package/lib/cmap/auth/mongodb_oidc/azure_token_cache.js +0 -49
  81. package/lib/cmap/auth/mongodb_oidc/azure_token_cache.js.map +0 -1
  82. package/lib/cmap/auth/mongodb_oidc/cache.js +0 -55
  83. package/lib/cmap/auth/mongodb_oidc/cache.js.map +0 -1
  84. package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js +0 -90
  85. package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js.map +0 -1
  86. package/lib/cmap/auth/mongodb_oidc/service_workflow.js +0 -43
  87. package/lib/cmap/auth/mongodb_oidc/service_workflow.js.map +0 -1
  88. package/lib/cmap/auth/mongodb_oidc/token_entry_cache.js +0 -62
  89. package/lib/cmap/auth/mongodb_oidc/token_entry_cache.js.map +0 -1
  90. package/src/client-side-encryption/providers/utils.ts +0 -37
  91. package/src/cmap/auth/mongodb_oidc/aws_service_workflow.ts +0 -29
  92. package/src/cmap/auth/mongodb_oidc/azure_service_workflow.ts +0 -86
  93. package/src/cmap/auth/mongodb_oidc/azure_token_cache.ts +0 -51
  94. package/src/cmap/auth/mongodb_oidc/cache.ts +0 -63
  95. package/src/cmap/auth/mongodb_oidc/callback_lock_cache.ts +0 -115
  96. package/src/cmap/auth/mongodb_oidc/service_workflow.ts +0 -49
  97. package/src/cmap/auth/mongodb_oidc/token_entry_cache.ts +0 -77
@@ -0,0 +1,137 @@
1
+ import { type Document } from 'bson';
2
+ import { setTimeout } from 'timers/promises';
3
+
4
+ import { ns } from '../../../utils';
5
+ import type { Connection } from '../../connection';
6
+ import type { MongoCredentials } from '../mongo_credentials';
7
+ import type { Workflow } from '../mongodb_oidc';
8
+ import { finishCommandDocument } from './command_builders';
9
+ import { type TokenCache } from './token_cache';
10
+
11
+ /** The time to throttle callback calls. */
12
+ const THROTTLE_MS = 100;
13
+
14
+ /**
15
+ * The access token format.
16
+ * @internal
17
+ */
18
+ export interface AccessToken {
19
+ access_token: string;
20
+ expires_in?: number;
21
+ }
22
+
23
+ /** @internal */
24
+ export type OIDCTokenFunction = (credentials: MongoCredentials) => Promise<AccessToken>;
25
+
26
+ /**
27
+ * Common behaviour for OIDC machine workflows.
28
+ * @internal
29
+ */
30
+ export abstract class MachineWorkflow implements Workflow {
31
+ cache: TokenCache;
32
+ callback: OIDCTokenFunction;
33
+ lastExecutionTime: number;
34
+
35
+ /**
36
+ * Instantiate the machine workflow.
37
+ */
38
+ constructor(cache: TokenCache) {
39
+ this.cache = cache;
40
+ this.callback = this.withLock(this.getToken.bind(this));
41
+ this.lastExecutionTime = Date.now() - THROTTLE_MS;
42
+ }
43
+
44
+ /**
45
+ * Execute the workflow. Gets the token from the subclass implementation.
46
+ */
47
+ async execute(connection: Connection, credentials: MongoCredentials): Promise<void> {
48
+ const token = await this.getTokenFromCacheOrEnv(connection, credentials);
49
+ const command = finishCommandDocument(token);
50
+ await connection.command(ns(credentials.source), command, undefined);
51
+ }
52
+
53
+ /**
54
+ * Reauthenticate on a machine workflow just grabs the token again since the server
55
+ * has said the current access token is invalid or expired.
56
+ */
57
+ async reauthenticate(connection: Connection, credentials: MongoCredentials): Promise<void> {
58
+ if (this.cache.hasAccessToken) {
59
+ // Reauthentication implies the token has expired.
60
+ if (connection.accessToken === this.cache.getAccessToken()) {
61
+ // If connection's access token is the same as the cache's, remove
62
+ // the token from the cache and connection.
63
+ this.cache.removeAccessToken();
64
+ delete connection.accessToken;
65
+ } else {
66
+ // If the connection's access token is different from the cache's, set
67
+ // the cache's token on the connection and do not remove from the
68
+ // cache.
69
+ connection.accessToken = this.cache.getAccessToken();
70
+ }
71
+ }
72
+ await this.execute(connection, credentials);
73
+ }
74
+
75
+ /**
76
+ * Get the document to add for speculative authentication.
77
+ */
78
+ async speculativeAuth(connection: Connection, credentials: MongoCredentials): Promise<Document> {
79
+ // The spec states only cached access tokens can use speculative auth.
80
+ if (!this.cache.hasAccessToken) {
81
+ return {};
82
+ }
83
+ const token = await this.getTokenFromCacheOrEnv(connection, credentials);
84
+ const document = finishCommandDocument(token);
85
+ document.db = credentials.source;
86
+ return { speculativeAuthenticate: document };
87
+ }
88
+
89
+ /**
90
+ * Get the token from the cache or environment.
91
+ */
92
+ private async getTokenFromCacheOrEnv(
93
+ connection: Connection,
94
+ credentials: MongoCredentials
95
+ ): Promise<string> {
96
+ if (this.cache.hasAccessToken) {
97
+ return this.cache.getAccessToken();
98
+ } else {
99
+ const token = await this.callback(credentials);
100
+ this.cache.put({ accessToken: token.access_token, expiresInSeconds: token.expires_in });
101
+ // Put the access token on the connection as well.
102
+ connection.accessToken = token.access_token;
103
+ return token.access_token;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Ensure the callback is only executed one at a time, and throttled to
109
+ * only once per 100ms.
110
+ */
111
+ private withLock(callback: OIDCTokenFunction): OIDCTokenFunction {
112
+ let lock: Promise<any> = Promise.resolve();
113
+ return async (credentials: MongoCredentials): Promise<AccessToken> => {
114
+ // We do this to ensure that we would never return the result of the
115
+ // previous lock, only the current callback's value would get returned.
116
+ await lock;
117
+ lock = lock
118
+ // eslint-disable-next-line github/no-then
119
+ .catch(() => null)
120
+ // eslint-disable-next-line github/no-then
121
+ .then(async () => {
122
+ const difference = Date.now() - this.lastExecutionTime;
123
+ if (difference <= THROTTLE_MS) {
124
+ await setTimeout(THROTTLE_MS - difference);
125
+ }
126
+ this.lastExecutionTime = Date.now();
127
+ return await callback(credentials);
128
+ });
129
+ return await lock;
130
+ };
131
+ }
132
+
133
+ /**
134
+ * Get the token from the environment or endpoint.
135
+ */
136
+ abstract getToken(credentials: MongoCredentials): Promise<AccessToken>;
137
+ }
@@ -0,0 +1,62 @@
1
+ import { MongoDriverError } from '../../../error';
2
+ import type { IdPInfo, OIDCResponse } from '../mongodb_oidc';
3
+
4
+ class MongoOIDCError extends MongoDriverError {}
5
+
6
+ /** @internal */
7
+ export class TokenCache {
8
+ private accessToken?: string;
9
+ private refreshToken?: string;
10
+ private idpInfo?: IdPInfo;
11
+ private expiresInSeconds?: number;
12
+
13
+ get hasAccessToken(): boolean {
14
+ return !!this.accessToken;
15
+ }
16
+
17
+ get hasRefreshToken(): boolean {
18
+ return !!this.refreshToken;
19
+ }
20
+
21
+ get hasIdpInfo(): boolean {
22
+ return !!this.idpInfo;
23
+ }
24
+
25
+ getAccessToken(): string {
26
+ if (!this.accessToken) {
27
+ throw new MongoOIDCError('Attempted to get an access token when none exists.');
28
+ }
29
+ return this.accessToken;
30
+ }
31
+
32
+ getRefreshToken(): string {
33
+ if (!this.refreshToken) {
34
+ throw new MongoOIDCError('Attempted to get a refresh token when none exists.');
35
+ }
36
+ return this.refreshToken;
37
+ }
38
+
39
+ getIdpInfo(): IdPInfo {
40
+ if (!this.idpInfo) {
41
+ throw new MongoOIDCError('Attempted to get IDP information when none exists.');
42
+ }
43
+ return this.idpInfo;
44
+ }
45
+
46
+ put(response: OIDCResponse, idpInfo?: IdPInfo) {
47
+ this.accessToken = response.accessToken;
48
+ this.refreshToken = response.refreshToken;
49
+ this.expiresInSeconds = response.expiresInSeconds;
50
+ if (idpInfo) {
51
+ this.idpInfo = idpInfo;
52
+ }
53
+ }
54
+
55
+ removeAccessToken() {
56
+ this.accessToken = undefined;
57
+ }
58
+
59
+ removeRefreshToken() {
60
+ this.refreshToken = undefined;
61
+ }
62
+ }
@@ -0,0 +1,34 @@
1
+ import * as fs from 'fs';
2
+
3
+ import { MongoAWSError } from '../../../error';
4
+ import { type AccessToken, MachineWorkflow } from './machine_workflow';
5
+ import { type TokenCache } from './token_cache';
6
+
7
+ /** Error for when the token is missing in the environment. */
8
+ const TOKEN_MISSING_ERROR = 'OIDC_TOKEN_FILE must be set in the environment.';
9
+
10
+ /**
11
+ * Device workflow implementation for AWS.
12
+ *
13
+ * @internal
14
+ */
15
+ export class TokenMachineWorkflow extends MachineWorkflow {
16
+ /**
17
+ * Instantiate the machine workflow.
18
+ */
19
+ constructor(cache: TokenCache) {
20
+ super(cache);
21
+ }
22
+
23
+ /**
24
+ * Get the token from the environment.
25
+ */
26
+ async getToken(): Promise<AccessToken> {
27
+ const tokenFile = process.env.OIDC_TOKEN_FILE;
28
+ if (!tokenFile) {
29
+ throw new MongoAWSError(TOKEN_MISSING_ERROR);
30
+ }
31
+ const token = await fs.promises.readFile(tokenFile, 'utf8');
32
+ return { access_token: token };
33
+ }
34
+ }
@@ -5,64 +5,93 @@ import type { HandshakeDocument } from '../connect';
5
5
  import type { Connection } from '../connection';
6
6
  import { type AuthContext, AuthProvider } from './auth_provider';
7
7
  import type { MongoCredentials } from './mongo_credentials';
8
- import { AwsServiceWorkflow } from './mongodb_oidc/aws_service_workflow';
9
- import { AzureServiceWorkflow } from './mongodb_oidc/azure_service_workflow';
10
- import { CallbackWorkflow } from './mongodb_oidc/callback_workflow';
8
+ import { AzureMachineWorkflow } from './mongodb_oidc/azure_machine_workflow';
9
+ import { GCPMachineWorkflow } from './mongodb_oidc/gcp_machine_workflow';
10
+ import { TokenCache } from './mongodb_oidc/token_cache';
11
+ import { TokenMachineWorkflow } from './mongodb_oidc/token_machine_workflow';
11
12
 
12
13
  /** Error when credentials are missing. */
13
14
  const MISSING_CREDENTIALS_ERROR = 'AuthContext must provide credentials.';
14
15
 
15
16
  /**
17
+ * The information returned by the server on the IDP server.
16
18
  * @public
17
- * @experimental
18
19
  */
19
- export interface IdPServerInfo {
20
+ export interface IdPInfo {
21
+ /**
22
+ * A URL which describes the Authentication Server. This identifier should
23
+ * be the iss of provided access tokens, and be viable for RFC8414 metadata
24
+ * discovery and RFC9207 identification.
25
+ */
20
26
  issuer: string;
27
+ /** A unique client ID for this OIDC client. */
21
28
  clientId: string;
29
+ /** A list of additional scopes to request from IdP. */
22
30
  requestScopes?: string[];
23
31
  }
24
32
 
25
33
  /**
34
+ * The response from the IdP server with the access token and
35
+ * optional expiration time and refresh token.
26
36
  * @public
27
- * @experimental
28
37
  */
29
38
  export interface IdPServerResponse {
39
+ /** The OIDC access token. */
30
40
  accessToken: string;
41
+ /** The time when the access token expires. For future use. */
31
42
  expiresInSeconds?: number;
43
+ /** The refresh token, if applicable, to be used by the callback to request a new token from the issuer. */
32
44
  refreshToken?: string;
33
45
  }
34
46
 
35
47
  /**
48
+ * The response required to be returned from the machine or
49
+ * human callback workflows' callback.
36
50
  * @public
37
- * @experimental
38
51
  */
39
- export interface OIDCCallbackContext {
52
+ export interface OIDCResponse {
53
+ /** The OIDC access token. */
54
+ accessToken: string;
55
+ /** The time when the access token expires. For future use. */
56
+ expiresInSeconds?: number;
57
+ /** The refresh token, if applicable, to be used by the callback to request a new token from the issuer. */
40
58
  refreshToken?: string;
41
- timeoutSeconds?: number;
42
- timeoutContext?: AbortSignal;
43
- version: number;
44
59
  }
45
60
 
46
61
  /**
62
+ * The parameters that the driver provides to the user supplied
63
+ * human or machine callback.
64
+ *
65
+ * The version number is used to communicate callback API changes that are not breaking but that
66
+ * users may want to know about and review their implementation. Users may wish to check the version
67
+ * number and throw an error if their expected version number and the one provided do not match.
47
68
  * @public
48
- * @experimental
49
69
  */
50
- export type OIDCRequestFunction = (
51
- info: IdPServerInfo,
52
- context: OIDCCallbackContext
53
- ) => Promise<IdPServerResponse>;
70
+ export interface OIDCCallbackParams {
71
+ /** Optional username. */
72
+ username?: string;
73
+ /** The context in which to timeout the OIDC callback. */
74
+ timeoutContext: AbortSignal;
75
+ /** The current OIDC API version. */
76
+ version: 1;
77
+ /** The IdP information returned from the server. */
78
+ idpInfo?: IdPInfo;
79
+ /** The refresh token, if applicable, to be used by the callback to request a new token from the issuer. */
80
+ refreshToken?: string;
81
+ }
54
82
 
55
83
  /**
84
+ * The signature of the human or machine callback functions.
56
85
  * @public
57
- * @experimental
58
86
  */
59
- export type OIDCRefreshFunction = (
60
- info: IdPServerInfo,
61
- context: OIDCCallbackContext
62
- ) => Promise<IdPServerResponse>;
87
+ export type OIDCCallbackFunction = (params: OIDCCallbackParams) => Promise<OIDCResponse>;
88
+
89
+ /** The current version of OIDC implementation. */
90
+ export const OIDC_VERSION = 1;
63
91
 
64
- type ProviderName = 'aws' | 'azure' | 'callback';
92
+ type EnvironmentName = 'test' | 'azure' | 'gcp' | undefined;
65
93
 
94
+ /** @internal */
66
95
  export interface Workflow {
67
96
  /**
68
97
  * All device workflows must implement this method in order to get the access
@@ -71,32 +100,41 @@ export interface Workflow {
71
100
  execute(
72
101
  connection: Connection,
73
102
  credentials: MongoCredentials,
74
- reauthenticating: boolean,
75
103
  response?: Document
76
- ): Promise<Document>;
104
+ ): Promise<void>;
105
+
106
+ /**
107
+ * Each workflow should specify the correct custom behaviour for reauthentication.
108
+ */
109
+ reauthenticate(connection: Connection, credentials: MongoCredentials): Promise<void>;
77
110
 
78
111
  /**
79
112
  * Get the document to add for speculative authentication.
80
113
  */
81
- speculativeAuth(credentials: MongoCredentials): Promise<Document>;
114
+ speculativeAuth(connection: Connection, credentials: MongoCredentials): Promise<Document>;
82
115
  }
83
116
 
84
117
  /** @internal */
85
- export const OIDC_WORKFLOWS: Map<ProviderName, Workflow> = new Map();
86
- OIDC_WORKFLOWS.set('callback', new CallbackWorkflow());
87
- OIDC_WORKFLOWS.set('aws', new AwsServiceWorkflow());
88
- OIDC_WORKFLOWS.set('azure', new AzureServiceWorkflow());
118
+ export const OIDC_WORKFLOWS: Map<EnvironmentName, () => Workflow> = new Map();
119
+ OIDC_WORKFLOWS.set('test', () => new TokenMachineWorkflow(new TokenCache()));
120
+ OIDC_WORKFLOWS.set('azure', () => new AzureMachineWorkflow(new TokenCache()));
121
+ OIDC_WORKFLOWS.set('gcp', () => new GCPMachineWorkflow(new TokenCache()));
89
122
 
90
123
  /**
91
124
  * OIDC auth provider.
92
- * @experimental
93
125
  */
94
126
  export class MongoDBOIDC extends AuthProvider {
127
+ workflow: Workflow;
128
+
95
129
  /**
96
130
  * Instantiate the auth provider.
97
131
  */
98
- constructor() {
132
+ constructor(workflow?: Workflow) {
99
133
  super();
134
+ if (!workflow) {
135
+ throw new MongoInvalidArgumentError('No workflow provided to the OIDC auth provider.');
136
+ }
137
+ this.workflow = workflow;
100
138
  }
101
139
 
102
140
  /**
@@ -104,9 +142,15 @@ export class MongoDBOIDC extends AuthProvider {
104
142
  */
105
143
  override async auth(authContext: AuthContext): Promise<void> {
106
144
  const { connection, reauthenticating, response } = authContext;
145
+ if (response?.speculativeAuthenticate?.done) {
146
+ return;
147
+ }
107
148
  const credentials = getCredentials(authContext);
108
- const workflow = getWorkflow(credentials);
109
- await workflow.execute(connection, credentials, reauthenticating, response);
149
+ if (reauthenticating) {
150
+ await this.workflow.reauthenticate(connection, credentials);
151
+ } else {
152
+ await this.workflow.execute(connection, credentials, response);
153
+ }
110
154
  }
111
155
 
112
156
  /**
@@ -116,9 +160,9 @@ export class MongoDBOIDC extends AuthProvider {
116
160
  handshakeDoc: HandshakeDocument,
117
161
  authContext: AuthContext
118
162
  ): Promise<HandshakeDocument> {
163
+ const { connection } = authContext;
119
164
  const credentials = getCredentials(authContext);
120
- const workflow = getWorkflow(credentials);
121
- const result = await workflow.speculativeAuth(credentials);
165
+ const result = await this.workflow.speculativeAuth(connection, credentials);
122
166
  return { ...handshakeDoc, ...result };
123
167
  }
124
168
  }
@@ -133,17 +177,3 @@ function getCredentials(authContext: AuthContext): MongoCredentials {
133
177
  }
134
178
  return credentials;
135
179
  }
136
-
137
- /**
138
- * Gets either a device workflow or callback workflow.
139
- */
140
- function getWorkflow(credentials: MongoCredentials): Workflow {
141
- const providerName = credentials.mechanismProperties.PROVIDER_NAME;
142
- const workflow = OIDC_WORKFLOWS.get(providerName || 'callback');
143
- if (!workflow) {
144
- throw new MongoInvalidArgumentError(
145
- `Could not load workflow for provider ${credentials.mechanismProperties.PROVIDER_NAME}`
146
- );
147
- }
148
- return workflow;
149
- }
@@ -8,7 +8,6 @@ export const AuthMechanism = Object.freeze({
8
8
  MONGODB_SCRAM_SHA1: 'SCRAM-SHA-1',
9
9
  MONGODB_SCRAM_SHA256: 'SCRAM-SHA-256',
10
10
  MONGODB_X509: 'MONGODB-X509',
11
- /** @experimental */
12
11
  MONGODB_OIDC: 'MONGODB-OIDC'
13
12
  } as const);
14
13
 
@@ -91,7 +91,10 @@ export async function performInitialHandshake(
91
91
  if (credentials) {
92
92
  if (
93
93
  !(credentials.mechanism === AuthMechanism.MONGODB_DEFAULT) &&
94
- !options.authProviders.getOrCreateProvider(credentials.mechanism)
94
+ !options.authProviders.getOrCreateProvider(
95
+ credentials.mechanism,
96
+ credentials.mechanismProperties
97
+ )
95
98
  ) {
96
99
  throw new MongoInvalidArgumentError(`AuthMechanism '${credentials.mechanism}' not supported`);
97
100
  }
@@ -146,7 +149,10 @@ export async function performInitialHandshake(
146
149
  authContext.response = response;
147
150
 
148
151
  const resolvedCredentials = credentials.resolveAuthMechanism(response);
149
- const provider = options.authProviders.getOrCreateProvider(resolvedCredentials.mechanism);
152
+ const provider = options.authProviders.getOrCreateProvider(
153
+ resolvedCredentials.mechanism,
154
+ resolvedCredentials.mechanismProperties
155
+ );
150
156
  if (!provider) {
151
157
  throw new MongoInvalidArgumentError(
152
158
  `No AuthProvider for ${resolvedCredentials.mechanism} defined.`
@@ -218,7 +224,8 @@ export async function prepareHandshakeDocument(
218
224
  handshakeDoc.saslSupportedMechs = `${credentials.source}.${credentials.username}`;
219
225
 
220
226
  const provider = authContext.options.authProviders.getOrCreateProvider(
221
- AuthMechanism.MONGODB_SCRAM_SHA256
227
+ AuthMechanism.MONGODB_SCRAM_SHA256,
228
+ credentials.mechanismProperties
222
229
  );
223
230
  if (!provider) {
224
231
  // This auth mechanism is always present.
@@ -228,7 +235,10 @@ export async function prepareHandshakeDocument(
228
235
  }
229
236
  return await provider.prepare(handshakeDoc, authContext);
230
237
  }
231
- const provider = authContext.options.authProviders.getOrCreateProvider(credentials.mechanism);
238
+ const provider = authContext.options.authProviders.getOrCreateProvider(
239
+ credentials.mechanism,
240
+ credentials.mechanismProperties
241
+ );
232
242
  if (!provider) {
233
243
  throw new MongoInvalidArgumentError(`No AuthProvider for ${credentials.mechanism} defined.`);
234
244
  }
@@ -174,6 +174,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
174
174
  public authContext?: AuthContext;
175
175
  public delayedTimeoutId: NodeJS.Timeout | null = null;
176
176
  public generation: number;
177
+ public accessToken?: string;
177
178
  public readonly description: Readonly<StreamDescription>;
178
179
  /**
179
180
  * Represents if the connection has been established:
@@ -551,7 +551,8 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
551
551
 
552
552
  const resolvedCredentials = credentials.resolveAuthMechanism(connection.hello);
553
553
  const provider = this[kServer].topology.client.s.authProviders.getOrCreateProvider(
554
- resolvedCredentials.mechanism
554
+ resolvedCredentials.mechanism,
555
+ resolvedCredentials.mechanismProperties
555
556
  );
556
557
 
557
558
  if (!provider) {
@@ -698,6 +698,9 @@ export const OPTIONS = {
698
698
  });
699
699
  }
700
700
  },
701
+ // Note that if the authMechanismProperties contain a TOKEN_RESOURCE that has a
702
+ // comma in it, it MUST be supplied as a MongoClient option instead of in the
703
+ // connection string.
701
704
  authMechanismProperties: {
702
705
  target: 'credentials',
703
706
  transform({ options, values }): MongoCredentials {
package/src/error.ts CHANGED
@@ -36,6 +36,7 @@ export const NODE_IS_RECOVERING_ERROR_MESSAGE = new RegExp('node is recovering',
36
36
  export const MONGODB_ERROR_CODES = Object.freeze({
37
37
  HostUnreachable: 6,
38
38
  HostNotFound: 7,
39
+ AuthenticationFailed: 18,
39
40
  NetworkTimeout: 89,
40
41
  ShutdownInProgress: 91,
41
42
  PrimarySteppedDown: 189,
@@ -529,6 +530,34 @@ export class MongoAWSError extends MongoRuntimeError {
529
530
  }
530
531
  }
531
532
 
533
+ /**
534
+ * A error generated when the user attempts to authenticate
535
+ * via OIDC callbacks, but fails.
536
+ *
537
+ * @public
538
+ * @category Error
539
+ */
540
+ export class MongoOIDCError extends MongoRuntimeError {
541
+ /**
542
+ * **Do not use this constructor!**
543
+ *
544
+ * Meant for internal use only.
545
+ *
546
+ * @remarks
547
+ * This class is only meant to be constructed within the driver. This constructor is
548
+ * not subject to semantic versioning compatibility guarantees and may change at any time.
549
+ *
550
+ * @public
551
+ **/
552
+ constructor(message: string) {
553
+ super(message);
554
+ }
555
+
556
+ override get name(): string {
557
+ return 'MongoOIDCError';
558
+ }
559
+ }
560
+
532
561
  /**
533
562
  * A error generated when the user attempts to authenticate
534
563
  * via Azure, but fails.
@@ -536,7 +565,7 @@ export class MongoAWSError extends MongoRuntimeError {
536
565
  * @public
537
566
  * @category Error
538
567
  */
539
- export class MongoAzureError extends MongoRuntimeError {
568
+ export class MongoAzureError extends MongoOIDCError {
540
569
  /**
541
570
  * **Do not use this constructor!**
542
571
  *
@@ -557,6 +586,34 @@ export class MongoAzureError extends MongoRuntimeError {
557
586
  }
558
587
  }
559
588
 
589
+ /**
590
+ * A error generated when the user attempts to authenticate
591
+ * via GCP, but fails.
592
+ *
593
+ * @public
594
+ * @category Error
595
+ */
596
+ export class MongoGCPError extends MongoOIDCError {
597
+ /**
598
+ * **Do not use this constructor!**
599
+ *
600
+ * Meant for internal use only.
601
+ *
602
+ * @remarks
603
+ * This class is only meant to be constructed within the driver. This constructor is
604
+ * not subject to semantic versioning compatibility guarantees and may change at any time.
605
+ *
606
+ * @public
607
+ **/
608
+ constructor(message: string) {
609
+ super(message);
610
+ }
611
+
612
+ override get name(): string {
613
+ return 'MongoGCPError';
614
+ }
615
+ }
616
+
560
617
  /**
561
618
  * An error generated when a ChangeStream operation fails to execute.
562
619
  *
package/src/index.ts CHANGED
@@ -52,6 +52,7 @@ export {
52
52
  MongoDriverError,
53
53
  MongoError,
54
54
  MongoExpiredSessionError,
55
+ MongoGCPError,
55
56
  MongoGridFSChunkError,
56
57
  MongoGridFSStreamError,
57
58
  MongoInvalidArgumentError,
@@ -61,6 +62,7 @@ export {
61
62
  MongoNetworkError,
62
63
  MongoNetworkTimeoutError,
63
64
  MongoNotConnectedError,
65
+ MongoOIDCError,
64
66
  MongoParseError,
65
67
  MongoRuntimeError,
66
68
  MongoServerClosedError,
@@ -250,12 +252,14 @@ export type {
250
252
  MongoCredentialsOptions
251
253
  } from './cmap/auth/mongo_credentials';
252
254
  export type {
253
- IdPServerInfo,
255
+ IdPInfo,
254
256
  IdPServerResponse,
255
- OIDCCallbackContext,
256
- OIDCRefreshFunction,
257
- OIDCRequestFunction
257
+ OIDCCallbackFunction,
258
+ OIDCCallbackParams,
259
+ OIDCResponse
258
260
  } from './cmap/auth/mongodb_oidc';
261
+ export type { Workflow } from './cmap/auth/mongodb_oidc';
262
+ export type { TokenCache } from './cmap/auth/mongodb_oidc/token_cache';
259
263
  export type {
260
264
  MessageHeader,
261
265
  OpCompressedRequest,
@@ -10,6 +10,7 @@ import {
10
10
  DEFAULT_ALLOWED_HOSTS,
11
11
  type MongoCredentials
12
12
  } from './cmap/auth/mongo_credentials';
13
+ import { type TokenCache } from './cmap/auth/mongodb_oidc/token_cache';
13
14
  import { AuthMechanism } from './cmap/auth/providers';
14
15
  import type { LEGAL_TCP_SOCKET_OPTIONS, LEGAL_TLS_SOCKET_OPTIONS } from './cmap/connect';
15
16
  import type { Connection } from './cmap/connection';
@@ -524,7 +525,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
524
525
  if (options.credentials?.mechanism === AuthMechanism.MONGODB_OIDC) {
525
526
  const allowedHosts =
526
527
  options.credentials?.mechanismProperties?.ALLOWED_HOSTS || DEFAULT_ALLOWED_HOSTS;
527
- const isServiceAuth = !!options.credentials?.mechanismProperties?.PROVIDER_NAME;
528
+ const isServiceAuth = !!options.credentials?.mechanismProperties?.ENVIRONMENT;
528
529
  if (!isServiceAuth) {
529
530
  for (const host of options.hosts) {
530
531
  if (!hostMatchesWildcards(host.toHostPort().host, allowedHosts)) {
@@ -828,6 +829,8 @@ export interface MongoOptions
828
829
  extendedMetadata: Promise<Document>;
829
830
  /** @internal */
830
831
  autoEncrypter?: AutoEncrypter;
832
+ /** @internal */
833
+ tokenCache?: TokenCache;
831
834
  proxyHost?: string;
832
835
  proxyPort?: number;
833
836
  proxyUsername?: string;