Skip to content

feat(auth): replace cookie scraping with Auth0 OAuth + DPoP-bound refresh#267

Open
sslivins wants to merge 3 commits intobinarydev:mainfrom
sslivins:pr3-email-password-auth
Open

feat(auth): replace cookie scraping with Auth0 OAuth + DPoP-bound refresh#267
sslivins wants to merge 3 commits intobinarydev:mainfrom
sslivins:pr3-email-password-auth

Conversation

@sslivins
Copy link
Copy Markdown

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.
  • API: Bumped endpoints to v5 to match what the mobile app uses.
  • Config flow: Email/password user step + working reauth step
    (in-place, no remove-and-re-add).
  • Options flow: Configurable scan_interval (default 900s / 15min,
    matching the previous behavior).
  • Coordinator: Passes config_entry to DataUpdateCoordinator
    super, which silences the HA 2025+ deprecation warning.
  • Entity: device_state_attributes to extra_state_attributes,
    drop a redundant listener wire-up that CoordinatorEntity already
    handles.
  • Sensor: Small _safe_float helper so a malformed numeric prop
    reports unknown instead of throwing in native_value.
  • Image: Guard against a missing content-type header.
  • Tests: Updated end-to-end for the new auth + api surfaces,
    including Auth0 error parsing.
  • Translations: New strings for email/password, reauth, and the
    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).
  • Manual: fresh install, reauth flow, 24h soak with default 15-min
    polls; saw the token refresh at the expected expires_in boundary;
    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.

sslivins and others added 3 commits April 30, 2026 09:56
…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>
@sslivins
Copy link
Copy Markdown
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 (_safe_float helper, _EMPTY_ITEM sentinel for missing devices). Figured better to have it in one PR than two, but happy to split it back out if you'd rather review separately.

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.

1 participant