1
1
import { Binary , BSON , type Document } from 'bson' ;
2
2
3
- import { MongoInvalidArgumentError , MongoMissingCredentialsError } from '../../../error' ;
3
+ import {
4
+ MONGODB_ERROR_CODES ,
5
+ MongoError ,
6
+ MongoInvalidArgumentError ,
7
+ MongoMissingCredentialsError
8
+ } from '../../../error' ;
4
9
import { ns } from '../../../utils' ;
5
10
import type { Connection } from '../../connection' ;
6
11
import type { MongoCredentials } from '../mongo_credentials' ;
7
- import type {
8
- OIDCMechanismServerStep1 ,
12
+ import {
13
+ IdPServerInfo ,
14
+ IdPServerResponse ,
15
+ OIDC_VERSION ,
16
+ OIDCCallbackContext ,
9
17
OIDCRefreshFunction ,
10
- OIDCRequestFunction ,
11
- OIDCRequestTokenResult
18
+ OIDCRequestFunction
12
19
} from '../mongodb_oidc' ;
13
20
import { AuthMechanism } from '../providers' ;
14
21
import { TokenEntryCache } from './token_entry_cache' ;
@@ -71,8 +78,8 @@ export class CallbackWorkflow implements Workflow {
71
78
let result ;
72
79
// Reauthentication must go through all the steps again regards of a cache entry
73
80
// being present.
74
- if ( entry && ! reauthenticating ) {
75
- if ( entry . isValid ( ) ) {
81
+ if ( entry ) {
82
+ if ( entry . isValid ( ) && ! reauthenticating ) {
76
83
// Presence of a valid cache entry means we can skip to the finishing step.
77
84
result = await this . finishAuthentication (
78
85
connection ,
@@ -91,12 +98,33 @@ export class CallbackWorkflow implements Workflow {
91
98
requestCallback ,
92
99
refreshCallback
93
100
) ;
94
- result = await this . finishAuthentication (
95
- connection ,
96
- credentials ,
97
- tokenResult ,
98
- response ?. speculativeAuthenticate ?. conversationId
99
- ) ;
101
+ try {
102
+ result = await this . finishAuthentication (
103
+ connection ,
104
+ credentials ,
105
+ tokenResult ,
106
+ response ?. speculativeAuthenticate ?. conversationId
107
+ ) ;
108
+ } catch ( error ) {
109
+ // If we are reauthenticating and this errors with reauthentication
110
+ // required, we need to do the entire process over again and clear
111
+ // the cache entry.
112
+ if (
113
+ reauthenticating &&
114
+ error instanceof MongoError &&
115
+ error . code === MONGODB_ERROR_CODES . Reauthenticate
116
+ ) {
117
+ this . cache . deleteEntry (
118
+ connection . address ,
119
+ credentials . username || '' ,
120
+ requestCallback ,
121
+ refreshCallback || null
122
+ ) ;
123
+ result = await this . execute ( connection , credentials , reauthenticating ) ;
124
+ } else {
125
+ throw error ;
126
+ }
127
+ }
100
128
}
101
129
} else {
102
130
// No entry in the cache requires us to do all authentication steps
@@ -108,9 +136,7 @@ export class CallbackWorkflow implements Workflow {
108
136
response
109
137
) ;
110
138
const conversationId = startDocument . conversationId ;
111
- const serverResult = BSON . deserialize (
112
- startDocument . payload . buffer
113
- ) as OIDCMechanismServerStep1 ;
139
+ const serverResult = BSON . deserialize ( startDocument . payload . buffer ) as IdPServerInfo ;
114
140
const tokenResult = await this . fetchAccessToken (
115
141
connection ,
116
142
credentials ,
@@ -159,7 +185,7 @@ export class CallbackWorkflow implements Workflow {
159
185
private async finishAuthentication (
160
186
connection : Connection ,
161
187
credentials : MongoCredentials ,
162
- tokenResult : OIDCRequestTokenResult ,
188
+ tokenResult : IdPServerResponse ,
163
189
conversationId ?: number
164
190
) : Promise < Document > {
165
191
const result = await connection . commandAsync (
@@ -177,11 +203,11 @@ export class CallbackWorkflow implements Workflow {
177
203
private async fetchAccessToken (
178
204
connection : Connection ,
179
205
credentials : MongoCredentials ,
180
- startResult : OIDCMechanismServerStep1 ,
206
+ serverInfo : IdPServerInfo ,
181
207
reauthenticating : boolean ,
182
208
requestCallback : OIDCRequestFunction ,
183
209
refreshCallback ?: OIDCRefreshFunction
184
- ) : Promise < OIDCRequestTokenResult > {
210
+ ) : Promise < IdPServerResponse > {
185
211
// Get the token from the cache.
186
212
const entry = this . cache . getEntry (
187
213
connection . address ,
@@ -190,7 +216,7 @@ export class CallbackWorkflow implements Workflow {
190
216
refreshCallback || null
191
217
) ;
192
218
let result ;
193
- const clientInfo = { principalName : credentials . username , timeoutSeconds : TIMEOUT_S } ;
219
+ const context : OIDCCallbackContext = { timeoutSeconds : TIMEOUT_S , version : OIDC_VERSION } ;
194
220
// Check if there's a token in the cache.
195
221
if ( entry ) {
196
222
// If the cache entry is valid, return the token result.
@@ -201,13 +227,14 @@ export class CallbackWorkflow implements Workflow {
201
227
// to use the refresh callback to get a new token. If no refresh callback
202
228
// exists, then fallback to the request callback.
203
229
if ( refreshCallback ) {
204
- result = await refreshCallback ( clientInfo , startResult , entry . tokenResult ) ;
230
+ context . refreshToken = entry . tokenResult . refreshToken ;
231
+ result = await refreshCallback ( serverInfo , context ) ;
205
232
} else {
206
- result = await requestCallback ( clientInfo , startResult ) ;
233
+ result = await requestCallback ( serverInfo , context ) ;
207
234
}
208
235
} else {
209
236
// With no token in the cache we use the request callback.
210
- result = await requestCallback ( clientInfo , startResult ) ;
237
+ result = await requestCallback ( serverInfo , context ) ;
211
238
}
212
239
// Validate that the result returned by the callback is acceptable.
213
240
if ( isCallbackResultInvalid ( result ) ) {
@@ -224,7 +251,7 @@ export class CallbackWorkflow implements Workflow {
224
251
requestCallback ,
225
252
refreshCallback || null ,
226
253
result ,
227
- startResult
254
+ serverInfo
228
255
) ;
229
256
return result ;
230
257
}
0 commit comments