forked from Versent/saml2aws
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add JumpCloud Protect (PUSH) MFA support
JumpCloud have released a new app that they call [JumpCloud Protect](https://jumpcloud.com/press/introducing-jumpcloud-protect-free-mobile-multi-factor-authentication). It is a free alternative to DUO for JumpCoud users that provides "push" second factor. It would be useful to add this in if possible (we are planning to use it).
- Loading branch information
Murat Mukhtarov
committed
Sep 8, 2021
1 parent
f3d590d
commit 1ffd06b
Showing
4 changed files
with
139 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package jumpcloud | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"net/http" | ||
"net/url" | ||
"path" | ||
"time" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
func (jc *Client) jumpCloudProtectAuth(xsrfToken string) (*http.Response, error) { | ||
jumpCloudParsedURL, err := url.Parse(jumpCloudProtectSubmitURL) | ||
if err != nil { | ||
return nil, errors.Wrap(err, fmt.Sprintf("unable to parse submit url, url=%s", jumpCloudProtectSubmitURL)) | ||
} | ||
|
||
req, err := http.NewRequest("POST", jumpCloudParsedURL.String(), emptyJSONIOReader()) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "error building jumpcloud protect auth request") | ||
} | ||
ensureHeaders(xsrfToken, req) | ||
|
||
res, err := jc.client.Do(req) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "error retrieving JumpCloud PUSH configuration") | ||
} | ||
defer res.Body.Close() | ||
|
||
if res.StatusCode != 200 { | ||
return nil, errors.New("error retrieving JumpCloud PUSH configuration, non 200 status returned") | ||
} | ||
|
||
jpResp, err := ioutil.ReadAll(res.Body) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "error retrieving Duo configuration") | ||
} | ||
|
||
type JP struct { | ||
ID string `json:"id"` | ||
ExpiresAt time.Time `json:"expiresAt"` | ||
InitiatedAt time.Time `json:"initiatedAt"` | ||
Status string `json:"status"` | ||
UserId string `json:"userId"` | ||
} | ||
|
||
jp := JP{} | ||
if err := json.Unmarshal(jpResp, &jp); err != nil { | ||
return nil, errors.Wrap(err, "failed to unmarshal JumpCloud Protect configuration") | ||
} | ||
|
||
jumpCloudParsedURL.Path = path.Join(jumpCloudParsedURL.Path, jp.ID) | ||
req, err = http.NewRequest("GET", jumpCloudParsedURL.String(), nil) | ||
|
||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to build JumpCoud push polling request") | ||
} | ||
|
||
ensureHeaders(xsrfToken, req) | ||
|
||
// Stay in the loop until we get something else other than "pending". | ||
// jp.Status can be: | ||
// * accepted | ||
// * expired | ||
// * denied | ||
|
||
for jp.Status == "pending" { | ||
time.Sleep(100 * time.Millisecond) | ||
|
||
resp, err := jc.client.Do(req) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "error retrieving verify response") | ||
} | ||
defer resp.Body.Close() | ||
|
||
bytes, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to unmarshal JumpCloud Protect body") | ||
} | ||
|
||
if err := json.Unmarshal(bytes, &jp); err != nil { | ||
return nil, errors.Wrap(err, "failed to unmarshal poll result json into struct") | ||
} | ||
} | ||
|
||
if jp.Status != "accepted" { | ||
return nil, errors.New(fmt.Sprintf("didn't receive accepted, status=%s\n", jp.Status)) | ||
} | ||
|
||
jumpCloudParsedURL.Path = path.Join(jumpCloudParsedURL.Path, "login") | ||
req, err = http.NewRequest("POST", jumpCloudParsedURL.String(), emptyJSONIOReader()) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to build JumpCoud login request") | ||
} | ||
|
||
ensureHeaders(xsrfToken, req) | ||
|
||
return jc.client.Do(req) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package jumpcloud | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"net/http" | ||
) | ||
|
||
func ensureHeaders(xsrfToken string, req *http.Request) { | ||
req.Header.Add("X-Xsrftoken", xsrfToken) | ||
req.Header.Add("Accept", "application/json") | ||
req.Header.Add("Content-Type", "application/json") | ||
} | ||
|
||
func emptyJSONIOReader() io.Reader { | ||
return bytes.NewReader([]byte(`{}`)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters