diff --git a/pkg/provider/pingfed/example/loginpage.html b/pkg/provider/pingfed/example/loginpage.html
new file mode 100644
index 000000000..d78f8a5db
--- /dev/null
+++ b/pkg/provider/pingfed/example/loginpage.html
@@ -0,0 +1,11 @@
+
+
+
+ Example Login Page
+
+
+
+
+
diff --git a/pkg/provider/pingfed/example/loginpage_absolute.html b/pkg/provider/pingfed/example/loginpage_absolute.html
new file mode 100644
index 000000000..6c00b8e2a
--- /dev/null
+++ b/pkg/provider/pingfed/example/loginpage_absolute.html
@@ -0,0 +1,11 @@
+
+
+
+ Example Login Page
+
+
+
+
+
diff --git a/pkg/provider/pingfed/pingfed.go b/pkg/provider/pingfed/pingfed.go
index bae7682d8..69a3edb00 100644
--- a/pkg/provider/pingfed/pingfed.go
+++ b/pkg/provider/pingfed/pingfed.go
@@ -283,21 +283,12 @@ func (ac *Client) getLoginForm(loginDetails *creds.LoginDetails) (string, url.Va
updateLoginFormData(authForm, s, loginDetails)
})
- authSubmitURL := ""
-
- doc.Find("form").Each(func(i int, s *goquery.Selection) {
- action, ok := s.Attr("action")
- if !ok {
- return
- }
- authSubmitURL = action
- })
-
- if authSubmitURL == "" {
+ authSubmitURL, err := extractAuthSubmitURL(loginDetails.URL, doc)
+ if err != nil {
return "", nil, fmt.Errorf("unable to locate IDP authentication form submit URL")
}
- return fmt.Sprintf("%s%s", loginDetails.URL, authSubmitURL), authForm, nil
+ return authSubmitURL, authForm, nil
}
func updateLoginFormData(authForm url.Values, s *goquery.Selection, user *creds.LoginDetails) {
@@ -321,6 +312,28 @@ func updateLoginFormData(authForm url.Values, s *goquery.Selection, user *creds.
}
}
+func extractAuthSubmitURL(baseURL string, doc *goquery.Document) (authSubmitURL string, err error){
+ doc.Find("form").Each(func(i int, s *goquery.Selection) {
+ action, ok := s.Attr("action")
+ if !ok {
+ return
+ }
+ authSubmitURL = action
+ })
+
+ if authSubmitURL == "" {
+ err = fmt.Errorf("unable to locate IDP authentication form submit URL")
+ return
+ }
+
+ // account for relative action URI
+ if url, urlErr := url.ParseRequestURI(authSubmitURL); urlErr == nil && !url.IsAbs() {
+ authSubmitURL = fmt.Sprintf("%s%s", baseURL, authSubmitURL)
+ }
+
+ return
+}
+
func extractFormData(res *http.Response) (url.Values, string, error) {
formData := url.Values{}
var actionURL string
diff --git a/pkg/provider/pingfed/pingfed_test.go b/pkg/provider/pingfed/pingfed_test.go
index 4591c1270..4c8a7ae4f 100644
--- a/pkg/provider/pingfed/pingfed_test.go
+++ b/pkg/provider/pingfed/pingfed_test.go
@@ -22,3 +22,25 @@ func TestExtractMfaFormData(t *testing.T) {
require.Equal(t, "https://authenticator.pingone.com/pingid/ppm/auth/poll", actionURL)
require.Equal(t, url.Values{"csrfToken": []string{"fc80998c-34d8-4dd2-925c-3b3be8a0dee8"}}, mfaForm)
}
+
+var extractAuthSubmitURLTests = []struct {
+ f string // input html file
+ expected string // expected url
+}{
+ {"example/loginpage.html", "https://example.com/relative/login"},
+ {"example/loginpage_absolute.html", "https://other.example.com/login"},
+}
+
+func TestExtractAuthSubmitURL(t *testing.T) {
+ for _, tt := range extractAuthSubmitURLTests {
+ data, err := ioutil.ReadFile(tt.f)
+ require.Nil(t, err)
+
+ doc, err := goquery.NewDocumentFromReader(bytes.NewReader(data))
+ require.Nil(t, err)
+
+ url, err := extractAuthSubmitURL("https://example.com", doc)
+ require.Nil(t, err)
+ require.Equal(t, tt.expected, url)
+ }
+}