@@ -98,6 +98,7 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username, pas
9898 Id string `json:"id"`
9999 Type string `json:"factorType"`
100100 Provider string `json:"provider"`
101+ Status string `json:"status"`
101102 Embedded struct {
102103 Challenge struct {
103104 CorrectAnswer * int `json:"correctAnswer"`
@@ -183,7 +184,8 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username, pas
183184 break
184185 }
185186
186- var selectedFactor , totpFactor , pushFactor * mfaFactor
187+ var selectedFactor , totpFactor * mfaFactor
188+ var pushFactors []* mfaFactor
187189
188190 // Scan for available factors
189191 for _ , v := range result .Embedded .Factors {
@@ -197,11 +199,17 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username, pas
197199 continue
198200 }
199201
202+ // Skip inactive factors so they don't cause spurious auth failures
203+ // when the user has other active factors available.
204+ if v .Status != "" && v .Status != "ACTIVE" {
205+ continue
206+ }
207+
200208 switch v .Type {
201209 case mfaTOTPMethod :
202210 totpFactor = & v
203211 case mfaPushMethod :
204- pushFactor = & v
212+ pushFactors = append ( pushFactors , & v )
205213 }
206214 }
207215
@@ -211,14 +219,24 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username, pas
211219 switch {
212220 case totpFactor != nil && totp != "" :
213221 selectedFactor = totpFactor
214- case pushFactor != nil && pushFactor .Provider == oktaProvider :
215- selectedFactor = pushFactor
222+ case len (pushFactors ) > 0 :
223+ // Prefer Okta Verify Push over Google Authenticator push
224+ for _ , pf := range pushFactors {
225+ if pf .Provider == oktaProvider {
226+ selectedFactor = pf
227+ break
228+ }
229+ }
230+ if selectedFactor == nil {
231+ selectedFactor = pushFactors [0 ]
232+ }
216233 case totpFactor != nil && totp == "" :
217234 return nil , logical .ErrorResponse ("'totp' passcode parameter is required to perform MFA" ), nil , nil
218235 default :
219236 return nil , logical .ErrorResponse ("Okta Verify Push or TOTP or Google TOTP factor is required in order to perform MFA" ), nil , nil
220237 }
221238
239+ verifyFactor:
222240 requestPath := fmt .Sprintf ("authn/factors/%s/verify" , selectedFactor .Id )
223241
224242 payload := map [string ]interface {}{
@@ -278,8 +296,40 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username, pas
278296 return nil , logical .ErrorResponse ("exiting pending mfa challenge" ), nil , nil
279297 }
280298 case "REJECTED" :
299+ // If there are more push factors, try the next one
300+ if len (pushFactors ) > 1 && selectedFactor .Type == mfaPushMethod {
301+ // Remove the current factor and try the next
302+ for i , pf := range pushFactors {
303+ if pf .Id == selectedFactor .Id {
304+ pushFactors = append (pushFactors [:i ], pushFactors [i + 1 :]... )
305+ break
306+ }
307+ }
308+ if len (pushFactors ) > 0 {
309+ selectedFactor = pushFactors [0 ]
310+ // Reset result state for the new factor
311+ result .Status = "MFA_REQUIRED"
312+ result .FactorResult = ""
313+ goto verifyFactor
314+ }
315+ }
281316 return nil , logical .ErrorResponse ("multi-factor authentication denied" ), nil , nil
282317 case "TIMEOUT" :
318+ // If there are more push factors, try the next one
319+ if len (pushFactors ) > 1 && selectedFactor .Type == mfaPushMethod {
320+ for i , pf := range pushFactors {
321+ if pf .Id == selectedFactor .Id {
322+ pushFactors = append (pushFactors [:i ], pushFactors [i + 1 :]... )
323+ break
324+ }
325+ }
326+ if len (pushFactors ) > 0 {
327+ selectedFactor = pushFactors [0 ]
328+ result .Status = "MFA_REQUIRED"
329+ result .FactorResult = ""
330+ goto verifyFactor
331+ }
332+ }
283333 return nil , logical .ErrorResponse ("failed to complete multi-factor authentication" ), nil , nil
284334 case "SUCCESS" :
285335 // Allowed
0 commit comments