Skip to content

Commit 5fecafc

Browse files
committed
fix: suggestions
1 parent 76aa52b commit 5fecafc

16 files changed

+113
-139
lines changed

src/cmap/auth/gssapi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class GSSAPI extends AuthProvider {
5858
saslContinue(negotiatedPayload, saslStartResponse.conversationId)
5959
);
6060

61-
const finalizePayload = await finalize(client, username, saslContinueResponse.payload);
61+
const finalizePayload = await finalize(client, username ?? '', saslContinueResponse.payload);
6262

6363
await externalCommand(connection, {
6464
saslContinue: 1,

src/cmap/auth/mongo_credentials.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export interface AuthMechanismProperties extends Document {
6161

6262
/** @public */
6363
export interface MongoCredentialsOptions {
64-
username: string;
64+
username?: string;
6565
password: string;
6666
source: string;
6767
db?: string;
@@ -75,7 +75,7 @@ export interface MongoCredentialsOptions {
7575
*/
7676
export class MongoCredentials {
7777
/** The username used for authentication */
78-
readonly username: string;
78+
readonly username?: string;
7979
/** The password used for authentication */
8080
readonly password: string;
8181
/** The database that the user should authenticate against */

src/cmap/auth/mongodb_oidc.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import type { Document } from 'bson';
2+
13
import { MongoInvalidArgumentError, MongoMissingCredentialsError } from '../../error';
24
import type { HandshakeDocument } from '../connect';
5+
import type { Connection } from '../connection';
36
import { AuthContext, AuthProvider } from './auth_provider';
47
import type { MongoCredentials } from './mongo_credentials';
58
import { AwsServiceWorkflow } from './mongodb_oidc/aws_service_workflow';
69
import { CallbackWorkflow } from './mongodb_oidc/callback_workflow';
7-
import type { Workflow } from './mongodb_oidc/workflow';
810

911
/** Error when credentials are missing. */
1012
const MISSING_CREDENTIALS_ERROR = 'AuthContext must provide credentials.';
@@ -60,6 +62,24 @@ export type OIDCRefreshFunction = (
6062

6163
type ProviderName = 'aws' | 'callback';
6264

65+
export interface Workflow {
66+
/**
67+
* All device workflows must implement this method in order to get the access
68+
* token and then call authenticate with it.
69+
*/
70+
execute(
71+
connection: Connection,
72+
credentials: MongoCredentials,
73+
reauthenticating: boolean,
74+
response?: Document
75+
): Promise<Document>;
76+
77+
/**
78+
* Get the document to add for speculative authentication.
79+
*/
80+
speculativeAuth(credentials: MongoCredentials): Promise<Document>;
81+
}
82+
6383
/** @internal */
6484
export const OIDC_WORKFLOWS: Map<ProviderName, Workflow> = new Map();
6585
OIDC_WORKFLOWS.set('callback', new CallbackWorkflow());

src/cmap/auth/mongodb_oidc/aws_service_workflow.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { readFile } from 'fs/promises';
1+
import * as fs from 'fs';
22

33
import { MongoAWSError } from '../../../error';
44
import { ServiceWorkflow } from './service_workflow';
@@ -24,6 +24,6 @@ export class AwsServiceWorkflow extends ServiceWorkflow {
2424
if (!tokenFile) {
2525
throw new MongoAWSError(TOKEN_MISSING_ERROR);
2626
}
27-
return readFile(tokenFile, 'utf8');
27+
return fs.promises.readFile(tokenFile, 'utf8');
2828
}
2929
}

src/cmap/auth/mongodb_oidc/cache.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Base class for OIDC caches.
3+
*/
4+
export abstract class Cache<T> {
5+
entries: Map<string, T>;
6+
7+
/**
8+
* Create a new cache.
9+
*/
10+
constructor() {
11+
this.entries = new Map<string, T>();
12+
}
13+
14+
/**
15+
* Clear the cache.
16+
*/
17+
clear() {
18+
this.entries.clear();
19+
}
20+
21+
/**
22+
* Create a cache key from the address and username.
23+
*/
24+
cacheKey(address: string, username: string, callbackHash: string): string {
25+
return JSON.stringify([address, username, callbackHash]);
26+
}
27+
}

src/cmap/auth/mongodb_oidc/callback_lock_cache.ts

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,15 @@ import type {
88
OIDCRefreshFunction,
99
OIDCRequestFunction
1010
} from '../mongodb_oidc';
11+
import { Cache } from './cache';
1112

1213
/** Error message for when request callback is missing. */
1314
const REQUEST_CALLBACK_REQUIRED_ERROR =
1415
'Auth mechanism property REQUEST_TOKEN_CALLBACK is required.';
1516
/* Counter for function "hashes".*/
1617
let FN_HASH_COUNTER = 0;
1718
/* No function present function */
18-
const NO_FUNCTION: OIDCRequestFunction = () => {
19-
return Promise.resolve({ accessToken: 'test' });
20-
};
19+
const NO_FUNCTION: OIDCRequestFunction = async () => ({ accessToken: 'test' });
2120
/* The map of function hashes */
2221
const FN_HASHES = new WeakMap<OIDCRequestFunction | OIDCRefreshFunction, number>();
2322
/* Put the no function hash in the map. */
@@ -35,23 +34,7 @@ interface CallbacksEntry {
3534
/**
3635
* A cache of request and refresh callbacks per server/user.
3736
*/
38-
export class CallbackLockCache {
39-
entries: Map<string, CallbacksEntry>;
40-
41-
/**
42-
* Instantiate the new cache.
43-
*/
44-
constructor() {
45-
this.entries = new Map<string, CallbacksEntry>();
46-
}
47-
48-
/**
49-
* Clear the cache.
50-
*/
51-
clear() {
52-
this.entries.clear();
53-
}
54-
37+
export class CallbackLockCache extends Cache<CallbacksEntry> {
5538
/**
5639
* Get the callbacks for the connection and credentials. If an entry does not
5740
* exist a new one will get set.
@@ -63,7 +46,7 @@ export class CallbackLockCache {
6346
throw new MongoInvalidArgumentError(REQUEST_CALLBACK_REQUIRED_ERROR);
6447
}
6548
const callbackHash = hashFunctions(requestCallback, refreshCallback);
66-
const key = cacheKey(connection, credentials, callbackHash);
49+
const key = this.cacheKey(connection.address, credentials.username ?? '', callbackHash);
6750
const entry = this.entries.get(key);
6851
if (entry) {
6952
return entry;
@@ -90,17 +73,6 @@ export class CallbackLockCache {
9073
}
9174
}
9275

93-
/**
94-
* Get a cache key based on connection and credentials.
95-
*/
96-
function cacheKey(
97-
connection: Connection,
98-
credentials: MongoCredentials,
99-
callbackHash: string
100-
): string {
101-
return JSON.stringify([connection.address, credentials.username, callbackHash]);
102-
}
103-
10476
/**
10577
* Ensure the callback is only executed one at a time.
10678
*/
@@ -117,15 +89,15 @@ function withLock(callback: OIDCRequestFunction | OIDCRefreshFunction) {
11789
* Get the hash string for the request and refresh functions.
11890
*/
11991
function hashFunctions(requestFn: OIDCRequestFunction, refreshFn?: OIDCRefreshFunction): string {
120-
let requestHash = FN_HASHES.get(requestFn || NO_FUNCTION);
121-
let refreshHash = FN_HASHES.get(refreshFn || NO_FUNCTION);
122-
if (!requestHash && requestFn) {
92+
let requestHash = FN_HASHES.get(requestFn);
93+
let refreshHash = FN_HASHES.get(refreshFn ?? NO_FUNCTION);
94+
if (requestHash == null) {
12395
// Create a new one for the function and put it in the map.
12496
FN_HASH_COUNTER++;
12597
requestHash = FN_HASH_COUNTER;
12698
FN_HASHES.set(requestFn, FN_HASH_COUNTER);
12799
}
128-
if (!refreshHash && refreshFn) {
100+
if (refreshHash == null && refreshFn) {
129101
// Create a new one for the function and put it in the map.
130102
FN_HASH_COUNTER++;
131103
refreshHash = FN_HASH_COUNTER;

src/cmap/auth/mongodb_oidc/callback_workflow.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import type {
99
IdPServerResponse,
1010
OIDCCallbackContext,
1111
OIDCRefreshFunction,
12-
OIDCRequestFunction
12+
OIDCRequestFunction,
13+
Workflow
1314
} from '../mongodb_oidc';
1415
import { AuthMechanism } from '../providers';
1516
import { CallbackLockCache } from './callback_lock_cache';
1617
import { TokenEntryCache } from './token_entry_cache';
17-
import type { Workflow } from './workflow';
1818

1919
/** The current version of OIDC implementation. */
2020
const OIDC_VERSION = 0;
@@ -70,7 +70,7 @@ export class CallbackWorkflow implements Workflow {
7070
credentials
7171
);
7272
// Look for an existing entry in the cache.
73-
const entry = this.cache.getEntry(connection.address, credentials.username, callbackHash);
73+
const entry = this.cache.getEntry(connection.address, credentials.username ?? '', callbackHash);
7474
let result;
7575
if (entry) {
7676
// Reauthentication cannot use a token from the cache since the server has
@@ -111,7 +111,7 @@ export class CallbackWorkflow implements Workflow {
111111
error instanceof MongoError &&
112112
error.code === MONGODB_ERROR_CODES.Reauthenticate
113113
) {
114-
this.cache.deleteEntry(connection.address, credentials.username || '', callbackHash);
114+
this.cache.deleteEntry(connection.address, credentials.username ?? '', callbackHash);
115115
result = await this.execute(connection, credentials, reauthenticating);
116116
} else {
117117
throw error;
@@ -203,7 +203,7 @@ export class CallbackWorkflow implements Workflow {
203203
refreshCallback?: OIDCRefreshFunction
204204
): Promise<IdPServerResponse> {
205205
// Get the token from the cache.
206-
const entry = this.cache.getEntry(connection.address, credentials.username, callbackHash);
206+
const entry = this.cache.getEntry(connection.address, credentials.username ?? '', callbackHash);
207207
let result;
208208
const context: OIDCCallbackContext = { timeoutSeconds: TIMEOUT_S, version: OIDC_VERSION };
209209
// Check if there's a token in the cache.
@@ -228,7 +228,7 @@ export class CallbackWorkflow implements Workflow {
228228
// Validate that the result returned by the callback is acceptable. If it is not
229229
// we must clear the token result from the cache.
230230
if (isCallbackResultInvalid(result)) {
231-
this.cache.deleteEntry(connection.address, credentials.username || '', callbackHash);
231+
this.cache.deleteEntry(connection.address, credentials.username ?? '', callbackHash);
232232
throw new MongoMissingCredentialsError(CALLBACK_RESULT_ERROR);
233233
}
234234
// Cleanup the cache.
@@ -250,7 +250,7 @@ export class CallbackWorkflow implements Workflow {
250250
* saslStart or saslContinue depending on the presence of a conversation id.
251251
*/
252252
function finishCommandDocument(token: string, conversationId?: number): Document {
253-
if (conversationId) {
253+
if (conversationId != null && typeof conversationId === 'number') {
254254
return {
255255
saslContinue: 1,
256256
conversationId: conversationId,
@@ -273,9 +273,9 @@ function finishCommandDocument(token: string, conversationId?: number): Document
273273
* function is invalid. This means the result is nullish, doesn't contain
274274
* the accessToken required field, and does not contain extra fields.
275275
*/
276-
function isCallbackResultInvalid(tokenResult: any): boolean {
277-
if (!tokenResult) return true;
278-
if (!tokenResult.accessToken) return true;
276+
function isCallbackResultInvalid(tokenResult: unknown): boolean {
277+
if (tokenResult == null || typeof tokenResult !== 'object') return true;
278+
if (!('accessToken' in tokenResult)) return true;
279279
return !Object.getOwnPropertyNames(tokenResult).every(prop => RESULT_PROPERTIES.includes(prop));
280280
}
281281

src/cmap/auth/mongodb_oidc/service_workflow.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { BSON, type Document } from 'bson';
33
import { ns } from '../../../utils';
44
import type { Connection } from '../../connection';
55
import type { MongoCredentials } from '../mongo_credentials';
6+
import type { Workflow } from '../mongodb_oidc';
67
import { AuthMechanism } from '../providers';
7-
import type { Workflow } from './workflow';
88

99
/**
1010
* Common behaviour for OIDC device workflows.

src/cmap/auth/mongodb_oidc/token_entry_cache.ts

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { IdPServerInfo, IdPServerResponse } from '../mongodb_oidc';
2+
import { Cache } from './cache';
23

3-
/* 5 minutes in milliseonds */
4+
/* 5 minutes in milliseconds */
45
const EXPIRATION_BUFFER_MS = 300000;
56
/* Default expiration is now for when no expiration provided */
67
const DEFAULT_EXPIRATION_SECS = 0;
@@ -32,13 +33,7 @@ export class TokenEntry {
3233
* Cache of OIDC token entries.
3334
* @internal
3435
*/
35-
export class TokenEntryCache {
36-
entries: Map<string, TokenEntry>;
37-
38-
constructor() {
39-
this.entries = new Map();
40-
}
41-
36+
export class TokenEntryCache extends Cache<TokenEntry> {
4237
/**
4338
* Set an entry in the token cache.
4439
*/
@@ -54,29 +49,22 @@ export class TokenEntryCache {
5449
serverInfo,
5550
expirationTime(tokenResult.expiresInSeconds)
5651
);
57-
this.entries.set(cacheKey(address, username, callbackHash), entry);
52+
this.entries.set(this.cacheKey(address, username, callbackHash), entry);
5853
return entry;
5954
}
6055

61-
/**
62-
* Clear the cache.
63-
*/
64-
clear(): void {
65-
this.entries.clear();
66-
}
67-
6856
/**
6957
* Delete an entry from the cache.
7058
*/
7159
deleteEntry(address: string, username: string, callbackHash: string): void {
72-
this.entries.delete(cacheKey(address, username, callbackHash));
60+
this.entries.delete(this.cacheKey(address, username, callbackHash));
7361
}
7462

7563
/**
7664
* Get an entry from the cache.
7765
*/
7866
getEntry(address: string, username: string, callbackHash: string): TokenEntry | undefined {
79-
return this.entries.get(cacheKey(address, username, callbackHash));
67+
return this.entries.get(this.cacheKey(address, username, callbackHash));
8068
}
8169

8270
/**
@@ -97,10 +85,3 @@ export class TokenEntryCache {
9785
function expirationTime(expiresInSeconds?: number): number {
9886
return Date.now() + (expiresInSeconds ?? DEFAULT_EXPIRATION_SECS) * 1000;
9987
}
100-
101-
/**
102-
* Create a cache key from the address and username.
103-
*/
104-
function cacheKey(address: string, username: string, callbackHash: string): string {
105-
return JSON.stringify([address, username, callbackHash]);
106-
}

src/cmap/auth/mongodb_oidc/workflow.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/cmap/auth/scram.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ function makeFirstMessage(
8686
credentials: MongoCredentials,
8787
nonce: Buffer
8888
) {
89-
const username = cleanUsername(credentials.username);
89+
const username = cleanUsername(credentials.username ?? '');
9090
const mechanism =
9191
cryptoMethod === 'sha1' ? AuthMechanism.MONGODB_SCRAM_SHA1 : AuthMechanism.MONGODB_SCRAM_SHA256;
9292

@@ -135,7 +135,7 @@ async function continueScramConversation(
135135
const nonce = authContext.nonce;
136136

137137
const db = credentials.source;
138-
const username = cleanUsername(credentials.username);
138+
const username = cleanUsername(credentials.username ?? '');
139139
const password = credentials.password;
140140

141141
let processedPassword;

0 commit comments

Comments
 (0)