Skip to content

Commit da2b004

Browse files
nobuyomichaelliau
authored andcommitted
Skip redirection to approval when it is not requied (dexidp#2686)
Signed-off-by: nobuyo <[email protected]>
1 parent 6f7f37c commit da2b004

File tree

1 file changed

+40
-13
lines changed

1 file changed

+40
-13
lines changed

server/handlers.go

+40-13
Original file line numberDiff line numberDiff line change
@@ -372,13 +372,24 @@ func (s *Server) handlePasswordLogin(w http.ResponseWriter, r *http.Request) {
372372
}
373373
return
374374
}
375-
redirectURL, err := s.finalizeLogin(identity, authReq, conn.Connector)
375+
redirectURL, canSkipApproval, err := s.finalizeLogin(identity, authReq, conn.Connector)
376376
if err != nil {
377377
s.logger.Errorf("Failed to finalize login: %v", err)
378378
s.renderError(r, w, http.StatusInternalServerError, "Login error.")
379379
return
380380
}
381381

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+
382393
http.Redirect(w, r, redirectURL, http.StatusSeeOther)
383394
default:
384395
s.renderError(r, w, http.StatusBadRequest, "Unsupported request method.")
@@ -460,19 +471,30 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
460471
return
461472
}
462473

463-
redirectURL, err := s.finalizeLogin(identity, authReq, conn.Connector)
474+
redirectURL, canSkipApproval, err := s.finalizeLogin(identity, authReq, conn.Connector)
464475
if err != nil {
465476
s.logger.Errorf("Failed to finalize login: %v", err)
466477
s.renderError(r, w, http.StatusInternalServerError, "Login error.")
467478
return
468479
}
469480

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+
470492
http.Redirect(w, r, redirectURL, http.StatusSeeOther)
471493
}
472494

473495
// finalizeLogin associates the user's identity with the current AuthRequest, then returns
474496
// 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) {
476498
claims := storage.Claims{
477499
UserID: identity.UserID,
478500
Username: identity.Username,
@@ -488,8 +510,8 @@ func (s *Server) finalizeLogin(identity connector.Identity, authReq storage.Auth
488510
a.ConnectorData = identity.ConnectorData
489511
return a, nil
490512
}
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)
493515
}
494516

495517
email := claims.Email
@@ -500,26 +522,29 @@ func (s *Server) finalizeLogin(identity connector.Identity, authReq storage.Auth
500522
s.logger.Infof("login successful: connector %q, username=%q, preferred_username=%q, email=%q, groups=%q",
501523
authReq.ConnectorID, claims.Username, claims.PreferredUsername, email, claims.Groups)
502524

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+
}
504529

505530
// an HMAC is used here to ensure that the request ID is unpredictable, ensuring that an attacker who intercepted the original
506531
// flow would be unable to poll for the result at the /approval endpoint
507532
h := hmac.New(sha256.New, authReq.HMACKey)
508533
h.Write([]byte(authReq.ID))
509534
mac := h.Sum(nil)
510535

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)
512537
_, ok := conn.(connector.RefreshConnector)
513538
if !ok {
514-
return returnURL, nil
539+
return returnURL, false, nil
515540
}
516541

517542
// Try to retrieve an existing OfflineSession object for the corresponding user.
518543
session, err := s.storage.GetOfflineSessions(identity.UserID, authReq.ConnectorID)
519544
if err != nil {
520545
if err != storage.ErrNotFound {
521546
s.logger.Errorf("failed to get offline session: %v", err)
522-
return "", err
547+
return "", false, err
523548
}
524549
offlineSessions := storage.OfflineSessions{
525550
UserID: identity.UserID,
@@ -532,10 +557,10 @@ func (s *Server) finalizeLogin(identity connector.Identity, authReq storage.Auth
532557
// the newly received refreshtoken.
533558
if err := s.storage.CreateOfflineSessions(offlineSessions); err != nil {
534559
s.logger.Errorf("failed to create offline session: %v", err)
535-
return "", err
560+
return "", false, err
536561
}
537562

538-
return returnURL, nil
563+
return returnURL, false, nil
539564
}
540565

541566
// Update existing OfflineSession obj with new RefreshTokenRef.
@@ -546,10 +571,10 @@ func (s *Server) finalizeLogin(identity connector.Identity, authReq storage.Auth
546571
return old, nil
547572
}); err != nil {
548573
s.logger.Errorf("failed to update offline session: %v", err)
549-
return "", err
574+
return "", false, err
550575
}
551576

552-
return returnURL, nil
577+
return returnURL, false, nil
553578
}
554579

555580
func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
@@ -588,6 +613,8 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
588613

589614
switch r.Method {
590615
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.
591618
if s.skipApproval {
592619
s.sendCodeResponse(w, r, authReq)
593620
return

0 commit comments

Comments
 (0)