feat(auth): replace cookie scraping with Auth0 OAuth + DPoP-bound refresh#267
Open
sslivins wants to merge 3 commits intobinarydev:mainfrom
Open
feat(auth): replace cookie scraping with Auth0 OAuth + DPoP-bound refresh#267sslivins wants to merge 3 commits intobinarydev:mainfrom
sslivins wants to merge 3 commits intobinarydev:mainfrom
Conversation
…resh Replaces the manual cookie-extraction auth flow with the same Auth0 Universal Login + DPoP-bound refresh token flow the MyGenerac mobile app uses. Adds an email/password config flow, an in-place reauth step, a configurable scan_interval option, and bumps the API surface to v5 to match the app. - New auth.py: Auth0 Universal Login + DPoP token client - API endpoints bumped to v5 - Email/password config flow with reauth step - Options flow for scan_interval (default 900s) - Coordinator passes config_entry to silence HA 2025+ warning - entity: device_state_attributes -> extra_state_attributes - sensor: _safe_float helper for malformed numeric props - image: guard against missing content-type header - Tests updated end-to-end for new auth and api surfaces Existing entries hit reauth on next poll and prompt for MyGenerac email/password once. Tokens are then persisted in the config entry and refreshed automatically. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tity, sensors - auth.py: refresh-token success/forbidden/invalid_grant/invalid_request paths plus 7 form-parser cases for the legacy CSRF login flow. - coordinator.py: default + override scan_interval; InvalidCredentialsException and InvalidGrantError both surface as ConfigEntryAuthFailed. - __init__.py: persist callback wired into auth on setup; first-refresh InvalidCredentialsException / InvalidGrantError raise ConfigEntryAuthFailed. - config_flow.py: reauth flow keeps original email (not user-supplied) when calling login(); reconfigure persists CONF_SCAN_INTERVAL into entry.options. - entity.py: missing device id falls back to _EMPTY_ITEM (available=False). - sensor.py: _safe_float treats None / 'N/A' / non-numeric as None instead of crashing native_value (RunTime, ProtectionTime, BatteryVoltage). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use hass.config_entries.async_setup() so entry transitions through SETUP_IN_PROGRESS, matching the pattern of existing tests in this file. Calling async_setup_entry directly when entry is NOT_LOADED triggers OperationNotAllowed in async_forward_entry_setups. - Remove blank line after module docstring per reorder-python-imports. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Author
|
Quick follow-up - pushed two more commits adding test coverage for the auth changes (refresh-token flow, hidden-input form parser, error paths) plus some defensive bits in the coordinator and sensor code ( |
This was referenced Apr 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hey! First off, thanks for starting and maintaining this integration.
I've been running it for a while and it's been genuinely useful. Really
appreciate the work you've put in. 🙏
That said, the cookie thing has been a recurring headache. Every few
weeks something on Generac's portal shifts (or the cookie just ages
out) and I'm back in DevTools copying values out of a browser, and I
noticed a lot of the recent issues here are folks hitting the same
wall. So I took a stab at fixing it.
My first attempt was actually a script that drove the web login and
got around the bot challenge in front of it. That worked surprisingly
well, but it was still a manual thing I had to run whenever the cookie
died, which kind of defeated the point. Since I'm reasonably familiar
with OAuth, I figured I'd peek at how the mobile app authenticates and
see if it was simple enough to port over. Turned out to be very doable,
and fortunately once you're past the login the API surface the app
talks to is identical to what this integration already uses. So I
pointed the integration at the same Auth0 Universal Login the app
uses, with the same DPoP-bound refresh token flow. End result: real
OAuth, no cookie extraction, automatic token refresh, and a proper
reauth prompt if a refresh ever fails (e.g. password change) instead
of a silently-broken entry.
One thing worth flagging: the app talks to the v5 API where the
integration was on an older version, so I switched everything over to
v5 while I was in there. Nothing I use seems broken by it, but you'll
probably want to take a closer look since you know the surface better
than I do.
What's in here
auth.py(new): Auth0 Universal Login + DPoP token client.(in-place, no remove-and-re-add).
scan_interval(default 900s / 15min,matching the previous behavior).
config_entrytoDataUpdateCoordinatorsuper, which silences the HA 2025+ deprecation warning.
device_state_attributestoextra_state_attributes,drop a redundant listener wire-up that
CoordinatorEntityalreadyhandles.
_safe_floathelper so a malformed numeric propreports
unknowninstead of throwing innative_value.content-typeheader.including Auth0 error parsing.
scan interval option.
Migration
No silent fallback. After upgrading, existing entries land in reauth
on the next poll and prompt for MyGenerac email + password once. From
then on tokens are persisted in the config entry and refreshed
automatically.
Testing
pytest -p no:timeout: full suite green locally (Windows).polls; saw the token refresh at the expected
expires_inboundary;sensors update normally; reauth re-authenticates without losing the
entry.
Follow-ups
I've got a few other small PRs coming to clean up some nits I noticed
along the way. Feel free to take or leave whatever you don't want.
Thanks again for the project, and no rush on review. Happy to iterate
on whatever you want changed.