diff --git a/pkg/provider/okta/okta.go b/pkg/provider/okta/okta.go index 65978ca50..52690b2f3 100644 --- a/pkg/provider/okta/okta.go +++ b/pkg/provider/okta/okta.go @@ -36,6 +36,7 @@ import ( const ( IdentifierDuoMfa = "DUO WEB" IdentifierSmsMfa = "OKTA SMS" + IdentifierEmailMfa = "OKTA EMAIL" IdentifierPushMfa = "OKTA PUSH" IdentifierTotpMfa = "GOOGLE TOKEN:SOFTWARE:TOTP" IdentifierOktaTotpMfa = "OKTA TOKEN:SOFTWARE:TOTP" @@ -50,6 +51,7 @@ var ( supportedMfaOptions = map[string]string{ IdentifierDuoMfa: "DUO MFA authentication", IdentifierSmsMfa: "SMS MFA authentication", + IdentifierEmailMfa: "EMAIL MFA authentication", IdentifierPushMfa: "PUSH MFA authentication", IdentifierTotpMfa: "TOTP MFA authentication", IdentifierOktaTotpMfa: "Okta MFA authentication", @@ -811,7 +813,7 @@ func verifyMfa(oc *Client, oktaOrgHost string, loginDetails *creds.LoginDetails, switch mfa := challengeContext.mfaIdentifer; mfa { case IdentifierYubiMfa: return gjson.Get(challengeContext.challengeResponseBody, "sessionToken").String(), nil - case IdentifierSmsMfa, IdentifierTotpMfa, IdentifierOktaTotpMfa, IdentifierSymantecTotpMfa: + case IdentifierSmsMfa, IdentifierEmailMfa, IdentifierTotpMfa, IdentifierOktaTotpMfa, IdentifierSymantecTotpMfa: var verifyCode = loginDetails.MFAToken if verifyCode == "" { verifyCode = prompter.RequestSecurityCode("000000") diff --git a/pkg/provider/okta/okta_test.go b/pkg/provider/okta/okta_test.go index c55499f61..06e8cd4f7 100644 --- a/pkg/provider/okta/okta_test.go +++ b/pkg/provider/okta/okta_test.go @@ -267,6 +267,54 @@ func TestVerifyMfa(t *testing.T) { }) } +func TestVerifyMfa_Email(t *testing.T) { + + ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/verify": + _, err := w.Write([]byte(`{ + "sessionToken": "TOKEN_3", + "status": "SUCCESS" + }`)) + assert.Nil(t, err) + + default: + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + } + })) + defer ts.Close() + + t.Run("Email", func(t *testing.T) { + oc, loginDetails := setupTestClient(t, ts, "EMAIL") + + err := oc.setDeviceTokenCookie(loginDetails) + assert.Nil(t, err) + + var out bytes.Buffer + log.SetOutput(&out) + context, err := verifyMfa(oc, "", &creds.LoginDetails{ + MFAToken: "123456", + }, fmt.Sprintf(`{ + "stateToken": "TOKEN_1", + "_embedded": { + "factors": [ + { + "id": "EMAIL", + "provider": "OKTA", + "factorType": "EMAIL", + "_links": { + "verify": { "href": "%s/verify" } + } + } + ] + } + }`, ts.URL)) + log.SetOutput(os.Stderr) + assert.Nil(t, err) + assert.Equal(t, context, "TOKEN_3") + }) +} + func TestVerifyMfa_Duo(t *testing.T) { t.Run("Duo Push", func(t *testing.T) { ts := setupTestDuoHttpServer(t, "Duo Push") diff --git a/saml2aws.go b/saml2aws.go index 670e79f5d..e3b30b9fd 100644 --- a/saml2aws.go +++ b/saml2aws.go @@ -40,8 +40,8 @@ var MFAsByProvider = ProviderList{ "PingNTLM": []string{"Auto"}, // automatically detects PingID "PingOne": []string{"Auto"}, // automatically detects PingID "JumpCloud": []string{"Auto", "TOTP", "WEBAUTHN", "DUO", "PUSH"}, - "Okta": []string{"Auto", "PUSH", "DUO", "SMS", "TOTP", "OKTA", "FIDO", "YUBICO TOKEN:HARDWARE", "SYMANTEC"}, // automatically detects DUO, SMS, ToTP, and FIDO - "OneLogin": []string{"Auto", "OLP", "SMS", "TOTP", "YUBIKEY", "DUO TOTP"}, // automatically detects OneLogin Protect, SMS and ToTP + "Okta": []string{"Auto", "PUSH", "DUO", "SMS", "EMAIL", "TOTP", "OKTA", "FIDO", "YUBICO TOKEN:HARDWARE", "SYMANTEC"}, // automatically detects DUO, SMS, ToTP, and FIDO + "OneLogin": []string{"Auto", "OLP", "SMS", "TOTP", "YUBIKEY", "DUO TOTP"}, // automatically detects OneLogin Protect, SMS and ToTP "Authentik": []string{"Auto"}, "KeyCloak": []string{"Auto"}, // automatically detects ToTP "GoogleApps": []string{"Auto"}, // automatically detects ToTP