Skip to content

Instantly share code, notes, and snippets.

@rithvikvibhu
Last active December 15, 2024 23:19
Show Gist options
  • Save rithvikvibhu/952f83ea656c6782fbd0f1645059055d to your computer and use it in GitHub Desktop.
Save rithvikvibhu/952f83ea656c6782fbd0f1645059055d to your computer and use it in GitHub Desktop.
Get tokens for Google Home Foyer API

Get tokens for Google Home Foyer API

Intro

This script (python 3) generates tokens that can be used when making requests to the Google Home Foyer API. There are 2 kinds of tokens used here:

  1. Master token - Is in the form aas_et/*** and is long lived. Needs Google username and password.
  2. Access token - Is in the form ya29.*** and lasts for an hour. Needs Master token to generate.

If you do not want to store the Google account password in plaintext, get the master token once, and set it as an override value.

It's safer/easier to generate an app password and use it instead of the actual password. It still has the same access as the regular password, but still better than using the real password while scripting. (https://myaccount.google.com/apppasswords)

Usage

# Install python requirements
pip install gpsoauth

# Update the constants at the beginning of the file

# Get the tokens!
python3 get_tokens.py
from gpsoauth import perform_master_login, perform_oauth
from uuid import getnode as getmac
# Creds to use when logging in
USERNAME = '<google_username>'
PASSWORD = '<google_password_or_app_password>'
# Optional Overrides (Set to None to ignore)
device_id = None
master_token = None
access_token = None
# Flags
DEBUG = False
def get_master_token(username, password, android_id):
res = perform_master_login(username, password, android_id)
if DEBUG:
print(res)
if 'Token' not in res:
print('[!] Could not get master token.')
return None
return res['Token']
def get_access_token(username, master_token, android_id):
res = perform_oauth(
username, master_token, android_id,
app='com.google.android.apps.chromecast.app',
service='oauth2:https://www.google.com/accounts/OAuthLogin',
client_sig='24bb24c05e47e0aefa68a58a766179d9b613a600'
)
if DEBUG:
print(res)
if 'Auth' not in res:
print('[!] Could not get access token.')
return None
return res['Auth']
def _get_android_id():
mac_int = getmac()
if (mac_int >> 40) % 2:
raise OSError("a valid MAC could not be determined."
" Provide an android_id (and be"
" sure to provide the same one on future runs).")
android_id = _create_mac_string(mac_int)
android_id = android_id.replace(':', '')
return android_id
def _create_mac_string(num, splitter=':'):
mac = hex(num)[2:]
if mac[-1] == 'L':
mac = mac[:-1]
pad = max(12 - len(mac), 0)
mac = '0' * pad + mac
mac = splitter.join([mac[x:x + 2] for x in range(0, 12, 2)])
mac = mac.upper()
return mac
if not device_id:
device_id = _get_android_id()
print('''
This script generates tokens that can be used when making requests to the Google Home Foyer API.
There are 2 kinds of tokens used here:
1. Master token - Is in the form `aas_et/***` and is long lived. Needs Google username and password.
2. Access token - Is in the form `ya29.***` and lasts for an hour. Needs Master token to generate.
If you do not want to store the Google account password in plaintext,
get the master token once, and set it as an override value.
It's safer/easier to generate an app password and use it instead of the actual password.
It still has the same access as the regular password, but still better than using the real password while scripting.
(https://myaccount.google.com/apppasswords)
''')
print('\n[*] Getting master token...')
if not master_token:
master_token = get_master_token(USERNAME, PASSWORD, device_id)
print('[*] Master token:', master_token)
print('\n[*] Getting access token...')
if not access_token:
access_token = get_access_token(USERNAME, master_token, device_id)
print('[*] Access token:', access_token)
print('\n[*] Done.')
@MauGianfa
Copy link

Hi,
Yes I did.... Any other idea?

@edx-sayed-salem
Copy link

@MauGianfa , I just tried this (from a few comments ago) and it's fine. Did you try it?

Thanks. I can confirm using specific version of packages like this works:

FROM python:3.9-slim-bullseye
RUN pip install "urllib3<2.0.0" "gpsoauth==1.0.2"
COPY ./get_tokens.py ./
CMD python3 get_tokens.py
docker build -t get_tokens . && docker run --rm -it get_tokens

@emmaly
Copy link

emmaly commented May 5, 2024

@MauGianfa , I just tried this (from a few comments ago) and it's fine. Did you try it?

Thanks. I can confirm using specific version of packages like this works:

FROM python:3.9-slim-bullseye
RUN pip install "urllib3<2.0.0" "gpsoauth==1.0.2"
COPY ./get_tokens.py ./
CMD python3 get_tokens.py
docker build -t get_tokens . && docker run --rm -it get_tokens

This worked great. Humorously enough, I was facing this issue in June 2023 (last year) as you can see in my previous comments on this gist, I came back here and found the solution that worked the fastest. Thank you!

@shayaantx
Copy link

shayaantx commented May 28, 2024

urllib3<2.0.0 seems to install 1.26.18 which doesn't work for me for some reason

hardcoding to 1.25.1 seems to work currently

@Coax88
Copy link

Coax88 commented Jun 3, 2024

I get this error

Traceback (most recent call last): File "//get_tokens.py", line 90, in <module> master_token = get_master_token(USERNAME, PASSWORD, device_id) File "//get_tokens.py", line 22, in get_master_token res = perform_master_login(username, password, android_id) File "/usr/local/lib/python3.9/site-packages/gpsoauth/__init__.py", line 155, in perform_master_login return _perform_auth_request(data, proxy) File "/usr/local/lib/python3.9/site-packages/gpsoauth/__init__.py", line 98, in _perform_auth_request res = session.post(AUTH_URL, data=data, verify=True) File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 637, in post return self.request("POST", url, data=data, json=json, **kwargs) File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 589, in request resp = self.send(prep, **send_kwargs) File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 703, in send r = adapter.send(request, **kwargs) File "/usr/local/lib/python3.9/site-packages/requests/adapters.py", line 698, in send raise SSLError(e, request=request) requests.exceptions.SSLError: HTTPSConnectionPool(host='android.clients.google.com', port=443): Max retries exceeded with url: /auth (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1133)')))

@tdeboss
Copy link

tdeboss commented Jun 4, 2024

I have the same error ad @Coax88

@Coax88
Copy link

Coax88 commented Jun 5, 2024

I tried with the docker instead now i get this, have tried login on the browser and run the script again, then it says the same

[*] Getting master token...
{'Error': 'NeedsBrowser', 'Url': 'https://accounts.google.com/signin/continue?sarp=1&scc=1&continue=https://accounts.google.com/o/android/auth?hl%3Den_us%26xoauth_display_name%3DAndroid%2BLogin%2BService%26source%3DAndroid%2BLogin&plt=AKgnsbuZ_w9LYv1eVbP_S1pKbz26IzgW1FHtqj9n_O5K0S2sMMlgsP8F85yrgPZQZdqoUBcUM32wl-rlhZnk1Ee4MyZ-HClGWMW1JwUdvqFHh50NsRhMo1w43dyh1mZkeqmMRBi4zp6j', 'ErrorDetail': 'To access your account, you must sign in on the web. Touch Next to start browser sign-in.'}
[!] Could not get master token.
[*] Master token: None

[*] Getting access token...
{'Error': 'BadAuthentication'}
[!] Could not get access token.
[*] Access token: None

[*] Done.
root@docker:~# `
``

@r74tech
Copy link

r74tech commented Jun 6, 2024

Thanks. I can confirm using specific version of packages like this works:

FROM python:3.9-slim-bullseye
RUN pip install "urllib3<2.0.0" "gpsoauth==1.0.2"
COPY ./get_tokens.py ./
CMD python3 get_tokens.py
docker build -t get_tokens . && docker run --rm -it get_tokens
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 597, in urlopen
    httplib_response = self._make_request(conn, method, url,
  File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 343, in _make_request
    self._validate_conn(conn)
  File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 839, in _validate_conn
    conn.connect()
  File "/usr/local/lib/python3.9/site-packages/urllib3/connection.py", line 337, in connect
    self.sock = ssl_wrap_socket(
  File "/usr/local/lib/python3.9/site-packages/urllib3/util/ssl_.py", line 345, in ssl_wrap_socket
    return context.wrap_socket(sock, server_hostname=server_hostname)
  File "/usr/local/lib/python3.9/ssl.py", line 501, in wrap_socket
    return self.sslsocket_class._create(
  File "/usr/local/lib/python3.9/ssl.py", line 1074, in _create
    self.do_handshake()
  File "/usr/local/lib/python3.9/ssl.py", line 1343, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1133)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/requests/adapters.py", line 667, in send
    resp = conn.urlopen(
  File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 637, in urlopen
    retries = retries.increment(method, url, error=e, _pool=self,
  File "/usr/local/lib/python3.9/site-packages/urllib3/util/retry.py", line 399, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='android.clients.google.com', port=443): Max retries exceeded with url: /auth (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1133)')))

Looking at the threads here, I wonder if it was working until last week.

I couldn't use it, but I was able to do it using the method you showed me here, so I'll just note it.
leikoilja/glocaltokens#531

https://github.com/leikoilja/ha-google-home#master-token

@gw72
Copy link

gw72 commented Jun 8, 2024

What are the security implications of using a master token? Can anyone use that to access my whole google account at any time? Can access using the token be revoked?

@PietroSpina
Copy link

https://gist.github.com/rithvikvibhu/952f83ea656c6782fbd0f1645059055d?permalink_comment_id=4793755#gistcomment-4793755

So the folks like @CaliLuke @Nemesis24 that got their Master Tokens but had trouble putting them into the integration config window... did it ever work? what's the trick?

FWIW if I run the script multiple times, I get different master tokens every time

@edx-sayed-salem
Copy link

If anyone is getting urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='android.clients.google.com', port=443): Max retries exceeded with url: /auth (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1129)'))) like me and @r74tech

I cleared it by using Mozilla CA certificate from curl

I added this after the imports to force python to use this cert instead of default (you can modify this to download the cert somewhere else if you like, I use it in a container so I don't bother):

import urllib.request

# Download the SSL certificate
urllib.request.urlretrieve('https://curl.se/ca/cacert.pem', 'cacert.pem')

# Set downloaded cert to be used
os.environ['CURL_CA_BUNDLE'] = f'{os.getcwd()}/cacert.pem'

Hope it helps.

@dot-Justin
Copy link

Thank you! I just needed indeed to downgrade gpsoauth to 1.0.2 (previously install 1.0.4) to get rid of the NeedsBrowser error. pip freeze pip uninstall gpsoauth pip install gpsoauth==1.0.2

This worked for me, many thanks.

@dwang707
Copy link

dwang707 commented Sep 2, 2024

Hello,

I get a android id problem in my script

@BimpArno
Copy link

Am getting the SSL certverication error (i think)

requests.exceptions.SSLError: HTTPSConnectionPool(host='android.clients.google.com', port=443): Max retries exceeded with url: /auth (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)')))

looked at this:
https://www.geeksforgeeks.org/how-to-disable-security-certificate-checks-for-requests-in-python/

but no idea how to add this to the script

@BimpArno
Copy link

or can someone explain how to do that docker

Since there are several issues getting the token reliable on different environments, you can use a docker container which was created solely for this use: https://hub.docker.com/r/breph/ha-google-home_get-token. If you choose to use this container, run the following command:

$ docker run --rm -it breph/ha-google-home_get-token

i have no clue what to do here

@edx-sayed-salem
Copy link

@BimpArno you can edit get_tokens.py of the docker image you linked and rebuild it with its Dockerfile, it's all here I believe. Add the lines from a few comments ago. If it is that same issue, it should work.

@totoantibes
Copy link

@BimpArno you can edit get_tokens.py of the docker image you linked and rebuild it with its Dockerfile, it's all here I believe. Add the lines from a few comments ago. If it is that same issue, it should work.

so the Dockerfile worked, but i had to add import os to the get_tokens.py working so now it looks like this:

# Get tokens for Google Home Foyer API
# https://gist.github.com/rithvikvibhu/952f83ea656c6782fbd0f1645059055d


from gpsoauth import perform_master_login, perform_oauth
from uuid import getnode as getmac

import urllib.request
import os 
# Download the SSL certificate
urllib.request.urlretrieve('https://curl.se/ca/cacert.pem', 'cacert.pem')

# Set downloaded cert to be used
os.environ['CURL_CA_BUNDLE'] = f'{os.getcwd()}/cacert.pem'


# Creds to use when logging in
USERNAME = 'example@gmail.com'
PASSWORD = 'yourpassword'

# Optional Overrides (Set to None to ignore)
device_id = None
master_token = None
access_token = None

# Flags
DEBUG = True

def get_master_token(username, password, android_id):
    res = perform_master_login(username, password, android_id)
    if DEBUG:
        print(res)
    if 'Token' not in res:
        print('[!] Could not get master token.')
        return None
    return res['Token']


def get_access_token(username, master_token, android_id):
    res = perform_oauth(
        username, master_token, android_id,
        app='com.google.android.apps.chromecast.app',
        service='oauth2:https://www.google.com/accounts/OAuthLogin',
        client_sig='24bb24c05e47e0aefa68a58a766179d9b613a600'
    )
    if DEBUG:
        print(res)
    if 'Auth' not in res:
        print('[!] Could not get access token.')
        return None
    return res['Auth']


def _get_android_id():
    mac_int = getmac()
    if (mac_int >> 40) % 2:
        raise OSError("a valid MAC could not be determined."
                      " Provide an android_id (and be"
                      " sure to provide the same one on future runs).")

    android_id = _create_mac_string(mac_int)
    android_id = android_id.replace(':', '')
    return android_id


def _create_mac_string(num, splitter=':'):
    mac = hex(num)[2:]
    if mac[-1] == 'L':
        mac = mac[:-1]
    pad = max(12 - len(mac), 0)
    mac = '0' * pad + mac
    mac = splitter.join([mac[x:x + 2] for x in range(0, 12, 2)])
    mac = mac.upper()
    return mac


if not device_id:
    device_id = _get_android_id()
    print(device_id)


print('''
This script generates tokens that can be used when making requests to the Google Home Foyer API.
There are 2 kinds of tokens used here:

1. Master token - Is in the form `aas_et/***` and is long lived. Needs Google username and password.
2. Access token - Is in the form `ya29.***` and lasts for an hour. Needs Master token to generate.

If you do not want to store the Google account password in plaintext,
get the master token once, and set it as an override value.

It's safer/easier to generate an app password and use it instead of the actual password.
It still has the same access as the regular password, but still better than using the real password while scripting.
(https://myaccount.google.com/apppasswords)
''')

print('\n[*] Getting master token...')
if not master_token:
    master_token = get_master_token(USERNAME, PASSWORD, device_id)
print('[*] Master token:', master_token)

print('\n[*] Getting access token...')
if not access_token:
    access_token = get_access_token(USERNAME, master_token, device_id)
print('[*] Access token:', access_token)

print('\n[*] Done.')

@Th0tti
Copy link

Th0tti commented Oct 28, 2024

How can I delete or overwrite the master token?

@Maxver0
Copy link

Maxver0 commented Nov 15, 2024

This script doesn't seem to work.

Updated the script's username and password variables. Tried full email, just the username, normal password, the app password. For the app password, I'd remove the spaces and any extra characters but no luck.

HA Integration throws the same error.

[] Getting master token...
{'Error': 'BadAuthentication'}
[!] Could not get master token.
[
] Master token: None

[] Getting access token...
{'Error': 'BadAuthentication'}
[!] Could not get access token.
[
] Access token: None

@ktfcaptain
Copy link

Just spent way too long trying to get this working with no success

@MichelBytes
Copy link

RE: Google Account Master Token
I am having trouble running this script. I have done everything I can thank for. I wish there was just a premade generator I can just download and it would work. instead, I have installed Python and Node, which confuses me all the heck. I don't know Python or Node for that reason. I think I need help. Can someone help? I read so many GitHub comments and I even went to Reddit and I still need help. I just want Google Home installed on my HomeAssistant. This is frustrating. I got my app password to bypass the 2FA

@titangamer46
Copy link

Where exactly do i post this script. there is no information telling which terminal or program to paste it in

@Brephlas
Copy link

@MichelBytes and @titangamer46 you can try to do use the docker container. You should've docker installed, Docker Desktop might be the easiest solution here.
When installed, you should be able to run docker in any terminal. If this works, run the command docker run --rm -it breph/ha-google-home_get-token and fill in the requested inputs

This should suffice to get a valid token. If not, feel free to provide further details

@HomeserverKluge
Copy link

hello, I need help, I've been fiddling around with this damn token for almost 3 hours but I can't get it to work, the logs just say "

Traceback (most recent call last):
File "//get_tokens.py", line 9, in
USERNAME = input("what is your google mail address? (include @gmail.com): ")
EOFError: EOF when reading a line
what is your google mail address? (include @gmail.com): "

@Brephlas
Copy link

@HomeserverKluge sounds like you didn't enter anything to the input. You need to enter the mail address + password/app passowrd of the account where you have added your devices to

@HomeserverKluge
Copy link

I have this as a container in Portainer and where should I add the email and password

@newstartech
Copy link

it works ok. thanks

@edx-sayed-salem
Copy link

I have this as a container in Portainer and where should I add the email and password

You have to run in an interactive session to provide input, otherwise just rewrite a few lines and put your creds. in the code or use environment variables and rebuild the image.

@Brephlas
Copy link

Good idea with the environment variables :) I've updated the image on dockerhub, so you can now use them right from this image

Here is an example on how to set them in portainer:
portainer_run_container

@HomeserverKluge
Copy link

HomeserverKluge commented Dec 15, 2024

Thanks a lot, it worked now, I didn't use USERNAME but Email_USERNAME. But I still have a question: would you be able to display your integrated camera in Google Home in Home Assistant?

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