Skip to content

Commit ea3d2bc

Browse files
committed
feat: update for human and non human
1 parent 24dc578 commit ea3d2bc

File tree

5 files changed

+60
-38
lines changed

5 files changed

+60
-38
lines changed

src/cmap/auth/mongo_credentials.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
MongoMissingCredentialsError
99
} from '../../error';
1010
import { GSSAPICanonicalizationValue } from './gssapi';
11-
import type { OIDCRequestFunction } from './mongodb_oidc';
11+
import type { OIDCCallbackFunction } from './mongodb_oidc';
1212
import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism } from './providers';
1313

1414
// https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst
@@ -58,7 +58,9 @@ export interface AuthMechanismProperties extends Document {
5858
CANONICALIZE_HOST_NAME?: GSSAPICanonicalizationValue;
5959
AWS_SESSION_TOKEN?: string;
6060
/** @experimental */
61-
OIDC_TOKEN_CALLBACK?: OIDCRequestFunction;
61+
OIDC_CALLBACK?: OIDCCallbackFunction;
62+
/** @experimental */
63+
OIDC_HUMAN_CALLBACK?: OIDCCallbackFunction;
6264
/** @experimental */
6365
PROVIDER_NAME?: 'aws' | 'azure';
6466
/** @experimental */
@@ -206,10 +208,11 @@ export class MongoCredentials {
206208

207209
if (
208210
!this.mechanismProperties.PROVIDER_NAME &&
209-
!this.mechanismProperties.OIDC_TOKEN_CALLBACK
211+
!this.mechanismProperties.OIDC_CALLBACK &&
212+
!this.mechanismProperties.OIDC_HUMAN_CALLBACK
210213
) {
211214
throw new MongoInvalidArgumentError(
212-
`Either a PROVIDER_NAME or a OIDC_TOKEN_CALLBACK must be specified for mechanism '${this.mechanism}'.`
215+
`Either a PROVIDER_NAME, OIDC_CALLBACK, or OIDC_HUMAN_CALLBACK must be specified for mechanism '${this.mechanism}'.`
213216
);
214217
}
215218

src/cmap/auth/mongodb_oidc.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const MISSING_CREDENTIALS_ERROR = 'AuthContext must provide credentials.';
1616
* @public
1717
* @experimental
1818
*/
19-
export interface IdPServerInfo {
19+
export interface IdPInfo {
2020
issuer: string;
2121
clientId: string;
2222
requestScopes?: string[];
@@ -36,25 +36,28 @@ export interface IdPServerResponse {
3636
* @public
3737
* @experimental
3838
*/
39-
export interface OIDCToken {
39+
export interface OIDCResponse {
4040
accessToken: string;
4141
expiresInSeconds?: number;
42+
refreshToken?: string;
4243
}
4344

4445
/**
4546
* @public
4647
* @experimental
4748
*/
48-
export interface OIDCTokenParams {
49+
export interface OIDCCallbackParams {
4950
timeoutContext: AbortSignal;
5051
version: number;
52+
idpInfo?: IdPInfo;
53+
refreshToken?: string;
5154
}
5255

5356
/**
5457
* @public
5558
* @experimental
5659
*/
57-
export type OIDCRequestFunction = (params: OIDCTokenParams) => Promise<OIDCToken>;
60+
export type OIDCCallbackFunction = (params: OIDCCallbackParams) => Promise<OIDCResponse>;
5861

5962
type ProviderName = 'aws' | 'azure' | 'callback';
6063

src/cmap/auth/mongodb_oidc/azure_machine_workflow.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import { AzureTokenCache } from './azure_token_cache';
55
import { MachineWorkflow } from './machine_workflow';
66

77
/** Base URL for getting Azure tokens. */
8-
const AZURE_BASE_URL =
9-
'http://169.254.169.254/metadata/identity/oauth2/token?';
8+
const AZURE_BASE_URL = 'http://169.254.169.254/metadata/identity/oauth2/token?';
109

1110
/** Azure request headers. */
1211
const AZURE_HEADERS = Object.freeze({ Metadata: 'true', Accept: 'application/json' });

src/cmap/auth/mongodb_oidc/callback_workflow.ts

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@ import { BSON, type Document } from 'bson';
33
import { MongoMissingCredentialsError } from '../../../error';
44
import { ns } from '../../../utils';
55
import type { Connection } from '../../connection';
6-
import type { MongoCredentials } from '../mongo_credentials';
6+
import type { AuthMechanismProperties, MongoCredentials } from '../mongo_credentials';
77
import type {
8-
IdPServerInfo,
8+
IdPInfo,
99
IdPServerResponse,
10-
OIDCRequestFunction,
11-
OIDCTokenParams,
10+
OIDCCallbackFunction,
11+
OIDCCallbackParams,
1212
Workflow
1313
} from '../mongodb_oidc';
1414
import { finishCommandDocument, startCommandDocument } from './command_builders';
1515

1616
/** The current version of OIDC implementation. */
1717
const OIDC_VERSION = 1;
1818

19-
/** 5 minutes in seconds */
20-
const TIMEOUT_S = 300;
19+
/** 5 minutes in milliseconds */
20+
const HUMAN_TIMEOUT_MS = 300000;
2121

2222
/** Properties allowed on results of callbacks. */
2323
const RESULT_PROPERTIES = ['accessToken', 'expiresInSeconds', 'refreshToken'];
@@ -26,7 +26,15 @@ const RESULT_PROPERTIES = ['accessToken', 'expiresInSeconds', 'refreshToken'];
2626
const CALLBACK_RESULT_ERROR =
2727
'User provided OIDC callbacks must return a valid object with an accessToken.';
2828

29-
const NO_REQUEST_CALLBACK = 'No OIDC_TOKEN_CALLBACK provided for callback workflow.';
29+
const NO_CALLBACK = 'No OIDC_CALLBACK or OIDC_HUMAN_CALLBACK provided for callback workflow.';
30+
31+
/**
32+
* The OIDC callback information.
33+
*/
34+
interface OIDCCallbackInfo {
35+
callback: OIDCCallbackFunction;
36+
isHumanWorkflow: boolean;
37+
}
3038

3139
/**
3240
* OIDC implementation of a callback based workflow.
@@ -67,19 +75,11 @@ export class CallbackWorkflow implements Workflow {
6775
credentials: MongoCredentials,
6876
response?: Document
6977
): Promise<Document> {
70-
const requestCallback = credentials.mechanismProperties.OIDC_TOKEN_CALLBACK;
71-
if (!requestCallback) {
72-
throw new MongoMissingCredentialsError(NO_REQUEST_CALLBACK);
73-
}
78+
const callbackInfo = getCallback(credentials.mechanismProperties);
7479
const startDocument = await this.startAuthentication(connection, credentials, response);
7580
const conversationId = startDocument.conversationId;
76-
const serverResult = BSON.deserialize(startDocument.payload.buffer) as IdPServerInfo;
77-
const tokenResult = await this.fetchAccessToken(
78-
connection,
79-
credentials,
80-
serverResult,
81-
requestCallback
82-
);
81+
const idpInfo = BSON.deserialize(startDocument.payload.buffer) as IdPInfo;
82+
const tokenResult = await this.fetchAccessToken(connection, credentials, idpInfo, callbackInfo);
8383
const result = await this.finishAuthentication(
8484
connection,
8585
credentials,
@@ -136,15 +136,18 @@ export class CallbackWorkflow implements Workflow {
136136
private async fetchAccessToken(
137137
connection: Connection,
138138
credentials: MongoCredentials,
139-
serverInfo: IdPServerInfo,
140-
requestCallback: OIDCRequestFunction
139+
idpInfo: IdPInfo,
140+
callbackInfo: OIDCCallbackInfo
141141
): Promise<IdPServerResponse> {
142-
const params: OIDCTokenParams = {
143-
timeoutContext: AbortSignal.timeout(TIMEOUT_S),
144-
version: OIDC_VERSION
142+
const params: OIDCCallbackParams = {
143+
timeoutContext: AbortSignal.timeout(
144+
callbackInfo.isHumanWorkflow ? HUMAN_TIMEOUT_MS : HUMAN_TIMEOUT_MS
145+
), // TODO: CSOT
146+
version: OIDC_VERSION,
147+
idpInfo: idpInfo // TODO: refreshToken?
145148
};
146149
// With no token in the cache we use the request callback.
147-
const result = await requestCallback(params);
150+
const result = await callbackInfo.callback(params);
148151
// Validate that the result returned by the callback is acceptable. If it is not
149152
// we must clear the token result from the cache.
150153
if (isCallbackResultInvalid(result)) {
@@ -154,6 +157,20 @@ export class CallbackWorkflow implements Workflow {
154157
}
155158
}
156159

160+
/**
161+
* Returns a callback, either machine or human, and a flag whether the workflow is
162+
* human or not.
163+
*/
164+
function getCallback(mechanismProperties: AuthMechanismProperties): OIDCCallbackInfo {
165+
if (!mechanismProperties.OIDC_CALLBACK || !mechanismProperties.OIDC_HUMAN_CALLBACK) {
166+
throw new MongoMissingCredentialsError(NO_CALLBACK);
167+
}
168+
if (mechanismProperties.OIDC_CALLBACK) {
169+
return { callback: mechanismProperties.OIDC_CALLBACK, isHumanWorkflow: false };
170+
}
171+
return { callback: mechanismProperties.OIDC_HUMAN_CALLBACK, isHumanWorkflow: true };
172+
}
173+
157174
/**
158175
* Determines if a result returned from a request or refresh callback
159176
* function is invalid. This means the result is nullish, doesn't contain

src/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,11 +250,11 @@ export type {
250250
MongoCredentialsOptions
251251
} from './cmap/auth/mongo_credentials';
252252
export type {
253-
IdPServerInfo,
253+
IdPInfo as IdPServerInfo,
254254
IdPServerResponse,
255-
OIDCRequestFunction,
256-
OIDCToken,
257-
OIDCTokenParams
255+
OIDCCallbackFunction as OIDCRequestFunction,
256+
OIDCResponse as OIDCToken,
257+
OIDCCallbackParams as OIDCTokenParams
258258
} from './cmap/auth/mongodb_oidc';
259259
export type {
260260
MessageHeader,

0 commit comments

Comments
 (0)