mintapi / mintapi

an unofficial screen-scraping "API" for Mint.com
MIT License
1.21k stars 275 forks source link

"challenge required" error #110

Closed dzg closed 6 years ago

dzg commented 7 years ago

All was working great; today Mint forced me to change password.

I updated password, but mintapi says mintapi.api.MintException: Challenge required, please log in to Mint.com manually and complete the captcha.

I re-logged in in Chrome, got the email verification code, confirmed, etc., then copied values for ius_session from accounts.intuit.com cookies and thx_guid from pf.intuit.com cookies.

But I still get the same error.

(I was never able to get selenium to work.)

tnoetzel commented 7 years ago

Getting the same issue here... wasn't forced to change my password. Guessing mint changed something on their end...

flarco commented 7 years ago

Same issue... didn't change password.

jorythompson commented 7 years ago

FIXED! Me too (no password change.) I tried changed the ius_session and thx_guid cookies to match my browser's and that fixed it.

tnoetzel commented 7 years ago

I tried changing the ius_session and thx_guid cookie items to match my browser again (hours later) and it now works... definitely didn't before.

Any idea what caused this?

dzg commented 7 years ago

It started working again, for a few hours. Now I'm getting a different error: Session has expired.

The authentication works (mintapi.Mint(email, password, ius_session, thx_guid), but I cannot pull any data, except accounts, and that only some of the time, and only from shell mintapi. And sometimes i just get null response.

I have a feeling Mint is tweaking stuff on their end.

Andreilys commented 7 years ago

If I keep running my script eventually it will go through. I think a potential solution would be to restart the script whenever the challenge required exception is raised (or any other ones), only problem I can see is it turns into an infinite loop however from the small testing i've done it seems that it goes through.

        while result is None:
            try:
                self.mint_trans = self.mint.get_transactions_json(include_investment=False, skip_duplicates=False)
                result = True
            except Exception as e:
                os.execv(sys.executable, ['python'] + sys.argv)

Would an implementation like this make sense in the mintapi project so we can abstract away the hacky-ness of my solution?

alexcstark commented 7 years ago

I get this error whenever I don't input my ius_session and thx_guid...and when I do input those, I get mintapi.api.MintException: Mint.com login failed[1]. As far as I can tell, my Selenium and chromedriver work together properly.

jorythompson commented 7 years ago

I am getting this pretty consisitenly: 2017-09-25 23:43:44,371 - main -CRITICAL - MintException: Could not parse account data: <error><code>1</code><description>Session has expired.</description><name></name><type></type></error> Even though I have updated my ius_session and thx_guid cookies.

Has Intuit changed something?

jorythompson commented 7 years ago

I got tired of updating these cookies so I hacked this together (works with the current Chrome browser and there are calls to Firefox as well) to extract the appropriate cookies and update my configuration file (change the replace_value function to do what you like).

import ConfigParser
import browsercookie

class MintCookies:
    def __init__(self):
        self.cookies = [{"url": "accounts.intuit.com", "cookie": "ius_session", "value": None},
                        {"url": "pf.intuit.com", "cookie": "thx_guid", "value": None}]
        self.cookie_jar = browsercookie.chrome()

    def find_cookie(self, url, cookie):
        for c in self.cookie_jar._cookies:
            if c == unicode(url):
                c = self.cookie_jar._cookies[c]
                val = c[unicode("/")]
                try:
                    val = val[unicode(cookie)]
                except KeyError:
                    return None
                return val.value
        return None

    def find_cookies(self):
        for c in self.cookies:
            c["value"] = self.find_cookie(c["url"], c["cookie"])
        return self.cookies

    @staticmethod
    def replace_value(config_file, key, value):
        config = ConfigParser.ConfigParser()
        config.optionxform = str
        config.read(config_file)
        old_value = config.get("mint connection", key)
        f = open(config_file)
        contents = f.read()
        f.close()
        contents = contents.replace(key + ":" + old_value, key + ":" + value)
        f = open(config_file, "w")
        f.write(contents)
        print "changing {} to {}".format(old_value, value)

def main():
    mc = MintCookies()
    cookies = mc.find_cookies()
    for c in cookies:
        MintCookies.replace_value("home.ini", c["cookie"] + "_cookie", c["value"])

if __name__ == "__main__":
    main()

Note there is a change you need to make to browsercookie (the tempfile.NamedTemporaryFile function has a known limitation on Windows) if you are windows:

@contextmanager
def create_local_copy(cookie_file):
    """Make a local copy of the sqlite cookie database and return the new filename.
    This is necessary in case this database is still being written to while the user browses
    to avoid sqlite locking errors.
    """
    # check if cookie file exists
    if os.path.exists(cookie_file):
        # copy to random name in tmp folder
        t_cookie_file = tempfile.NamedTemporaryFile(suffix='.sqlite', mode="wb", delete=False)
        t_cookie_file.write(open(cookie_file, 'rb').read())
        tmp_cookie_file = t_cookie_file.name
        t_cookie_file.close()
        yield tmp_cookie_file
    else:
        raise BrowserCookieError('Can not find cookie file at: ' + cookie_file)

    os.remove(tmp_cookie_file)
dzg commented 7 years ago

@jorythompson Wow, this is great.

However, I just re-logged into Mint, and I do not see accounts.intuit.com or pf.intuit.com in Cookies; and no ius_session or thx_guid cookies.

The only 'session' type cookie I see is MINTJSESSIONID from mint.intuit.com

Is it just me?

jprouty commented 6 years ago

Hi all, can you try with a clean copy that includes #120 ? Hopefully this will solve your issue.

dzg commented 6 years ago

It's working for me now with selenium. Yay!

However, every time I do a mint = mintapi.Mint(email, password) (or even launch it from shell) it pops up an automated Chrome instance to login (even with keyring).

But I am using mintapi for a bitbar widget so that it runs every hour and keeps my Mint info in my menubar. So it's not practical to have Chrome pop up every hour.

Can I store the session permanently some how so selenium doesn't pop up Chrome every time?

(When logged in, Chrome currently shows a thx_guid cookie for pf.intuit.com but no ius_session cookie and no accounts.intuit.com cookies at all.)

crclayton commented 6 years ago

My old script was providing a thx_guid and an ius_session. They must have become out of date, so I stopped passing them as arguments and now my script works again.

Doesn't work:

mint = mintapi.Mint(email, password, ius_session, thx_guid)

Works:

mint = mintapi.Mint(email, password)
mrooney commented 6 years ago

Closing after multiple confirmations of working, thanks!

toms3t commented 5 years ago

Has anyone found a solid workaround for the 2FA authentication pop-up? I'd like to automate the process of pulling data and building a report weekly but 2FA is preventing this.

mrooney commented 5 years ago

Has anyone found a solid workaround for the 2FA authentication pop-up? I'd like to automate the process of pulling data and building a report weekly but 2FA is preventing this.

The latest version has session caching so that this should be avoided in most cases, after the first one. Give it a shot @toms3t and let me know if that helps :)