[GH-ISSUE #376] OAuth doesn't work with arbitrary file names #295

Closed
opened 2026-02-27 22:09:03 +03:00 by kerem · 3 comments
Owner

Originally created by @benblank on GitHub (Apr 10, 2023).
Original GitHub issue: https://github.com/sigma67/ytmusicapi/issues/376

Originally assigned to: @sigma67 on GitHub.

Describe the bug

When creating a YTMusic instance with OAuth credentials from a file not named "oauth.json" (or rather, the provided path to which does not contain the string "oauth.json"), the resulting instance cannot successfully connect to YouTube Music. Worse, the resulting error (a complaint from the requests library that a header is of the wrong type) doesn't provide any information which helps identify the actual problem (the authentication data being interpreted as invalid browser auth) unless one reads the ytmusicapi source.

To Reproduce

Steps to reproduce the error:

  1. Make and enter a new, empty directory.

  2. python3 -m venv .venv

  3. . .venv/bin/activate

  4. pip3 install ytmusicapi

  5. ytmusicapi oauth

  6. Follow the instructions as usual to create "oauth.json".

  7. mv oauth.json oauth-foo.json ← And here's the problem, folks. 🙂

  8. Create a new file "example.py" with the following contents:

    from ytmusicapi import YTMusic
    
    print(YTMusic("oauth-foo.json").get_playlist("LM", limit=None))
    
  9. python3 example.py

  10. Observe the resulting InvalidHeader exception complaining that the value of the "expires_in" header should be a string or bytes object.

Additional context

I ran into this because I'm trying to migrate some data between two accounts, so naively renamed the files created by ytmusicapi oauth to "oauth-.json".

Possible solution

(Note that I'd be happy to submit a PR for this; I'd create it now, but I need to step away from the keyboard for a bit.)

Looking at the source of the prepare_headers function, I see that it's detecting whether to use OAuth by examining the file's name; I think it would be less surprising to base it on the contents, instead.

This could be done, for example, by checking for the presence of a key which is required to be present when using OAuth but is unlikely to be found in the headers used for browser authentication or by checking whether "expires_at" or "expires_in" keys are present and have have an integer value, as integers aren't valid header values.

Alternatively, input_json could simply be assumed to contain OAuth data (optimistically trying to create a YTMusicOAuth instance), with a fallback to browser authentication if a KeyError is thrown. YTMusicOAuth.load_headers appears to require that the keys "access_token", "expires_at", "refresh_token", and "token_type" all be present; it seems very unlikely that all four would be found in a browser's request headers, as none of them are standard HTTP headers.

Originally created by @benblank on GitHub (Apr 10, 2023). Original GitHub issue: https://github.com/sigma67/ytmusicapi/issues/376 Originally assigned to: @sigma67 on GitHub. **Describe the bug** When creating a `YTMusic` instance with OAuth credentials from a file not named "oauth.json" (or rather, the provided path to which does not *contain* the string "oauth.json"), the resulting instance cannot successfully connect to YouTube Music. Worse, the resulting error (a complaint from the `requests` library that a header is of the wrong type) doesn't provide any information which helps identify the actual *problem* (the authentication data being interpreted as invalid browser auth) unless one reads the `ytmusicapi` source. **To Reproduce** Steps to reproduce the error: 1. Make and enter a new, empty directory. 2. `python3 -m venv .venv` 3. `. .venv/bin/activate` 4. `pip3 install ytmusicapi` 5. `ytmusicapi oauth` 6. Follow the instructions as usual to create "oauth.json". 7. `mv oauth.json oauth-foo.json` **← And here's the problem, folks. 🙂** 8. Create a new file "example.py" with the following contents: ``` py from ytmusicapi import YTMusic print(YTMusic("oauth-foo.json").get_playlist("LM", limit=None)) ``` 9. `python3 example.py` 10. Observe the resulting `InvalidHeader` exception complaining that the value of the "expires_in" header should be a string or bytes object. **Additional context** I ran into this because I'm trying to migrate some data between two accounts, so naively renamed the files created by `ytmusicapi oauth` to "oauth-<account>.json". **Possible solution** *(Note that I'd be happy to submit a PR for this; I'd create it now, but I need to step away from the keyboard for a bit.)* Looking at the source of the `prepare_headers` function, I see that it's detecting whether to use OAuth by examining the file's *name*; I think it would be less surprising to base it on the *contents*, instead. This could be done, for example, by checking for the presence of a key which is required to be present when using OAuth but is unlikely to be found in the headers used for browser authentication or by checking whether "expires_at" or "expires_in" keys are present and have have an integer value, as integers aren't valid header values. Alternatively, `input_json` could simply be *assumed* to contain OAuth data (optimistically trying to create a `YTMusicOAuth` instance), with a fallback to browser authentication if a `KeyError` is thrown. `YTMusicOAuth.load_headers` appears to require that the keys "access_token", "expires_at", "refresh_token", and "token_type" all be present; it seems very unlikely that *all four* would be found in a browser's request headers, as none of them are standard HTTP headers.
kerem 2026-02-27 22:09:03 +03:00
Author
Owner

@sigma67 commented on GitHub (Apr 10, 2023):

Yes, I didn't really see a probably with mandating a specific filename. But I suppose it's more flexible to allow any filename, as it's being passed in through the constructor.

Regarding an implementation, I think we should implement some validation for the files. For browser-based auth, we can rely on the presence of the Cookie value, while for oauth the following are mandatory: token_type, access_token, refresh_token and expires_at.

<!-- gh-comment-id:1501502414 --> @sigma67 commented on GitHub (Apr 10, 2023): Yes, I didn't really see a probably with mandating a specific filename. But I suppose it's more flexible to allow any filename, as it's being passed in through the constructor. Regarding an implementation, I think we should implement some validation for the files. For browser-based auth, we can rely on the presence of the `Cookie` value, while for `oauth` the following are mandatory: `token_type`, `access_token`, `refresh_token` and `expires_at`.
Author
Owner

@sigma67 commented on GitHub (Apr 10, 2023):

For validation, create functions in auth/browser.py and auth/oauth.py to separate that logic from the ytmusic.py file.

For browser validation we should reuse the existing logic:

github.com/sigma67/ytmusicapi@defb621407/ytmusicapi/ytmusic.py (L105-L112)

<!-- gh-comment-id:1501510654 --> @sigma67 commented on GitHub (Apr 10, 2023): For validation, create functions in auth/browser.py and auth/oauth.py to separate that logic from the ytmusic.py file. For browser validation we should reuse the existing logic: https://github.com/sigma67/ytmusicapi/blob/defb621407a3d50cadb12c9e24d439b140f21397/ytmusicapi/ytmusic.py#L105-L112
Author
Owner

@benblank commented on GitHub (Apr 19, 2023):

Yes! Sorry, I've had a bit less time than expected, but I'm very much working on it.

Basically, I was finding it awkward to track the different types of authentication (none, browser, OAuth) and handle them consistently with the logic being spread out, so I'm doing some minor refactoring to centralize it for each type. I'll try to get a draft PR up later today with an example of what I'm talking about, so that you can actually visualize it. 😉

<!-- gh-comment-id:1515281242 --> @benblank commented on GitHub (Apr 19, 2023): Yes! Sorry, I've had a bit less time than expected, but I'm very much working on it. Basically, I was finding it awkward to track the different types of authentication (none, browser, OAuth) and handle them consistently with the logic being spread out, so I'm doing some minor refactoring to centralize it for each type. I'll try to get a draft PR up later today with an example of what I'm talking about, so that you can actually visualize it. 😉
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/ytmusicapi#295
No description provided.