1
1
import { Binary , BSON , type Document } from 'bson' ;
2
2
3
- import { MONGODB_ERROR_CODES , MongoError , MongoMissingCredentialsError } from '../../../error' ;
3
+ import { MongoMissingCredentialsError } from '../../../error' ;
4
4
import { ns } from '../../../utils' ;
5
5
import type { Connection } from '../../connection' ;
6
6
import type { MongoCredentials } from '../mongo_credentials' ;
7
7
import type {
8
8
IdPServerInfo ,
9
9
IdPServerResponse ,
10
10
OIDCCallbackContext ,
11
- OIDCRefreshFunction ,
12
11
OIDCRequestFunction ,
13
12
Workflow
14
13
} from '../mongodb_oidc' ;
15
14
import { AuthMechanism } from '../providers' ;
16
- import { CallbackLockCache } from './callback_lock_cache' ;
17
- import { TokenEntryCache } from './token_entry_cache' ;
18
15
19
16
/** The current version of OIDC implementation. */
20
17
const OIDC_VERSION = 0 ;
@@ -29,22 +26,13 @@ const RESULT_PROPERTIES = ['accessToken', 'expiresInSeconds', 'refreshToken'];
29
26
const CALLBACK_RESULT_ERROR =
30
27
'User provided OIDC callbacks must return a valid object with an accessToken.' ;
31
28
29
+ const NO_REQUEST_CALLBACK = 'No REQUEST_TOKEN_CALLBACK provided for callback workflow.' ;
30
+
32
31
/**
33
32
* OIDC implementation of a callback based workflow.
34
33
* @internal
35
34
*/
36
35
export 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
-
48
36
/**
49
37
* Get the document to add for speculative authentication. This also needs
50
38
* to add a db field from the credentials source.
@@ -64,87 +52,32 @@ export class CallbackWorkflow implements Workflow {
64
52
reauthenticating : boolean ,
65
53
response ?: Document
66
54
) : 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 (
69
62
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
71
80
) ;
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
- }
148
81
return result ;
149
82
}
150
83
@@ -197,50 +130,16 @@ export class CallbackWorkflow implements Workflow {
197
130
connection : Connection ,
198
131
credentials : MongoCredentials ,
199
132
serverInfo : IdPServerInfo ,
200
- reauthenticating : boolean ,
201
- callbackHash : string ,
202
- requestCallback : OIDCRequestFunction ,
203
- refreshCallback ?: OIDCRefreshFunction
133
+ requestCallback : OIDCRequestFunction
204
134
) : Promise < IdPServerResponse > {
205
- // Get the token from the cache.
206
- const entry = this . cache . getEntry ( connection . address , credentials . username , callbackHash ) ;
207
- let result ;
208
135
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 ) ;
228
138
// Validate that the result returned by the callback is acceptable. If it is not
229
139
// we must clear the token result from the cache.
230
140
if ( isCallbackResultInvalid ( result ) ) {
231
- this . cache . deleteEntry ( connection . address , credentials . username , callbackHash ) ;
232
141
throw new MongoMissingCredentialsError ( CALLBACK_RESULT_ERROR ) ;
233
142
}
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
- ) ;
244
143
return result ;
245
144
}
246
145
}
0 commit comments