func()

in pkg/provider/googleapps/googleapps.go [47:162]


func (kc *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error) {

	// Get the first page
	authURL, authForm, err := kc.loadFirstPage(loginDetails)
	if err != nil {
		return "", errors.Wrap(err, "error loading first page")
	}

	authForm.Set("Email", loginDetails.Username)

	passwordURL, passwordForm, err := kc.loadLoginPage(authURL+"?hl=en&loc=US", loginDetails.URL+"&hl=en&loc=US", authForm)
	if err != nil {
		return "", errors.Wrap(err, "error loading login page")
	}

	logger.Debugf("loginURL: %s", passwordURL)

	authForm.Set("Passwd", loginDetails.Password)

	referingURL := passwordURL

	if _, rawIdPresent := passwordForm["rawidentifier"]; rawIdPresent {
		authForm.Set("rawidentifier", loginDetails.Username)
		referingURL = authURL
	}

	if v, tlPresent := passwordForm["TL"]; tlPresent {
		authForm.Set("TL", v[0])
	}
	if v, gxfPresent := passwordForm["gxf"]; gxfPresent {
		authForm.Set("gxf", v[0])
	}

	responseDoc, err := kc.loadChallengePage(passwordURL+"?hl=en&loc=US", referingURL, authForm, loginDetails)
	if err != nil {
		return "", errors.Wrap(err, "error loading challenge page")
	}

	captchaInputIds := []string{
		"logincaptcha",
		"identifier-captcha-input",
	}

	var captchaFound *goquery.Selection
	var captchaInputId string

	for _, v := range captchaInputIds {
		captchaFound = responseDoc.Find(fmt.Sprintf("#%s", v))
		if captchaFound != nil && captchaFound.Length() > 0 {
			captchaInputId = v
			break
		}
	}

	for captchaFound != nil && captchaFound.Length() > 0 {
		captchaImgDiv := responseDoc.Find(".captcha-img")
		captchaPictureSrc, found := goquery.NewDocumentFromNode(captchaImgDiv.Children().Nodes[0]).Attr("src")

		if !found {
			return "", errors.New("captcha image not found but requested")
		}

		captchaPictureURL, err := generateFullURLIfRelative(captchaPictureSrc, passwordURL)
		if err != nil {
			return "", errors.Wrap(err, "error generating captcha image URL")
		}

		captcha, err := kc.tryDisplayCaptcha(captchaPictureURL)
		if err != nil {
			return "", err
		}

		captchaForm, captchaURL, err := extractInputsByFormID(responseDoc, "gaia_loginform", "challenge")
		if err != nil {
			return "", errors.Wrap(err, "error extracting captcha")
		}

		logger.Debugf("captchaURL: %s", captchaURL)

		_, captchaV1 := captchaForm["Passwd"]
		if captchaV1 {
			captchaForm.Set("Passwd", loginDetails.Password)
		}
		captchaForm.Set(captchaInputId, captcha)

		responseDoc, err = kc.loadChallengePage(captchaURL+"?hl=en&loc=US", captchaURL, captchaForm, loginDetails)
		if err != nil {
			return "", errors.Wrap(err, "error loading challenge page")
		}

		captchaFound = responseDoc.Find(fmt.Sprintf("#%s", captchaInputId))
	}

	// New Captcha proceeds back to password page
	passworddBeingRequested := responseDoc.Find("#password")
	if passworddBeingRequested != nil && passworddBeingRequested.Length() > 0 {
		loginForm, loginURL, err := extractInputsByFormID(responseDoc, "challenge")
		if err != nil {
			return "", errors.Wrap(err, "error parsing password page after captcha")
		}

		loginForm.Set("Passwd", loginDetails.Password)

		responseDoc, err = kc.loadChallengePage(loginURL+"?hl=en&loc=US", loginURL, loginForm, loginDetails)
		if err != nil {
			return "", errors.Wrap(err, "error loading challenge page")
		}
	}

	samlAssertion := mustFindInputByName(responseDoc, "SAMLResponse")
	if samlAssertion == "" {
		return "", errors.New("page is missing saml assertion")
	}

	return samlAssertion, nil
}