@@ -372,13 +372,24 @@ func (s *Server) handlePasswordLogin(w http.ResponseWriter, r *http.Request) {
372
372
}
373
373
return
374
374
}
375
- redirectURL , err := s .finalizeLogin (identity , authReq , conn .Connector )
375
+ redirectURL , canSkipApproval , err := s .finalizeLogin (identity , authReq , conn .Connector )
376
376
if err != nil {
377
377
s .logger .Errorf ("Failed to finalize login: %v" , err )
378
378
s .renderError (r , w , http .StatusInternalServerError , "Login error." )
379
379
return
380
380
}
381
381
382
+ if canSkipApproval {
383
+ authReq , err = s .storage .GetAuthRequest (authReq .ID )
384
+ if err != nil {
385
+ s .logger .Errorf ("Failed to get finalized auth request: %v" , err )
386
+ s .renderError (r , w , http .StatusInternalServerError , "Login error." )
387
+ return
388
+ }
389
+ s .sendCodeResponse (w , r , authReq )
390
+ return
391
+ }
392
+
382
393
http .Redirect (w , r , redirectURL , http .StatusSeeOther )
383
394
default :
384
395
s .renderError (r , w , http .StatusBadRequest , "Unsupported request method." )
@@ -460,19 +471,30 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
460
471
return
461
472
}
462
473
463
- redirectURL , err := s .finalizeLogin (identity , authReq , conn .Connector )
474
+ redirectURL , canSkipApproval , err := s .finalizeLogin (identity , authReq , conn .Connector )
464
475
if err != nil {
465
476
s .logger .Errorf ("Failed to finalize login: %v" , err )
466
477
s .renderError (r , w , http .StatusInternalServerError , "Login error." )
467
478
return
468
479
}
469
480
481
+ if canSkipApproval {
482
+ authReq , err = s .storage .GetAuthRequest (authReq .ID )
483
+ if err != nil {
484
+ s .logger .Errorf ("Failed to get finalized auth request: %v" , err )
485
+ s .renderError (r , w , http .StatusInternalServerError , "Login error." )
486
+ return
487
+ }
488
+ s .sendCodeResponse (w , r , authReq )
489
+ return
490
+ }
491
+
470
492
http .Redirect (w , r , redirectURL , http .StatusSeeOther )
471
493
}
472
494
473
495
// finalizeLogin associates the user's identity with the current AuthRequest, then returns
474
496
// the approval page's path.
475
- func (s * Server ) finalizeLogin (identity connector.Identity , authReq storage.AuthRequest , conn connector.Connector ) (string , error ) {
497
+ func (s * Server ) finalizeLogin (identity connector.Identity , authReq storage.AuthRequest , conn connector.Connector ) (returnURL string , canSkipApproval bool , err error ) {
476
498
claims := storage.Claims {
477
499
UserID : identity .UserID ,
478
500
Username : identity .Username ,
@@ -488,8 +510,8 @@ func (s *Server) finalizeLogin(identity connector.Identity, authReq storage.Auth
488
510
a .ConnectorData = identity .ConnectorData
489
511
return a , nil
490
512
}
491
- if err : = s .storage .UpdateAuthRequest (authReq .ID , updater ); err != nil {
492
- return "" , fmt .Errorf ("failed to update auth request: %v" , err )
513
+ if err = s .storage .UpdateAuthRequest (authReq .ID , updater ); err != nil {
514
+ return "" , false , fmt .Errorf ("failed to update auth request: %v" , err )
493
515
}
494
516
495
517
email := claims .Email
@@ -500,26 +522,29 @@ func (s *Server) finalizeLogin(identity connector.Identity, authReq storage.Auth
500
522
s .logger .Infof ("login successful: connector %q, username=%q, preferred_username=%q, email=%q, groups=%q" ,
501
523
authReq .ConnectorID , claims .Username , claims .PreferredUsername , email , claims .Groups )
502
524
503
- // TODO: if s.skipApproval or !authReq.ForceApprovalPrompt, we can skip the redirect to /approval and go ahead and send code
525
+ // we can skip the redirect to /approval and go ahead and send code if it's not required
526
+ if s .skipApproval || ! authReq .ForceApprovalPrompt {
527
+ return "" , true , nil
528
+ }
504
529
505
530
// an HMAC is used here to ensure that the request ID is unpredictable, ensuring that an attacker who intercepted the original
506
531
// flow would be unable to poll for the result at the /approval endpoint
507
532
h := hmac .New (sha256 .New , authReq .HMACKey )
508
533
h .Write ([]byte (authReq .ID ))
509
534
mac := h .Sum (nil )
510
535
511
- returnURL : = path .Join (s .issuerURL .Path , "/approval" ) + "?req=" + authReq .ID + "&hmac=" + base64 .RawURLEncoding .EncodeToString (mac )
536
+ returnURL = path .Join (s .issuerURL .Path , "/approval" ) + "?req=" + authReq .ID + "&hmac=" + base64 .RawURLEncoding .EncodeToString (mac )
512
537
_ , ok := conn .(connector.RefreshConnector )
513
538
if ! ok {
514
- return returnURL , nil
539
+ return returnURL , false , nil
515
540
}
516
541
517
542
// Try to retrieve an existing OfflineSession object for the corresponding user.
518
543
session , err := s .storage .GetOfflineSessions (identity .UserID , authReq .ConnectorID )
519
544
if err != nil {
520
545
if err != storage .ErrNotFound {
521
546
s .logger .Errorf ("failed to get offline session: %v" , err )
522
- return "" , err
547
+ return "" , false , err
523
548
}
524
549
offlineSessions := storage.OfflineSessions {
525
550
UserID : identity .UserID ,
@@ -532,10 +557,10 @@ func (s *Server) finalizeLogin(identity connector.Identity, authReq storage.Auth
532
557
// the newly received refreshtoken.
533
558
if err := s .storage .CreateOfflineSessions (offlineSessions ); err != nil {
534
559
s .logger .Errorf ("failed to create offline session: %v" , err )
535
- return "" , err
560
+ return "" , false , err
536
561
}
537
562
538
- return returnURL , nil
563
+ return returnURL , false , nil
539
564
}
540
565
541
566
// Update existing OfflineSession obj with new RefreshTokenRef.
@@ -546,10 +571,10 @@ func (s *Server) finalizeLogin(identity connector.Identity, authReq storage.Auth
546
571
return old , nil
547
572
}); err != nil {
548
573
s .logger .Errorf ("failed to update offline session: %v" , err )
549
- return "" , err
574
+ return "" , false , err
550
575
}
551
576
552
- return returnURL , nil
577
+ return returnURL , false , nil
553
578
}
554
579
555
580
func (s * Server ) handleApproval (w http.ResponseWriter , r * http.Request ) {
@@ -588,6 +613,8 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
588
613
589
614
switch r .Method {
590
615
case http .MethodGet :
616
+ // TODO: `finalizeLogin()` now sends code directly to client without going through this endpoint,
617
+ // the `if skipApproval { ... }` block needs to be removed after a grace period.
591
618
if s .skipApproval {
592
619
s .sendCodeResponse (w , r , authReq )
593
620
return
0 commit comments