@@ -27,6 +27,7 @@ import { LoginCompletionHandler } from "../auth/login-completion-handler";
27
27
import { TosCookie } from "./tos-cookie" ;
28
28
import { TosFlow } from "../terms/tos-flow" ;
29
29
import { increaseLoginCounter } from '../../src/prometheusMetrics' ;
30
+ import * as uuidv4 from 'uuid/v4' ;
30
31
31
32
@injectable ( )
32
33
export class UserController {
@@ -302,12 +303,17 @@ export class UserController {
302
303
user : User . censor ( user ) ,
303
304
returnToUrl : req . query . returnTo
304
305
} ;
305
- await TosFlow . attach ( req . session ! , tosFlowInfo ) ;
306
306
}
307
307
308
+ // attaching a random identifier for this web flow to test if it's present in `/tos/proceed` handler
309
+ const flowId = uuidv4 ( ) ;
310
+ tosFlowInfo . flowId = flowId ;
311
+ await TosFlow . attach ( req . session ! , tosFlowInfo ) ;
312
+
308
313
const isUpdate = ! TosFlow . WithIdentity . is ( tosFlowInfo ) ;
309
314
const userInfo = tosFlowUserInfo ( tosFlowInfo ) ;
310
315
const tosHints = {
316
+ flowId,
311
317
isUpdate, // indicate whether to show the "we've updated ..." message
312
318
userInfo // let us render the avatar on the dashboard page
313
319
} ;
@@ -356,6 +362,21 @@ export class UserController {
356
362
return ;
357
363
}
358
364
365
+ // detaching the (random) identifier of this webflow
366
+ const flowId = tosFlowInfo . flowId ;
367
+ delete tosFlowInfo . flowId ;
368
+ await TosFlow . attach ( req . session ! , tosFlowInfo ) ;
369
+
370
+ // let's assume if the form is re-submitted a second time, we need to abort the process, because
371
+ // otherwise we potentially create accounts for the same provider identity twice.
372
+ //
373
+ // todo@alex : check if it's viable to test the flow ids for a single submission, instead of detaching
374
+ // from the session.
375
+ if ( typeof flowId !== "string" ) {
376
+ await redirectOnInvalidSession ( ) ;
377
+ return ;
378
+ }
379
+
359
380
const agreeTOS = req . body . agreeTOS ;
360
381
if ( ! agreeTOS ) {
361
382
// The user did not accept the terms.
@@ -364,7 +385,7 @@ export class UserController {
364
385
log . info ( logContext , '(TOS) User did NOT agree. Redirecting to /logout.' , logPayload ) ;
365
386
366
387
res . redirect ( this . env . hostUrl . withApi ( { pathname : "/logout" } ) . toString ( ) ) ;
367
- // todo@alex : consider redirecting to a description pages afterwards (returnTo param)
388
+ // todo@alex : consider redirecting to a info page (returnTo param)
368
389
369
390
return ;
370
391
}
@@ -377,8 +398,21 @@ export class UserController {
377
398
await redirectOnInvalidSession ( ) ;
378
399
return ;
379
400
}
380
- await this . handleTosProceedForNewUser ( req , res , authFlow , tosFlowInfo , req . body ) ;
381
- } else if ( TosFlow . WithUser . is ( tosFlowInfo ) ) {
401
+
402
+ // there is a possibility, that a competing browser session already created a new user account
403
+ // for this provider identity, thus we need to check again, in order to avoid created unreachable accounts
404
+ const user = await this . userService . findUserForLogin ( { candidate : tosFlowInfo . candidate } ) ;
405
+ if ( user ) {
406
+ log . info ( `(TOS) User was created in a parallel browser session, let's login...` , { logPayload } ) ;
407
+ await this . loginCompletionHandler . complete ( req , res , { user, authHost : tosFlowInfo . authHost , returnToUrl : authFlow . returnTo } ) ;
408
+ } else {
409
+ await this . handleTosProceedForNewUser ( req , res , authFlow , tosFlowInfo , req . body ) ;
410
+ }
411
+
412
+ return ;
413
+ }
414
+
415
+ if ( TosFlow . WithUser . is ( tosFlowInfo ) ) {
382
416
const { user, returnToUrl } = tosFlowInfo ;
383
417
384
418
await this . userService . acceptCurrentTerms ( user ) ;
0 commit comments