in pkg/provider/googleapps/googleapps.go [321:452]
func (kc *Client) loadChallengePage(submitURL string, referer string, authForm url.Values, loginDetails *creds.LoginDetails) (*goquery.Document, error) {
req, err := http.NewRequest("POST", submitURL, strings.NewReader(authForm.Encode()))
if err != nil {
return nil, errors.Wrap(err, "error retrieving login form")
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept-Language", "en-US")
req.Header.Set("Content-Language", "en-US")
req.Header.Set("Referer", referer)
res, err := kc.client.Do(req)
if err != nil {
return nil, errors.Wrap(err, "failed to make request to login form")
}
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
return nil, errors.Wrap(err, "error parsing login page html document")
}
doc.Url, err = url.Parse(submitURL)
if err != nil {
return nil, errors.Wrap(err, "failed to define URL for html doc")
}
errMsg := mustFindErrorMsg(doc)
if errMsg != "" {
return nil, errors.New("Invalid username or password")
}
secondFactorHeader := "This extra step shows it’s really you trying to sign in"
secondFactorHeader2 := "This extra step shows that it’s really you trying to sign in"
secondFactorHeaderJp := "2 段階認証プロセス"
// have we been asked for 2-Step Verification
if extractNodeText(doc, "h2", secondFactorHeader) != "" ||
extractNodeText(doc, "h2", secondFactorHeader2) != "" ||
extractNodeText(doc, "h1", secondFactorHeaderJp) != "" {
responseForm, secondActionURL, err := extractInputsByFormID(doc, "challenge")
if err != nil {
return nil, errors.Wrap(err, "unable to extract challenge form")
}
logger.Debugf("secondActionURL: %s", secondActionURL)
switch {
case strings.Contains(secondActionURL, "challenge/totp/"): // handle TOTP challenge
var token = loginDetails.MFAToken
if token == "" {
token = prompter.RequestSecurityCode("000000")
}
responseForm.Set("Pin", token)
responseForm.Set("TrustDevice", "on") // Don't ask again on this computer
return kc.loadResponsePage(secondActionURL, submitURL, responseForm)
case strings.Contains(secondActionURL, "challenge/ipp/"): // handle SMS challenge
var token = prompter.StringRequired("Enter SMS token: G-")
responseForm.Set("Pin", token)
responseForm.Set("TrustDevice", "on") // Don't ask again on this computer
return kc.loadResponsePage(secondActionURL, submitURL, responseForm)
case strings.Contains(secondActionURL, "challenge/sk/"): // handle u2f challenge
facetComponents, err := url.Parse(secondActionURL)
if err != nil {
return nil, errors.Wrap(err, "unable to parse action URL for U2F challenge")
}
facet := facetComponents.Scheme + "://" + facetComponents.Host
challengeNonce := responseForm.Get("id-challenge")
appID, data := extractKeyHandles(doc, challengeNonce)
u2fClient, err := NewU2FClient(challengeNonce, appID, facet, data[0], &U2FDeviceFinder{})
if err != nil {
return nil, errors.Wrap(err, "Failed to prompt for second factor.")
}
response, err := u2fClient.ChallengeU2F()
if err != nil {
errors.Wrap(err, "Second factor failed.")
return kc.skipChallengePage(doc, submitURL, secondActionURL, loginDetails)
}
responseForm.Set("id-assertion", response)
responseForm.Set("TrustDevice", "on")
return kc.loadResponsePage(secondActionURL, submitURL, responseForm)
case strings.Contains(secondActionURL, "challenge/az/"): // handle phone challenge
dataAttrs := extractDataAttributes(doc, "div[data-context]", []string{"data-context", "data-gapi-url", "data-tx-id", "data-api-key", "data-tx-lifetime"})
logger.Debugf("prompt with data values: %+v", dataAttrs)
waitValues := map[string]string{
"txId": dataAttrs["data-tx-id"],
}
fmt.Println("Open the Google App, and tap 'Yes' on the prompt to sign in")
_, err := kc.postJSON(fmt.Sprintf("https://content.googleapis.com/cryptauth/v1/authzen/awaittx?alt=json&key=%s", dataAttrs["data-api-key"]), waitValues, submitURL)
if err != nil {
return nil, errors.Wrap(err, "unable to extract post wait tx form")
}
// responseForm.Set("Pin", token)
responseForm.Set("TrustDevice", "on") // Don't ask again on this computer
return kc.loadResponsePage(secondActionURL, submitURL, responseForm)
case strings.Contains(secondActionURL, "challenge/skotp/"): // handle one-time HOTP challenge
fmt.Println("Get a one-time code by visiting https://g.co/sc on another device where you can use your security key")
var token = prompter.RequestSecurityCode("000 000")
responseForm.Set("Pin", token)
responseForm.Set("TrustDevice", "on") // Don't ask again on this computer
return kc.loadResponsePage(secondActionURL, submitURL, responseForm)
}
return kc.skipChallengePage(doc, submitURL, secondActionURL, loginDetails)
}
return doc, nil
}