Skip to content

Instantly share code, notes, and snippets.

@ochen1
Last active November 6, 2021 22:13
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ochen1/0d00e8e18daf03fba2f2d6bc7f6b7d3b to your computer and use it in GitHub Desktop.
Save ochen1/0d00e8e18daf03fba2f2d6bc7f6b7d3b to your computer and use it in GitHub Desktop.
Google Login
from signal import pause
from rich.console import Console
from google_login import *
console = Console()
console.log("Initializing ChromeOptions...")
options = webdriver.ChromeOptions()
options.headless = False
options.add_argument("--no-sandbox")
options.add_argument("--disable-gpu")
options.add_argument("--disable-dev-shm-usage")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
def get_user_details(driver, google_login):
console.log("Navigating to user account details webpage...")
with google_login.wait_for_load():
driver.get("https://myaccount.google.com/")
assert urlparse(driver.current_url).hostname == "myaccount.google.com"
console.log("Collecting data...")
user_detail_data = {
"IJ_values": {
62: "full_name",
63: "email",
64: "gender",
65: "first_name",
73: "profile_picture"
}
}
data = {}
for index, name in user_detail_data["IJ_values"].items():
content = driver.execute_script("return window.IJ_values[{}]".format(index))
data[name] = content
return data
console.log("Starting Chrome...")
with webdriver.Chrome(options=options) as chrome:
chrome.get("about:blank")
google_login = GoogleLogin(chrome, console=console)
#console.log("Checking login status...")
#if google_login.user_is_logged_in():
# pass
#else:
# console.log(":warning: Not yet logged in. Logging in...", style="yellow")
# google_login.perform_google_login()
google_login.perform_google_login()
console.log("Getting user details...")
console.print(get_user_details(chrome, google_login))
console.log("Google login complete.", style="bold")
pause()
"""
Google Login by Oliver Chen, 2021
https://gist.github.com/ochen1/0d00e8e18daf03fba2f2d6bc7f6b7d3b
"""
from base64 import b64decode
from contextlib import contextmanager
from json import load
from random import uniform
from time import sleep
from urllib.parse import urlparse, parse_qs
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.expected_conditions import staleness_of
from selenium.webdriver.support.ui import WebDriverWait
from selenium_stealth import stealth
class GoogleLogin:
def __init__(self, driver: webdriver.chrome.webdriver.WebDriver, config=None, config_filename=None, console=None):
self.console = console
if config is not None:
self.config = config
else:
self.config = self.parse_config(config_filename=config_filename)
self.driver = driver
self.apply_stealth()
def parse_config(self, config_filename=None):
if config_filename is None:
config_filename = "config.json"
self.console and self.console.log("Parsing config...")
with open(config_filename) as config:
config = load(config)
config['google_account']['username'] = b64decode(config['google_account']['username']).decode()
config['google_account']['password'] = b64decode(config['google_account']['password']).decode()
return config
@staticmethod
def _randomly_wait(*args):
return sleep(round(uniform(*map(float, args)), 2))
@contextmanager
def wait_for_load(self, timeout=10):
old_page = self.driver.find_element_by_tag_name("html")
yield
WebDriverWait(self.driver, timeout).until(staleness_of(old_page))
sleep(1)
def apply_stealth(self):
self.console and self.console.log("Applying stealth...")
stealth(self.driver,
languages=["en-US", "en"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
)
def perform_google_login(self):
self.console and self.console.log("Navigating to login page...")
with self.wait_for_load():
self.driver.get("https://accounts.google.com/ServiceLogin")
self._randomly_wait(0.5, 2.2)
self.console and self.console.log("Inputting username...")
self._randomly_wait(1.5, 2.2)
self.driver.find_element_by_css_selector("input#identifierId[name='identifier']"). \
send_keys(self.config['google_account'].get('username'), Keys.ENTER)
sleep(2)
self.console and self.console.log("Inputting password...")
self._randomly_wait(1.5, 2.5)
self.driver.find_element_by_css_selector("input[type='password'][name='password']"). \
send_keys(self.config['google_account'].get('password'), Keys.ENTER)
self.console and self.console.log("Waiting for Javascript login...")
sleep(5)
self.console and self.console.log("Checking login status...")
with self.wait_for_load():
self.driver.get("https://myaccount.google.com/?utm_source=sign_in_no_continue")
self._randomly_wait(1, 2)
self.driver.get("about:blank")
self.console and self.console.log("Logged in.", style="bold")
@KuroSol
Copy link

KuroSol commented Jan 27, 2021

can you please upload config.json file sample as well
I just made it by my self and i got
File "/Users/apple/PycharmProjects/google-login/google_login.py", line 35, in parse_config
config["google_account"]["username"] = b64decode(config["google_account"]["username"]).decode()
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/base64.py", line 87, in b64decode
return binascii.a2b_base64(s)
binascii.Error: Incorrect padding
this error

@ochen1
Copy link
Author

ochen1 commented Jan 28, 2021

@KuroSol I'm so sorry, I forgot to describe the config! 😅

I originally uploaded this in a rush for the conversation here: https://gist.github.com/ikegami-yukino/51b247080976cb41fe93

Here is an example config.json file.

{
    "headless": false,
    "google_account": {
        "username": "base64-encoded username",
        "password": "base64-encoded password"
    }
}

You can base64-encode your strings with:

from base64 import b64encode
print(b64encode(b"username@gmail.com"))
print(b64encode(b"password12345!Password!!"))

Please note that base64 encodings can be easily reversed, and is NOT a substitution for encryption. You should always store passwords used in production encrypted (if not hashed, as would not be possible in this case).

@ochen1
Copy link
Author

ochen1 commented Jan 28, 2021

I found the reason it wasn't working for you guys. I implemented the apply_stealth method but forgot to call it 🤦‍♂️

I've also added a sample config.json as a comment.

It should work now if you correctly generate your config.json and run the bot with no exception thrown from base64 decoding.

Make sure you have installed the requirements.

pip install rich selenium-stealth

If you're on *nix, you might need to use pip3.

Some mentions:
@KuroSol @jmzg1229

Can you guys check if it works for you now?

@ochen1
Copy link
Author

ochen1 commented Jan 28, 2021

I think we're doing debugging right now:

  1. Bugs are reported
  2. That's weird, it worked on my machine. 😕
  3. I found bug: I forgot to call the stealth method
  4. How did that ever work on my machine??

The loop repeats. The bug still persists as reported, for some unknown reason 🤔

I'm going to test using another account.

@adv-zl
Copy link

adv-zl commented Jan 29, 2021

OSX catalina, ChromeDriver 88.0.4324.96, not working

@ochen1
Copy link
Author

ochen1 commented Jan 29, 2021

@adv-zl
Hi! This is indeed not working as of now.

If it works for anyone, it most likely either means they are using a G-Suite account or are very lucky.

I've developed another proof of concept that works as of now, but I need to find a good way of packaging it and then distributing it.

@bobweasel
Copy link

@ochen1 what is your discord?

@ochen1
Copy link
Author

ochen1 commented May 16, 2021

My Discord is idontknow#8950 , but this example is completely outdated and will not work.

I plan on deleting this thread soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment