1- import { Binary , BSON , type Document } from 'bson' ;
1+ import { BSON , type Document } from 'bson' ;
22
33import { MongoMissingCredentialsError } from '../../../error' ;
44import { ns } from '../../../utils' ;
@@ -11,7 +11,7 @@ import type {
1111 OIDCRequestFunction ,
1212 Workflow
1313} from '../mongodb_oidc' ;
14- import { AuthMechanism } from '../providers ' ;
14+ import { finishCommandDocument , startCommandDocument } from './command_builders ' ;
1515
1616/** The current version of OIDC implementation. */
1717const OIDC_VERSION = 0 ;
@@ -43,27 +43,35 @@ export class CallbackWorkflow implements Workflow {
4343 return { speculativeAuthenticate : document } ;
4444 }
4545
46+ /**
47+ * Reauthenticate the callback workflow.
48+ * For reauthentication:
49+ * - Check if the connection's accessToken is not equal to the token manager's.
50+ * - If they are different, use the token from the manager and set it on the connection and finish auth.
51+ * - On success return, on error continue.
52+ * - start auth to update the IDP information
53+ * - If the idp info has changed, clear access token and refresh token.
54+ * - If the idp info has not changed, attempt to use the refresh token.
55+ * - if there's still a refresh token at this point, attempt to finish auth with that.
56+ * - Attempt the full auth run, on error, raise to user.
57+ */
58+ async reauthenticate ( connection : Connection , credentials : MongoCredentials ) : Promise < Document > {
59+ return this . execute ( connection , credentials ) ;
60+ }
61+
4662 /**
4763 * Execute the OIDC callback workflow.
4864 */
4965 async execute (
5066 connection : Connection ,
5167 credentials : MongoCredentials ,
52- reauthenticating : boolean ,
5368 response ?: Document
5469 ) : Promise < Document > {
5570 const requestCallback = credentials . mechanismProperties . REQUEST_TOKEN_CALLBACK ;
5671 if ( ! requestCallback ) {
5772 throw new MongoMissingCredentialsError ( NO_REQUEST_CALLBACK ) ;
5873 }
59- // No entry in the cache requires us to do all authentication steps
60- // from start to finish, including getting a fresh token for the cache.
61- const startDocument = await this . startAuthentication (
62- connection ,
63- credentials ,
64- reauthenticating ,
65- response
66- ) ;
74+ const startDocument = await this . startAuthentication ( connection , credentials , response ) ;
6775 const conversationId = startDocument . conversationId ;
6876 const serverResult = BSON . deserialize ( startDocument . payload . buffer ) as IdPServerInfo ;
6977 const tokenResult = await this . fetchAccessToken (
@@ -89,11 +97,10 @@ export class CallbackWorkflow implements Workflow {
8997 private async startAuthentication (
9098 connection : Connection ,
9199 credentials : MongoCredentials ,
92- reauthenticating : boolean ,
93100 response ?: Document
94101 ) : Promise < Document > {
95102 let result ;
96- if ( ! reauthenticating && response ?. speculativeAuthenticate ) {
103+ if ( response ?. speculativeAuthenticate ) {
97104 result = response . speculativeAuthenticate ;
98105 } else {
99106 result = await connection . commandAsync (
@@ -144,29 +151,6 @@ export class CallbackWorkflow implements Workflow {
144151 }
145152}
146153
147- /**
148- * Generate the finishing command document for authentication. Will be a
149- * saslStart or saslContinue depending on the presence of a conversation id.
150- */
151- function finishCommandDocument ( token : string , conversationId ?: number ) : Document {
152- if ( conversationId != null && typeof conversationId === 'number' ) {
153- return {
154- saslContinue : 1 ,
155- conversationId : conversationId ,
156- payload : new Binary ( BSON . serialize ( { jwt : token } ) )
157- } ;
158- }
159- // saslContinue requires a conversationId in the command to be valid so in this
160- // case the server allows "step two" to actually be a saslStart with the token
161- // as the jwt since the use of the cached value has no correlating conversating
162- // on the particular connection.
163- return {
164- saslStart : 1 ,
165- mechanism : AuthMechanism . MONGODB_OIDC ,
166- payload : new Binary ( BSON . serialize ( { jwt : token } ) )
167- } ;
168- }
169-
170154/**
171155 * Determines if a result returned from a request or refresh callback
172156 * function is invalid. This means the result is nullish, doesn't contain
@@ -177,19 +161,3 @@ function isCallbackResultInvalid(tokenResult: unknown): boolean {
177161 if ( ! ( 'accessToken' in tokenResult ) ) return true ;
178162 return ! Object . getOwnPropertyNames ( tokenResult ) . every ( prop => RESULT_PROPERTIES . includes ( prop ) ) ;
179163}
180-
181- /**
182- * Generate the saslStart command document.
183- */
184- function startCommandDocument ( credentials : MongoCredentials ) : Document {
185- const payload : Document = { } ;
186- if ( credentials . username ) {
187- payload . n = credentials . username ;
188- }
189- return {
190- saslStart : 1 ,
191- autoAuthorize : 1 ,
192- mechanism : AuthMechanism . MONGODB_OIDC ,
193- payload : new Binary ( BSON . serialize ( payload ) )
194- } ;
195- }
0 commit comments