11import { Binary , BSON , type Document } from 'bson' ;
22
3- import { MONGODB_ERROR_CODES , MongoError , MongoMissingCredentialsError } from '../../../error' ;
3+ import { MongoMissingCredentialsError } from '../../../error' ;
44import { ns } from '../../../utils' ;
55import type { Connection } from '../../connection' ;
66import type { MongoCredentials } from '../mongo_credentials' ;
77import type {
88 IdPServerInfo ,
99 IdPServerResponse ,
1010 OIDCCallbackContext ,
11- OIDCRefreshFunction ,
1211 OIDCRequestFunction ,
1312 Workflow
1413} from '../mongodb_oidc' ;
1514import { AuthMechanism } from '../providers' ;
16- import { CallbackLockCache } from './callback_lock_cache' ;
17- import { TokenEntryCache } from './token_entry_cache' ;
1815
1916/** The current version of OIDC implementation. */
2017const OIDC_VERSION = 0 ;
@@ -29,22 +26,13 @@ const RESULT_PROPERTIES = ['accessToken', 'expiresInSeconds', 'refreshToken'];
2926const CALLBACK_RESULT_ERROR =
3027 'User provided OIDC callbacks must return a valid object with an accessToken.' ;
3128
29+ const NO_REQUEST_CALLBACK = 'No REQUEST_TOKEN_CALLBACK provided for callback workflow.' ;
30+
3231/**
3332 * OIDC implementation of a callback based workflow.
3433 * @internal
3534 */
3635export class CallbackWorkflow implements Workflow {
37- cache : TokenEntryCache ;
38- callbackCache : CallbackLockCache ;
39-
40- /**
41- * Instantiate the workflow
42- */
43- constructor ( ) {
44- this . cache = new TokenEntryCache ( ) ;
45- this . callbackCache = new CallbackLockCache ( ) ;
46- }
47-
4836 /**
4937 * Get the document to add for speculative authentication. This also needs
5038 * to add a db field from the credentials source.
@@ -64,87 +52,32 @@ export class CallbackWorkflow implements Workflow {
6452 reauthenticating : boolean ,
6553 response ?: Document
6654 ) : Promise < Document > {
67- // Get the callbacks with locks from the callback lock cache.
68- const { requestCallback, refreshCallback, callbackHash } = this . callbackCache . getEntry (
55+ const requestCallback = credentials . mechanismProperties . REQUEST_TOKEN_CALLBACK ;
56+ if ( ! requestCallback ) {
57+ throw new MongoMissingCredentialsError ( NO_REQUEST_CALLBACK ) ;
58+ }
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 (
6962 connection ,
70- credentials
63+ credentials ,
64+ reauthenticating ,
65+ response
66+ ) ;
67+ const conversationId = startDocument . conversationId ;
68+ const serverResult = BSON . deserialize ( startDocument . payload . buffer ) as IdPServerInfo ;
69+ const tokenResult = await this . fetchAccessToken (
70+ connection ,
71+ credentials ,
72+ serverResult ,
73+ requestCallback
74+ ) ;
75+ const result = await this . finishAuthentication (
76+ connection ,
77+ credentials ,
78+ tokenResult ,
79+ conversationId
7180 ) ;
72- // Look for an existing entry in the cache.
73- const entry = this . cache . getEntry ( connection . address , credentials . username , callbackHash ) ;
74- let result ;
75- if ( entry ) {
76- // Reauthentication cannot use a token from the cache since the server has
77- // stated it is invalid by the request for reauthentication.
78- if ( entry . isValid ( ) && ! reauthenticating ) {
79- // Presence of a valid cache entry means we can skip to the finishing step.
80- result = await this . finishAuthentication (
81- connection ,
82- credentials ,
83- entry . tokenResult ,
84- response ?. speculativeAuthenticate ?. conversationId
85- ) ;
86- } else {
87- // Presence of an expired cache entry means we must fetch a new one and
88- // then execute the final step.
89- const tokenResult = await this . fetchAccessToken (
90- connection ,
91- credentials ,
92- entry . serverInfo ,
93- reauthenticating ,
94- callbackHash ,
95- requestCallback ,
96- refreshCallback
97- ) ;
98- try {
99- result = await this . finishAuthentication (
100- connection ,
101- credentials ,
102- tokenResult ,
103- reauthenticating ? undefined : response ?. speculativeAuthenticate ?. conversationId
104- ) ;
105- } catch ( error ) {
106- // If we are reauthenticating and this errors with reauthentication
107- // required, we need to do the entire process over again and clear
108- // the cache entry.
109- if (
110- reauthenticating &&
111- error instanceof MongoError &&
112- error . code === MONGODB_ERROR_CODES . Reauthenticate
113- ) {
114- this . cache . deleteEntry ( connection . address , credentials . username , callbackHash ) ;
115- result = await this . execute ( connection , credentials , reauthenticating ) ;
116- } else {
117- throw error ;
118- }
119- }
120- }
121- } else {
122- // No entry in the cache requires us to do all authentication steps
123- // from start to finish, including getting a fresh token for the cache.
124- const startDocument = await this . startAuthentication (
125- connection ,
126- credentials ,
127- reauthenticating ,
128- response
129- ) ;
130- const conversationId = startDocument . conversationId ;
131- const serverResult = BSON . deserialize ( startDocument . payload . buffer ) as IdPServerInfo ;
132- const tokenResult = await this . fetchAccessToken (
133- connection ,
134- credentials ,
135- serverResult ,
136- reauthenticating ,
137- callbackHash ,
138- requestCallback ,
139- refreshCallback
140- ) ;
141- result = await this . finishAuthentication (
142- connection ,
143- credentials ,
144- tokenResult ,
145- conversationId
146- ) ;
147- }
14881 return result ;
14982 }
15083
@@ -197,50 +130,16 @@ export class CallbackWorkflow implements Workflow {
197130 connection : Connection ,
198131 credentials : MongoCredentials ,
199132 serverInfo : IdPServerInfo ,
200- reauthenticating : boolean ,
201- callbackHash : string ,
202- requestCallback : OIDCRequestFunction ,
203- refreshCallback ?: OIDCRefreshFunction
133+ requestCallback : OIDCRequestFunction
204134 ) : Promise < IdPServerResponse > {
205- // Get the token from the cache.
206- const entry = this . cache . getEntry ( connection . address , credentials . username , callbackHash ) ;
207- let result ;
208135 const context : OIDCCallbackContext = { timeoutSeconds : TIMEOUT_S , version : OIDC_VERSION } ;
209- // Check if there's a token in the cache.
210- if ( entry ) {
211- // If the cache entry is valid, return the token result.
212- if ( entry . isValid ( ) && ! reauthenticating ) {
213- return entry . tokenResult ;
214- }
215- // If the cache entry is not valid, remove it from the cache and first attempt
216- // to use the refresh callback to get a new token. If no refresh callback
217- // exists, then fallback to the request callback.
218- if ( refreshCallback ) {
219- context . refreshToken = entry . tokenResult . refreshToken ;
220- result = await refreshCallback ( serverInfo , context ) ;
221- } else {
222- result = await requestCallback ( serverInfo , context ) ;
223- }
224- } else {
225- // With no token in the cache we use the request callback.
226- result = await requestCallback ( serverInfo , context ) ;
227- }
136+ // With no token in the cache we use the request callback.
137+ const result = await requestCallback ( serverInfo , context ) ;
228138 // Validate that the result returned by the callback is acceptable. If it is not
229139 // we must clear the token result from the cache.
230140 if ( isCallbackResultInvalid ( result ) ) {
231- this . cache . deleteEntry ( connection . address , credentials . username , callbackHash ) ;
232141 throw new MongoMissingCredentialsError ( CALLBACK_RESULT_ERROR ) ;
233142 }
234- // Cleanup the cache.
235- this . cache . deleteExpiredEntries ( ) ;
236- // Put the new entry into the cache.
237- this . cache . addEntry (
238- connection . address ,
239- credentials . username || '' ,
240- callbackHash ,
241- result ,
242- serverInfo
243- ) ;
244143 return result ;
245144 }
246145}
0 commit comments