Skip to content

soc_vwid: use OIDC hybrid login flow - #3401

Closed
rleidner wants to merge 2 commits into
openWB:masterfrom
rleidner:soc_vwid_p8
Closed

rleidner wants to merge 2 commits into
openWB:masterfrom
rleidner:soc_vwid_p8

Conversation

@rleidner

Copy link
Copy Markdown
Collaborator

Fix login problem

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 updates the VWID authentication implementation in libvwid.py to use an OIDC hybrid login flow against the newer /auth/v1/idk/oidc/* endpoints in order to resolve login failures.

Changes:

  • Switches login flow to OIDC hybrid (response_type=code id_token token) and updates related headers/endpoints.
  • Introduces a set of VW-specific exception classes and improves error handling/logging in several request paths.
  • Adds additional vehicle/trip endpoints and multiple remote-action helper methods (climate/charging/lock/honk, etc.).
Comments suppressed due to low confidence (2)

packages/modules/vehicles/vwid/libvwid.py:568

  • doLogin() retry loop will never log/return via if i > tries because range(tries) yields 0..tries-1. This makes the failure branch unreachable and can hide the final-attempt failure path.
            for i in range(tries):
                self._session_logged_in = await self._login()
                if self._session_logged_in:
                    break
                if i > tries:
                    _LOGGER.error("Login failed after %s tries", tries)
                    return False

packages/modules/vehicles/vwid/libvwid.py:568

  • doLogin() retry loop will never log/return via if i > tries because range(tries) yields 0..tries-1. This makes the failure branch unreachable and can hide the final-attempt failure path.
            for i in range(tries):
                self._session_logged_in = await self._login()
                if self._session_logged_in:
                    break
                if i > tries:
                    _LOGGER.error("Login failed after %s tries", tries)
                    return False

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 553 to +554
def _clear_cookies(self):
self._session._cookie_jar._cookies.clear()
self._session._cookie_jar._cookies.clear() # pylint: disable=protected-access
Comment on lines +681 to +684
# Redirect case
if not redirect and req.status == 302:
return req.headers["Location"]
if req.status != 200:
raise Exception("Form POST request failed.")
return await req.text()
return req.headers.get("Location")

Comment on lines +802 to +809
auth_code = all_params.get("code", [None])[0]
id_token = all_params.get("id_token", [None])[0]
access_token = all_params.get("access_token", [None])[0]

if not auth_code:
raise AuthenticationError("No authorization code in callback URL")

return auth_code, id_token, access_token
Comment on lines 1152 to 1155
_LOGGER.debug(
"Did not receive return data for requested service %s."
" (This is expected for several service/car combinations)",
"Did not receive return data for requested service %s.\
(This is expected for several service/car combinations)",
service,
Comment on lines +1352 to +1361
async def setClimater(self, vin, data, action):
"""Execute climatisation actions."""
action = "start" if action else "stop"
try:
response_raw = await self.post(
f"{BASE_API}/vehicle/v1/vehicles/{vin}/climatisation/{action}",
json=data,
return_raw=True,
)
return await self._handle_action_result(response_raw)
Comment on lines 553 to +554
def _clear_cookies(self):
self._session._cookie_jar._cookies.clear()
self._session._cookie_jar._cookies.clear() # pylint: disable=protected-access
Comment on lines +681 to +684
# Redirect case
if not redirect and req.status == 302:
return req.headers["Location"]
if req.status != 200:
raise Exception("Form POST request failed.")
return await req.text()
return req.headers.get("Location")

Comment on lines +802 to +809
auth_code = all_params.get("code", [None])[0]
id_token = all_params.get("id_token", [None])[0]
access_token = all_params.get("access_token", [None])[0]

if not auth_code:
raise AuthenticationError("No authorization code in callback URL")

return auth_code, id_token, access_token
Comment on lines 1152 to 1155
_LOGGER.debug(
"Did not receive return data for requested service %s."
" (This is expected for several service/car combinations)",
"Did not receive return data for requested service %s.\
(This is expected for several service/car combinations)",
service,
Comment on lines +1352 to +1361
async def setClimater(self, vin, data, action):
"""Execute climatisation actions."""
action = "start" if action else "stop"
try:
response_raw = await self.post(
f"{BASE_API}/vehicle/v1/vehicles/{vin}/climatisation/{action}",
json=data,
return_raw=True,
)
return await self._handle_action_result(response_raw)
@rleidner rleidner closed this Jun 1, 2026
@rleidner

rleidner commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator Author

Interface is not working since today. Closing PR.

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