Skip to content

Instantly share code, notes, and snippets.

@rithvikvibhu
Last active April 14, 2024 00:01
Show Gist options
  • Star 34 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • 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
# 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
# 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.')
MIT License
Copyright (c) 2020 Rithvik Vibhu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@cjsturgess
Copy link

For anyone still facing an issue with this script, see this comment: leikoilja/ha-google-home#599 (comment)

Worked for me, first try.

@molexx
Copy link

molexx commented Feb 4, 2024

I got the double whammy here: the app password copy/paste from the Google website does indeed contain hidden characters; after I retyped that I tried several scripts (in homeassistant's terminal) and must be hitting the library issue, so retyped in the dockerised script is the way to go.

The docker command in the main readme is slicker than the one in the comment linked above as it does it all on one line.

https://github.com/leikoilja/ha-google-home?tab=readme-ov-file#master-token

So thanks @Telmo and @Brephlas !

@jnpetersen
Copy link

utilizing the link that @molexx shared, I ended up modifying the docker run slightly so that I could get inside the container to make a few changes to the python itself.

docker run -it -d breph/ha-google-home_get-token

After it created, I did a docker ps to find the name of the container (I tried providing a name and it would always fail). Once this was created, I used the following:

docker exec -it tender_gates bash

tender_gates being the name it generated for me, but once running this, I was in the container.

I ran apt install nano just to make it easier to work with and then ran nano get_tokens.py to open the file. I commented out

USERNAME = input("what is your google mail address? (include @gmail.com): ")
PASSWORD = input("what is the password? (either use regular password or app password): ")

and replacing it with

USERNAME = '<FULL GMAIL EMAIL ADDRESS>'
PASSWORD = '<APP PASSWORD COPIED AND PASTED FROM GOOGLE>'

After making this change, press Ctrl + O and then press enter to Save the changes, then press Ctrl + X to exit nano.

When I ran it in the container by typing python3 get_tokens.py, I thought that was going to work but kept getting [!] Could not get master token. so I decided to change DEBUG = False to DEBUG = True, saved and exited nano again and ran it again and was provided:

[*] 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=[... truncated ...]', '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

PROGRESS!! I held down Control and clicked the link which brought me to a browser to finish the authentication process from the web browser to authenticate the script to work. You may have to go through a few prompts (I did) and then it will end up at a page that says One Moment Please..., which at this point you can re-run python3 get_token.py and it should provide you with a lot more information than just the master and access tokens as DEBUG is still on, but you should be provided with the information needed.

Hope this helps someone.

@edx-sayed-salem
Copy link

edx-sayed-salem commented Mar 17, 2024

installing gpsoauth==1.0.2 seems to work ok. Newer versions return the NeedsBrowser error.

Update
It also only worked on docker images based on: debian:bullseye and alpine:3.16. I didn't try below these versions but newer than those don't work. The openssl package version is 1.1.1 on debian:bullseye and alpine:3.16. Newer versions had version >3.0.0 like @artickl said.

@lcamus
Copy link

lcamus commented Mar 31, 2024

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

@ulasozguler
Copy link

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

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