diff --git a/pkg/provider/pingfed/example/swipe-number.html b/pkg/provider/pingfed/example/swipe-number.html new file mode 100644 index 000000000..d80475e8c --- /dev/null +++ b/pkg/provider/pingfed/example/swipe-number.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + +
+
+
+

+ Authentication +

+

+ Select the number displayed in your PingID mobile app +

+
+
10
+
+
+ Authenticating on +
+ iPhone X +
+
+ Change Device +
+
+
corporate motd
+ + +
+ + +
+
+ + + +
+
+ +
+
+ + + +
+ + + + +
+ + diff --git a/pkg/provider/pingfed/pingfed.go b/pkg/provider/pingfed/pingfed.go index 8618af0af..718534f04 100644 --- a/pkg/provider/pingfed/pingfed.go +++ b/pkg/provider/pingfed/pingfed.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "fmt" "io" + "log" "net/http" "net/url" "time" @@ -172,6 +173,10 @@ func (ac *Client) handleSwipe(ctx context.Context, doc *goquery.Document, _ *url return ctx, nil, errors.Wrap(err, "error extracting swipe status form") } + if number := doc.Find("div.numbermatching").Text(); number != "" { + log.Printf("Select %v in your PingID mobile app ...\n", number) + } + // poll status. request must specifically be a GET form.Method = "GET" req, err := form.BuildRequest() diff --git a/pkg/provider/pingfed/pingfed_test.go b/pkg/provider/pingfed/pingfed_test.go index 4feb78760..460edd65e 100644 --- a/pkg/provider/pingfed/pingfed_test.go +++ b/pkg/provider/pingfed/pingfed_test.go @@ -3,9 +3,12 @@ package pingfed import ( "bytes" "context" + "crypto/tls" "io" + "log" "net/http" "net/http/cookiejar" + "net/http/httptest" "net/url" "os" "testing" @@ -33,26 +36,31 @@ var docTests = []struct { {docIsLogin, "example/login2.html", true}, {docIsLogin, "example/otp.html", false}, {docIsLogin, "example/swipe.html", false}, + {docIsLogin, "example/swipe-number.html", false}, {docIsLogin, "example/form-redirect.html", false}, {docIsLogin, "example/webauthn.html", false}, {docIsOTP, "example/login.html", false}, {docIsOTP, "example/otp.html", true}, {docIsOTP, "example/swipe.html", false}, + {docIsOTP, "example/swipe-number.html", false}, {docIsOTP, "example/form-redirect.html", false}, {docIsOTP, "example/webauthn.html", false}, {docIsSwipe, "example/login.html", false}, {docIsSwipe, "example/otp.html", false}, {docIsSwipe, "example/swipe.html", true}, + {docIsSwipe, "example/swipe-number.html", true}, {docIsSwipe, "example/form-redirect.html", false}, {docIsSwipe, "example/webauthn.html", false}, {docIsFormRedirect, "example/login.html", false}, {docIsFormRedirect, "example/otp.html", false}, {docIsFormRedirect, "example/swipe.html", false}, + {docIsFormRedirect, "example/swipe-number.html", false}, {docIsFormRedirect, "example/form-redirect.html", true}, {docIsFormRedirect, "example/webauthn.html", false}, {docIsWebAuthn, "example/login.html", false}, {docIsWebAuthn, "example/otp.html", false}, {docIsWebAuthn, "example/swipe.html", false}, + {docIsWebAuthn, "example/swipe-number.html", false}, {docIsWebAuthn, "example/form-redirect.html", false}, {docIsWebAuthn, "example/webauthn.html", true}, } @@ -135,6 +143,59 @@ func TestHandleOTP(t *testing.T) { require.Contains(t, s, "csrfToken=some-token") } +func TestHandleSwipe(t *testing.T) { + ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/pingid/ppm/auth/status": + _, err := w.Write([]byte("{\"status\":\"OK\"}")) + require.Nil(t, err) + default: + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + } + })) + defer ts.Close() + + performTest := func(data []byte) bytes.Buffer { + doc, err := goquery.NewDocumentFromReader(bytes.NewReader(bytes.ReplaceAll(data, []byte("https://authenticator.pingone.com"), []byte(ts.URL)))) + require.Nil(t, err) + + testTransport := http.DefaultTransport.(*http.Transport).Clone() + testTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + ac := Client{ + client: &provider.HTTPClient{Client: http.Client{Transport: testTransport}, Options: &provider.HTTPClientOptions{IsWithRetries: false}}, + } + + var out bytes.Buffer + log.SetOutput(&out) + _, req, err := ac.handleSwipe(context.Background(), doc, &url.URL{}) + log.SetOutput(os.Stderr) + require.Nil(t, err) + + b, err := io.ReadAll(req.Body) + require.Nil(t, err) + + s := string(b[:]) + require.Contains(t, s, "csrfToken=abdb4264-6aab-4e1a-a830-63c9188e2395") + + return out + } + + t.Run("Swipe", func(t *testing.T) { + data, err := os.ReadFile("example/swipe.html") + require.Nil(t, err) + + performTest(data) + }) + + t.Run("Swipe with number", func(t *testing.T) { + data, err := os.ReadFile("example/swipe-number.html") + require.Nil(t, err) + + out := performTest(data) + require.Contains(t, out.String(), "Select 10 in your PingID mobile app ...") + }) +} + func TestHandleFormRedirect(t *testing.T) { data, err := os.ReadFile("example/form-redirect.html") require.Nil(t, err)