tombulled / innertube

Python Client for Google's Private InnerTube API. Works with YouTube, YouTube Music and more!
https://pypi.org/project/innertube/
MIT License
298 stars 20 forks source link

:sparkles: Support Authentication #11

Open Neppu-Nep opened 2 years ago

Neppu-Nep commented 2 years ago

Not sure if you already knew about it or not but from my testing you can do authenticated requests by using cookies. (Not sure how long will this last before needing to repeat the process again).

Steps to do

  1. Open incognito browser and open developer console.
  2. Browse to network tab and enable Preserve log.
  3. Type in set_registration in the network search bar.
  4. Go to https://www.youtube.com/ and login as usual.
  5. Then select the 2nd result and then copy the whole cookie from the request headers.
  6. The cookie values needed are SID, HSID, SSID, APISID and SAPISID. You can delete the rest values or just leave them.
  7. Generate SAPISIDHASH by inserting SAPISID value obatained from above into the function below.
import time
import hashlib

def hashString(password):
    hash_object = hashlib.sha1(password.encode())
    pbHash = hash_object.hexdigest()
    return pbHash

current_time = str(int(time.time()))
origin = "https://www.youtube.com"
sapisidhash = hashString(f"{current_time} {sapisid} {origin}")
  1. Pass the headers below when making requests
    headers = {
    "Authorization": f"SAPISIDHASH {current_time}_{sapisidhash}",
    "Cookie": cookie_string_from_step_6,
    "x-origin": origin
    }
  2. Your requests are now authenticated requests.

Notes

Reason why incognito is required is because for some reason the SID, HSID and SSID cookies aren't included in the request headers and instead it relies on x-goog-authuser header value if you are already logged in. Clearing cache doesn't seem to include it either.

I'm not really good at actually implementing stuffs so would be grateful if you could add the feature into the existing code.

tombulled commented 2 years ago

Hi @Loli-Killer, thanks for raising this issue! I was aware that it was possible to make authenticated requests using cookies in this way, however as it requires such a manual action by the user, I haven't included the functionality in the codebase.

I'd really love to get OAuth working in some way, and I believe the community has been able to make some headway on this, so it's something I've been meaning to look into further. I'll keep this issue open, and hopefully will get a chance to explore the issue of authentication more soon :slightly_smiling_face:

BRUHItsABunny commented 2 years ago

Getting oauth to work is relatively easy

If you can read Golang this might help you out: https://github.com/BRUHItsABunny/go-android-firebase/blob/326f8ce0c3f2b62f6199078268b29456535023ce/api/api.go#L128 My personal (private) innertube library uses that function to fetch the auth token then used as Bearer authorization value. You would have to figure out what the "data" (url.Values object) would be but other than that pretty straightforward. This was reverse engineered from the Android YouTube app btw

tombulled commented 2 years ago

@BRUHItsABunny Awesome! Thanks for providing that link, your implementation looks incredibly helpful.

I've also found the following resource which looks like it might come in handy:

I'll see if I can get some sort of a POC script for OAuth working using the resources provided in this issue

tombulled commented 2 years ago

Here's a quick POC script I've whipped together using 89z/mech which requests an Oauth2 token for YouTube TV:

import httpx
import pprint

client = httpx.Client()

CLIENT_ID = "861556708454-d6dlm3lh05idd8npek18k6be8ba3oc68.apps.googleusercontent.com"
CLIENT_SECRET = "SboVhoG9s0rNafixCSGGKXAT"

device_data = client.post(
    "https://oauth2.googleapis.com/device/code",
    data=dict(
        client_id=CLIENT_ID,
        scope="https://www.googleapis.com/auth/youtube",
    ),
).json()

device_code = device_data["device_code"]

print('== YouTube TV OAuth ==')
print("  1. Visit:", device_data["verification_url"])
print("  2. Enter code:", device_data["user_code"])
print()
input("Once done, press enter to request a token...")

token_data = client.post(
    "https://oauth2.googleapis.com/token",
    data=dict(
        client_id=CLIENT_ID,
        client_secret=CLIENT_SECRET,
        device_code=device_code,
        grant_type="urn:ietf:params:oauth:grant-type:device_code",
    ),
).json()

pprint.pprint(token_data)

I'll keep fiddling and will see if I can get any further

BRUHItsABunny commented 2 years ago

Oh yea I remember seeing that a while ago as well. I personally opted out of TV oauth due to it requiring manual input upon retrieval, instead I opted into relying on the Google master token however that comes with it's own set of drawbacks. (A compromised master token has access to everything, eg: Drive, Gmail, any apps you may have linked to the Google account)

For that reason I use a dedicated Google account for the innertube automation I do but other than that it was worth it to me given auth tokens are only valid for a couple hours at a time.

tombulled commented 1 year ago

Just stumbled upon YouTube.js. It looks like they have a pretty concrete implementation of the YouTube TV OAuth flow - https://github.com/LuanRT/YouTube.js/blob/main/src/core/OAuth.ts

tombulled commented 1 year ago

A couple of potentially handy links for this:

BRUHItsABunny commented 1 year ago

Those last links are similar to my implementation as well, long lived master token oauth

mine I didn't implement a way to get master tokens yet but i think this looks like that missing piece

0dminnimda commented 1 year ago

What is the status of this?

tombulled commented 1 year ago

What is the status of this?

No further updates to report on this unfortunately, beyond the investigations in this issue. At some point I plan to implement the YouTube TV OAuth flow into innertube, but finding the time is unfortunately proving challenging these days

ghost commented 1 year ago

If you can read Golang this might help you out: https://github.com/BRUHItsABunny/go-android-firebase/blob/326f8ce0c3f2b62f6199078268b29456535023ce/api/api.go#L128

not sure how this helps. this assumes you already have the masterToken, which is quite difficult to get. I mean this from a programmatic standpoint, which manually copying cookies from a browser is not.

I've also found the following resource which looks like it might come in handy:

I am the author of that code, the current implementation is here:

https://github.com/1268/media/blob/v1.6.4/youtube/token.go

A couple of potentially handy links for this:

FYI this code is based on the Google Services Framework 4.4, which is at least ten years old at this point. since version 5, URL like this is used instead:

https://accounts.google.com/embedded/setup/android

tombulled commented 1 year ago

My current stance on authentication is that I'd prefer to avoid using a master token and am not a big fan of relying on browser cookies. OAuth is the preferred approach, and so the YouTube TV OAuth flow is looking like the best way forward, at least for now

ghost commented 1 year ago

LOL WTF I just found another one for YouTube TV (youtubetv-developer@support.youtube.com):

> curl -X POST 'https://oauth2.googleapis.com/device/code?client_id=627431331381.apps.googleusercontent.com&scope=https://www.googleapis.com/auth/youtube'
{
  "device_code": "AH-1Ng04yWzgknyxf0Zx1TKVJRplVL1KfgASq5xzWx9g-FQJUC00HuF8ZtuU2dNMCMRbSes3keVYRoBmPdMC3OBcRCHcm6yTYQ",
  "user_code": "JSR-LBX-RJB",
  "expires_in": 1800,
  "interval": 5,
  "verification_url": "https://www.google.com/device"
}

and:

client_secret O_HOjELPNFcHO_n_866hamcO
hussamh10 commented 11 months ago

@Loli-Killer Thanks for the step by step guide on how to create the headers for authentication However, I am confused on where do I pass the headers (and how) when making requests? Thanks!