Skip to content

feat(solis): OAuth 2.0 bearer auth mode alongside HMAC api-key#4078

Open
mgazza wants to merge 2 commits into
mainfrom
fix/solis-oauth-dual-auth
Open

feat(solis): OAuth 2.0 bearer auth mode alongside HMAC api-key#4078
mgazza wants to merge 2 commits into
mainfrom
fix/solis-oauth-dual-auth

Conversation

@mgazza

@mgazza mgazza commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Adds a dual-mode auth path to the Solis driver via the shared OAuthMixin, so SolisCloud can be driven with an OAuth 2.0 Bearer token (refreshed through the SaaS oauth-refresh edge function) as an alternative to the existing HMAC api-key. Existing api-key users are unaffectedauth_method defaults to api_key and the HMAC path is unchanged.

Changes

  • initialize() accepts auth_method/access_token/token_expires_at/token_hash; OAuth mode uses the OAuth host (api-oauth2.soliscloud.com) with a Bearer header.
  • _build_headers branches Bearer (OAuth) vs HMAC-SHA1 (api-key); _execute_request proactively refreshes and handles 401/403; cid/value control payloads unchanged.
  • components.py: api_key/api_secret now optional, with a startup guard that requires them in api-key mode; adds solis_auth_method/access_token/token_expires_at/token_hash args.
  • _with_retry aborts fast when OAuth refresh has permanently failed (no retry-window hang).
  • Test: OAuth/HMAC header selection.

Note: OAuth read/control endpoint paths reuse the HMAC paths at the OAuth host (design "approach 1") — to be confirmed by a manual smoke test before production use. Gated off by default in the SaaS until then.

🤖 Generated with Claude Code

mgazza and others added 2 commits June 17, 2026 13:48
SolisAPI gains a dual-mode auth path via the shared OAuthMixin:
- initialize() accepts auth_method/access_token/token_expires_at/token_hash;
  OAuth mode uses the OAuth host (api-oauth2.soliscloud.com) with a Bearer header
- _build_headers branches Bearer (OAuth) vs HMAC-SHA1 (api-key)
- _execute_request proactively refreshes the token and handles 401/403 via the
  oauth-refresh edge function (provider 'solis'); cid/value control payloads unchanged
- components.py: api_key/api_secret now optional; adds solis_auth_method/access_token/
  token_expires_at/token_hash config args
- test: OAuth/HMAC header selection

NOTE: OAuth read/control endpoint paths reuse the HMAC paths at the OAuth host per
design #366 approach 1 — to be confirmed by the manual smoke-test gate before prod use.

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

Addresses quick-review findings:
- initialize() raises a clear error if api_key/api_secret are missing in api-key
  mode (components.py made them optional, removing the old required-field guard;
  without this a misconfigured instance crashed later on None.encode())
- _execute_request raises immediately if the OAuth token can't be obtained, and
  _with_retry aborts when oauth_failed is set — so a permanent auth failure surfaces
  fast instead of burning the full retry window hitting the API with a bad token

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds dual authentication support to the SolisCloud integration so it can use either the existing HMAC api_key/api_secret flow or an OAuth 2.0 Bearer token flow (via the shared OAuthMixin). The intent is to keep existing users on the current HMAC path by default while enabling an alternative OAuth mode.

Changes:

  • Added OAuth-capable initialization and request handling to SolisAPI, including Bearer header construction and token refresh / 401 handling.
  • Updated component wiring so Solis can be configured with either api-key credentials or OAuth-related args (auth method, token fields).
  • Added a unit test to verify header selection between OAuth (Bearer) and api-key (HMAC) modes.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
apps/predbat/solis.py Adds OAuthMixin to SolisAPI, switches base host in OAuth mode, and branches header/auth + refresh/retry behavior.
apps/predbat/components.py Makes Solis api-key creds optional and introduces new Solis OAuth-related configuration args.
apps/predbat/tests/test_solis.py Adds a test validating OAuth vs HMAC header construction.

Comment thread apps/predbat/solis.py
Comment on lines +380 to +382
if getattr(self, "auth_method", "api_key") == "oauth":
if not await self.check_and_refresh_oauth_token():
raise SolisAPIError("Solis OAuth token unavailable — reconnect required", status_code=401)
Comment on lines +396 to +399
"auth_method": {"required": False, "config": "solis_auth_method", "default": "api_key"},
"access_token": {"required": False, "config": "solis_access_token"},
"token_expires_at": {"required": False, "config": "solis_token_expires_at"},
"token_hash": {"required": False, "config": "solis_token_hash"},
print("ERROR: OAuth _build_headers should not include Content-MD5")
failed = True

# API-key mode (default mock has no auth_method) — HMAC 'API <key>:<sig>'
Comment thread apps/predbat/solis.py
Comment on lines +377 to +382
# OAuth: proactively refresh the token before the call (no-op for api-key mode).
# If the token can't be obtained (refresh failed / needs reauth), fail fast — the
# oauth_failed flag below makes _with_retry abort instead of looping with a bad token.
if getattr(self, "auth_method", "api_key") == "oauth":
if not await self.check_and_refresh_oauth_token():
raise SolisAPIError("Solis OAuth token unavailable — reconnect required", status_code=401)
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.

2 participants