Skip to content

fix(vehicles/kia): restore SoC via new IDPConnect OAuth2 login - #3751

Merged
LKuemmel merged 3 commits into
openWB:masterfrom
Gulaschcowboy:fix/kia-idpconnect-login
Aug 4, 2026
Merged

LKuemmel merged 3 commits into
openWB:masterfrom
Gulaschcowboy:fix/kia-idpconnect-login

Conversation

@Gulaschcowboy

Copy link
Copy Markdown
Contributor

The Kia/Hyundai SoC adapter stopped working: Hyundai/Kia decommissioned the old Keycloak form login (eu-account..com/auth/realms/...), so getAuthCode() could no longer obtain an authorization code and every SoC request failed.

This switches the login to the current IDPConnect OAuth2 flow, mirroring the well-maintained hyundai_kia_connect_api library (KiaUvoApiEU) used by Home Assistant:

  • GET /auth/api/v2/user/oauth2/authorize (obtain session cookies)
  • GET /auth/api/v1/accounts/certs (RSA public key as JWK)
  • RSA/PKCS#1 v1.5 encrypt the password (hex)
  • POST /auth/account/signin (302 -> ?code=...)
  • POST /auth/api/v2/user/oauth2/token (client_secret, form-urlencoded)

refreshToken() is updated to the IDPConnect token endpoint and keeps a rotated refresh_token when the server returns one. If the refresh grant fails (expired/rotated refresh token), fetch_soc() now falls back to a full login instead of leaving the module stuck after the first poll - same strategy as hyundai_kia_connect_api's refresh_access_token().

Password encryption uses 'cryptography' (already pulled in transitively by requests) with a 'pycryptodome' fallback, so no new hard dependency is introduced.

Robustness fixes:

  • getAuthCode() uses its own requests.Session for the cookie chain and no longer calls getCookies() (dead Keycloak endpoints).
  • RAMDISK_PATH import is wrapped so a missing helpermodules.constants export falls back to /ramdisk instead of crashing the whole module import.
  • postHTTP() raises RuntimeError with the HTTP status/error text, and the token functions pre-initialise 'response' so a failed request no longer masks the real cause with "local variable 'response' referenced before assignment".

Tested on openWB 2.2.1 (Kia EV6): SoC is read on the initial poll and on subsequent refresh polls, no errors in main.log; RSA round-trip verified against a locally generated key pair.

The Kia/Hyundai SoC adapter stopped working: Hyundai/Kia decommissioned
the old Keycloak form login (eu-account.<brand>.com/auth/realms/...),
so getAuthCode() could no longer obtain an authorization code and every
SoC request failed.

This switches the login to the current IDPConnect OAuth2 flow, mirroring
the well-maintained hyundai_kia_connect_api library (KiaUvoApiEU) used by
Home Assistant:

  * GET  /auth/api/v2/user/oauth2/authorize   (obtain session cookies)
  * GET  /auth/api/v1/accounts/certs          (RSA public key as JWK)
  * RSA/PKCS#1 v1.5 encrypt the password (hex)
  * POST /auth/account/signin                 (302 -> ?code=...)
  * POST /auth/api/v2/user/oauth2/token       (client_secret, form-urlencoded)

refreshToken() is updated to the IDPConnect token endpoint and keeps a
rotated refresh_token when the server returns one. If the refresh grant
fails (expired/rotated refresh token), fetch_soc() now falls back to a
full login instead of leaving the module stuck after the first poll -
same strategy as hyundai_kia_connect_api's refresh_access_token().

Password encryption uses 'cryptography' (already pulled in transitively
by requests) with a 'pycryptodome' fallback, so no new hard dependency is
introduced.

Robustness fixes:
  * getAuthCode() uses its own requests.Session for the cookie chain and
    no longer calls getCookies() (dead Keycloak endpoints).
  * RAMDISK_PATH import is wrapped so a missing helpermodules.constants
    export falls back to <openWB-root>/ramdisk instead of crashing the
    whole module import.
  * postHTTP() raises RuntimeError with the HTTP status/error text, and
    the token functions pre-initialise 'response' so a failed request no
    longer masks the real cause with "local variable 'response'
    referenced before assignment".

Tested on openWB 2.2.1 (Kia EV6): SoC is read on the initial poll and on
subsequent refresh polls, no errors in main.log; RSA round-trip verified
against a locally generated key pair.
@LKuemmel
LKuemmel merged commit b19623f into openWB:master Aug 4, 2026
1 check passed
benderl pushed a commit that referenced this pull request Aug 6, 2026
* fix(vehicles/kia): restore SoC via new IDPConnect OAuth2 login

The Kia/Hyundai SoC adapter stopped working: Hyundai/Kia decommissioned
the old Keycloak form login (eu-account.<brand>.com/auth/realms/...),
so getAuthCode() could no longer obtain an authorization code and every
SoC request failed.

This switches the login to the current IDPConnect OAuth2 flow, mirroring
the well-maintained hyundai_kia_connect_api library (KiaUvoApiEU) used by
Home Assistant:

  * GET  /auth/api/v2/user/oauth2/authorize   (obtain session cookies)
  * GET  /auth/api/v1/accounts/certs          (RSA public key as JWK)
  * RSA/PKCS#1 v1.5 encrypt the password (hex)
  * POST /auth/account/signin                 (302 -> ?code=...)
  * POST /auth/api/v2/user/oauth2/token       (client_secret, form-urlencoded)

refreshToken() is updated to the IDPConnect token endpoint and keeps a
rotated refresh_token when the server returns one. If the refresh grant
fails (expired/rotated refresh token), fetch_soc() now falls back to a
full login instead of leaving the module stuck after the first poll -
same strategy as hyundai_kia_connect_api's refresh_access_token().

Password encryption uses 'cryptography' (already pulled in transitively
by requests) with a 'pycryptodome' fallback, so no new hard dependency is
introduced.

Robustness fixes:
  * getAuthCode() uses its own requests.Session for the cookie chain and
    no longer calls getCookies() (dead Keycloak endpoints).
  * RAMDISK_PATH import is wrapped so a missing helpermodules.constants
    export falls back to <openWB-root>/ramdisk instead of crashing the
    whole module import.
  * postHTTP() raises RuntimeError with the HTTP status/error text, and
    the token functions pre-initialise 'response' so a failed request no
    longer masks the real cause with "local variable 'response'
    referenced before assignment".

Tested on openWB 2.2.1 (Kia EV6): SoC is read on the initial poll and on
subsequent refresh polls, no errors in main.log; RSA round-trip verified
against a locally generated key pair.

* fix(vehicles/kia): remove unused client_secret variable (flake8 F841)
ndrsnhs pushed a commit to ndrsnhs/core that referenced this pull request Aug 24, 2026
…B#3751)

* fix(vehicles/kia): restore SoC via new IDPConnect OAuth2 login

The Kia/Hyundai SoC adapter stopped working: Hyundai/Kia decommissioned
the old Keycloak form login (eu-account.<brand>.com/auth/realms/...),
so getAuthCode() could no longer obtain an authorization code and every
SoC request failed.

This switches the login to the current IDPConnect OAuth2 flow, mirroring
the well-maintained hyundai_kia_connect_api library (KiaUvoApiEU) used by
Home Assistant:

  * GET  /auth/api/v2/user/oauth2/authorize   (obtain session cookies)
  * GET  /auth/api/v1/accounts/certs          (RSA public key as JWK)
  * RSA/PKCS#1 v1.5 encrypt the password (hex)
  * POST /auth/account/signin                 (302 -> ?code=...)
  * POST /auth/api/v2/user/oauth2/token       (client_secret, form-urlencoded)

refreshToken() is updated to the IDPConnect token endpoint and keeps a
rotated refresh_token when the server returns one. If the refresh grant
fails (expired/rotated refresh token), fetch_soc() now falls back to a
full login instead of leaving the module stuck after the first poll -
same strategy as hyundai_kia_connect_api's refresh_access_token().

Password encryption uses 'cryptography' (already pulled in transitively
by requests) with a 'pycryptodome' fallback, so no new hard dependency is
introduced.

Robustness fixes:
  * getAuthCode() uses its own requests.Session for the cookie chain and
    no longer calls getCookies() (dead Keycloak endpoints).
  * RAMDISK_PATH import is wrapped so a missing helpermodules.constants
    export falls back to <openWB-root>/ramdisk instead of crashing the
    whole module import.
  * postHTTP() raises RuntimeError with the HTTP status/error text, and
    the token functions pre-initialise 'response' so a failed request no
    longer masks the real cause with "local variable 'response'
    referenced before assignment".

Tested on openWB 2.2.1 (Kia EV6): SoC is read on the initial poll and on
subsequent refresh polls, no errors in main.log; RSA round-trip verified
against a locally generated key pair.

* fix(vehicles/kia): remove unused client_secret variable (flake8 F841)
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