Skip to content

Add OAuth Device Code login with automatic token refresh#704

Open
einanderson wants to merge 3 commits into
anxdpanic:masterfrom
einanderson:feature/oauth-device-login
Open

Add OAuth Device Code login with automatic token refresh#704
einanderson wants to merge 3 commits into
anxdpanic:masterfrom
einanderson:feature/oauth-device-login

Conversation

@einanderson

@einanderson einanderson commented Jun 5, 2026

Copy link
Copy Markdown

Summary

Adds Twitch Device Code Grant login + silent token refresh, so the OAuth token no longer has to be regenerated manually.

Addresses #701 (Using Device Grant Flow) and #698 (OAuth token stops working).

What it does

  • New Settings → Login → "Login (device code)": shows a short code; the user authorizes once at twitch.tv/activate.
  • Stores refresh_token + expiry and refreshes the access token automatically (on demand in api.Twitch.__init__, and proactively in the background service ~every 5 min).
  • Login uses the Twitch device code flow — no client secret needed.
  • ⚠️ The silent refresh requires a Twitch Public client. With a confidential app (one registered with a secret) Twitch rejects the refresh with missing client secret. In that case the add-on shows a one-time hint and falls back to the manual login — and stops retrying, so there is no log spam. For automatic refresh, register a Public application at dev.twitch.tv/console/apps and set its Client-ID under Settings → Login. (So it may be worth shipping a public Client-ID for the device flow.)
  • The existing manual link flow is kept as a fallback — nothing breaks for current users.

Implementation

  • addon/device_oauth.py — device-code request/poll + refresh (pure requests).
  • routes/device_login.py — interactive login dialog (abort-aware via xbmc.Monitor).
  • addon/utils.pyensure_valid_token() + token storage helpers; gracefully detects a confidential client and disables retrying for it.
  • addon/api.py, service.py — refresh hooks.
  • settings.xml, addon/strings.py, en_gb/strings.po — action + 3 hidden settings (oauth_refresh_token, oauth_token_expiry, oauth_refresh_unsupported) + English source strings (translatable via Weblate).

Testing

  • Live on LibreELEC / Raspberry Pi 4 / Kodi 21 (Omega): full login + silent refresh verified with a Public Client-ID (device login and the refresh_token grant both succeed without a secret; ensure_valid_token() refreshes cleanly).
  • Confidential-client path verified too: Twitch returns missing client secret, the add-on shows the hint once and falls back without retry spam.
  • Uses only requests + standard xbmc/xbmcgui APIs (no OS-specific code) → platform-independent by design; cross-platform testing (Windows/Android/macOS) welcome.

Notes

  • Public-client refresh tokens expire after ~30 days of inactivity; the background refresh keeps them valid as long as Kodi runs within that window.

Robust token storage (follow-up)

The rotating, single-use refresh token could be lost under Kodi's multi-process settings race: the background service and plugin invocations can refresh concurrently — consuming the same single-use token — or clobber each other's settings.xml write from an in-memory cache. That surfaced as Invalid refresh token and broke the silent refresh after a while.

Tokens are now kept in addon_data/oauth_tokens.json instead of the Kodi settings:

  • read fresh on every access, written atomically via os.replace;
  • the whole refresh runs under a cross-process fcntl lock with a re-check inside the lock, so a rotated token is never consumed twice;
  • one-time migration from the legacy settings (mirrored back for backward compatibility);
  • the confidential-client handling above is preserved.

Verified by forcing two refresh cycles: the refresh token rotated and stayed valid, with no Invalid refresh token.

The implicit-grant link flow never returns a refresh token, so the
OAuth token has to be regenerated manually every few weeks/months.

This adds Twitch's Device Code Grant flow ("Login (device code)" under
Settings -> Login): the user authorizes once with a short code at
twitch.tv/activate, and the add-on then refreshes the access token
silently via the stored refresh token (on demand in api.Twitch and
proactively from the background service). The existing manual link
flow is kept as a fallback. No client secret is required (public
client, default Client-ID). New strings are English source strings,
translatable via Weblate.

Refs anxdpanic#701, anxdpanic#698
@einanderson einanderson marked this pull request as ready for review June 5, 2026 14:16
@einanderson

Copy link
Copy Markdown
Author

Heads-up on a limitation I ran into while using this in production (Kodi 21 / LibreELEC, Raspberry Pi 4):

The silent token refresh (grant_type=refresh_token) only works when the OAuth app is registered as a Public client. With a Confidential client, Twitch rejects the refresh with:

400 {"status":400,"message":"missing client secret"}

i.e. Twitch requires a client_secret for the refresh grant on confidential clients (the hosted token page works because that secret lives server-side). Net effect: device login succeeds, but the automatic refresh fails and the user is logged out again once the access token expires — which defeats the main purpose of this PR.

I verified end-to-end with a self-registered Public client (Client Type: Public on the Twitch dev console — no secret): device login and the refresh_token grant both succeed without a secret, and ensure_valid_token() then refreshes cleanly and keeps working.

So for the auto-refresh to work out of the box, the device flow needs a public client ID — either ship one for the device flow, or document that users must register their own and set it (oauth_clientid). Happy to adjust the PR if that's useful.

A silent refresh is impossible for a confidential client (Twitch replies
'missing client secret'). Detect it, stop retrying, and show a one-time
hint to set a public Client-ID; the manual login keeps working. Public
client ids are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@einanderson

Copy link
Copy Markdown
Author

Addressed in the latest push (87f22df): the add-on now detects the confidential-client case (missing client secret), shows a one-time hint to set a public Client-ID, stops retrying, and falls back to the manual login — public client ids are unaffected. PR description updated accordingly.

@kuraikaze

kuraikaze commented Jun 5, 2026

Copy link
Copy Markdown

Hi @einanderson
After forlornly following this repo for a very long while, hoping someone with knowhow could fix-up the plugin so that the Twitch Enhanced Broadcasting features would be usable, I now get a flurry of notifications about two contributors in a couple of days 😲

The initial activity was notification that an almost 1 year old PR from @Serph91P had been closed/withdrawn, after seemingly no response from maintainers, which prompted me to follow back to his fork, and found he'd actually built and published an updated version to his custom Kodi Addon repo.
After installing and completing the additional Twitch Developer Application/Oauth/cookie-harvesting steps in the README.md I am at last able to play 1440p streams properly (with audio, at least. Haven't found a Twitch beta participant using HEVC yet).

His approach is rather more hands-on for the user than yours, but perhaps some cross-pollination of ideas or perhaps re-invention-of-wheel scenarios could be sidestepped ? (I am making a big assumption here that you two aren't already in contact - he seems a rather privacy focused guy, with almost no public communication footprint, so can't really tell if there was any communication about his PR with maintainers/contributors/community users).

However it goes with your merge I'm happy to be a guinea-pig and clone/test your PRs 😁

@anxdpanic

anxdpanic commented Jun 5, 2026 via email

Copy link
Copy Markdown
Owner

@anxdpanic

anxdpanic commented Jun 5, 2026 via email

Copy link
Copy Markdown
Owner

@einanderson

Copy link
Copy Markdown
Author

at the end i'am a vibe coder and using claude code to find a way to solve the problems. the decision is on your side. It's my first day on git so... :) but i solved in the last few days some issues with only ai support.

@kuraikaze

kuraikaze commented Jun 5, 2026

Copy link
Copy Markdown

@einanderson
A quick test of login & playback on LibreELEC / Generic x86 / Kodi 22-Alpha3 (Piers) + Twitch Turbo works nicely for me.

Cleared all my old settings, used Device Code auth & tried a mix of 1080p_h.264 / 1440p_h.264 / 1440p_h.265 - live & VODs at Source default quality. All streamed succesfully.

Notably, audio stream selects/plays correctly and no ads appear, which were the major bugbears with the extant 3.0.2 version & Enhanced Broadcast.

One remaining issue I have - which I don't believe you've looked at yet, from the commits ...
Search (either Streams or Channels) returns no results, even for channels/streams I know exist.
I'm pretty certain this was something that broke a year or two back in the 3.0.2 version after Twitch API changes.

Not a big deal, and absolutely no pressure from me - I've gotten into the habit of Following channels on PC/Browser so that I can watch them on Kodi/TV from Following Channels list, and that still works fine. I mostly only remembered it was an issue at all because I was clicking around trying to find things to usefully provide feedback on and saw the Search feature which I had previously hidden via settings on old install 😁

@einanderson

einanderson commented Jun 5, 2026

Copy link
Copy Markdown
Author

@einanderson I am not very active these days as life has changed. Are you interested in attempting maintenance on the addon, and on repo submission addressing the submission reviews? I would like this addon to continue and could open access for you. Let me know your comfort level. I am willing to help.

Thanks, that really means a lot. I can't take on full maintenance right now, but I'd love to stay involved — I'll keep contributing and send PRs every now and then that you can review and merge whenever it suits you.

(As I mentioned on the search PR, I work on these with Claude (Anthropic's Claude Code) assisting — just so the expectation's clear.)

Appreciate you keeping the project alive, and the offer to help — glad to chip in where I can.

…cess lock

The rotating, single-use refresh token could be lost under Kodi's multi-process
settings race: several add-on processes (service + plugin calls) refreshing at
once and consuming the same single-use token, or one clobbering another's
settings.xml write from its in-memory cache. This surfaced as "Invalid refresh
token" and broke the silent auto-refresh after a while.

Keep the tokens in addon_data/oauth_tokens.json instead: read fresh on every
access, written atomically via os.replace, and guarded by a cross-process fcntl
lock around the whole refresh with a re-check inside the lock so a rotated token
is never consumed twice. Migrates once from the legacy settings (mirrored back
for backward compatibility) and preserves the confidential-client handling.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@einanderson

Copy link
Copy Markdown
Author

@anxdpanic i'm doing something to keep this project alive :) Thx for your work!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants