func()

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

}