@@ -252,7 +252,21 @@ type LoginData struct {
252252 TargetCluster string
253253}
254254
255- func (f * loginFlow ) finish (ctx context.Context , user string , resp * wantypes.CredentialAssertionResponse , requiredExtensions * mfav1.ChallengeExtensions ) (* LoginData , error ) {
255+ // mfaValidationResult holds the results of MFA validation needed for subsequent operations.
256+ type mfaValidationResult struct {
257+ user string
258+ device * types.MFADevice
259+ credential * wan.Credential
260+ sessionData * wantypes.SessionData
261+ discoverableLogin bool
262+ challengeAllowReuse bool
263+ challenge string
264+ rpID string
265+ }
266+
267+ // validateMFAResponseInternal performs all cryptographic validation of an MFA
268+ // response without consuming it.
269+ func (f * loginFlow ) validateMFAResponseInternal (ctx context.Context , user string , resp * wantypes.CredentialAssertionResponse , requiredExtensions * mfav1.ChallengeExtensions ) (* mfaValidationResult , error ) {
256270 if requiredExtensions == nil {
257271 return nil , trace .BadParameter ("requested challenge extensions must be supplied." )
258272 }
@@ -413,44 +427,65 @@ func (f *loginFlow) finish(ctx context.Context, user string, resp *wantypes.Cred
413427 }
414428 }
415429
430+ // Return validation results without modifying any state
431+ return & mfaValidationResult {
432+ user : user ,
433+ device : dev ,
434+ credential : credential ,
435+ sessionData : sd ,
436+ discoverableLogin : discoverableLogin ,
437+ challengeAllowReuse : challengeAllowReuse ,
438+ challenge : challenge ,
439+ rpID : rpID ,
440+ }, nil
441+ }
442+
443+ func (f * loginFlow ) finish (ctx context.Context , user string , resp * wantypes.CredentialAssertionResponse , requiredExtensions * mfav1.ChallengeExtensions ) (* LoginData , error ) {
444+ // Validate the MFA response
445+ result , err := f .validateMFAResponseInternal (ctx , user , resp , requiredExtensions )
446+ if err != nil {
447+ return nil , trace .Wrap (err )
448+ }
449+
416450 // Update last used timestamp and device counter.
417- if err := updateCredentialAndTimestamps (dev , credential , discoverableLogin ); err != nil {
451+ if err := updateCredentialAndTimestamps (result . device , result . credential , result . discoverableLogin ); err != nil {
418452 return nil , trace .Wrap (err )
419453 }
454+
420455 // Retroactively write the credential RPID, now that it cleared authn.
421- if webDev := dev .GetWebauthn (); webDev != nil && webDev .CredentialRpId == "" {
456+ if webDev := result . device .GetWebauthn (); webDev != nil && webDev .CredentialRpId == "" {
422457 log .DebugContext (ctx , "Recording RPID in device" ,
423- "rpid" , rpID ,
424- "user" , user ,
425- "device" , dev .GetName (),
458+ "rpid" , result . rpID ,
459+ "user" , result . user ,
460+ "device" , result . device .GetName (),
426461 )
427- webDev .CredentialRpId = rpID
462+ webDev .CredentialRpId = result . rpID
428463 }
429464
430- if err := f .identity .UpsertMFADevice (ctx , user , dev ); err != nil {
465+ if err := f .identity .UpsertMFADevice (ctx , result . user , result . device ); err != nil {
431466 return nil , trace .Wrap (err )
432467 }
433468
434469 // The user just solved the challenge, so let's make sure it won't be used
435470 // again, unless reuse is explicitly allowed.
436471 // Note that even reusable sessions are deleted when their expiration time
437472 // passes.
438- if ! challengeAllowReuse {
439- if err := f .sessionData .Delete (ctx , user , challenge ); err != nil {
473+ if ! result . challengeAllowReuse {
474+ if err := f .sessionData .Delete (ctx , result . user , result . challenge ); err != nil {
440475 log .WarnContext (ctx , "failed to delete login SessionData for user" ,
441- "user" , user ,
442- "scope" , sd .ChallengeExtensions .Scope ,
476+ "user" , result . user ,
477+ "scope" , result . sessionData .ChallengeExtensions .Scope ,
443478 )
444479 }
445480 }
446481
447482 return & LoginData {
448- User : user ,
449- Device : dev ,
450- AllowReuse : sd .ChallengeExtensions .AllowReuse ,
451- Payload : sd .Payload ,
452- SourceCluster : sd .SourceCluster ,
453- TargetCluster : sd .TargetCluster ,
483+ User : result . user ,
484+ Device : result . device ,
485+ AllowReuse : result . sessionData .ChallengeExtensions .AllowReuse ,
486+ Payload : result . sessionData .Payload ,
487+ SourceCluster : result . sessionData .SourceCluster ,
488+ TargetCluster : result . sessionData .TargetCluster ,
454489 }, nil
455490}
456491
0 commit comments