Skip to content

feat(vag_vehicle): sign in with email and password, report SoC age - #156

Merged
frahlg merged 3 commits into
mainfrom
143-vag-auto-login
Oct 3, 2026
Merged

frahlg merged 3 commits into
mainfrom
143-vag-auto-login

Conversation

@frahlg

@frahlg frahlg commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Fixes #143.

Problem

The EU Data Act portal session lasts about an hour. vag_vehicle 0.1.1 needs the owner to paste a new Cookie header each time, so the car's SoC disappears every hour. The driver also had no clock, so it could not tell how old a reading was. After every restart it marked the first file stale, even when the reading was fresh.

Change

Sign-in (needs srcfl/ftw#1505). With email and password the driver signs in through identity.vwgroup.io, following evcc's vehicle/vw/eudataact:

  1. Authorize.
  2. Post the emailPasswordForm identifier form.
  3. Read the password page's window._IDK state with patterns. No JavaScript runs.
  4. Post the password.
  5. Walk the redirects back to the portal, skipping an optional marketing consent page through its callback.

How it behaves:

  • The host keeps the session cookies, and each redirect is a separate host.http_request that Core checks against allowed_hosts.
  • When the portal answers 401/403 or redirects, the driver signs in again on the next poll, a second later.
  • A failed sign-in waits 15 minutes, so a wrong password cannot lock the account. VW's reason (for example login.errors.password_invalid) goes to the log; the password never does.
  • On an older Core without http_request, a pasted cookie works as before. Email and password alone log what is missing.

Age. The SoC point's timestampUtc is when the car measured it, and the portal's Date header is VW's time now. Their difference is the reading's age, reported as vehicle_soc_age_s.

  • A reading older than 20 minutes is not fresh, even in a new file.
  • A fresh first file after start is no longer marked stale.
  • Ages are reported only in sign-in mode, because http_get returns no headers.

Metadata.

  • identity.vwgroup.io joins http_hosts, so FTW setup fills allowed_hosts with both hosts.
  • password joins config_secrets.
  • auth_post_paths lists the two form paths for each of the four brand client ids.
  • Existing installs whose allowed_hosts lists only the portal get a log line telling them to add the identity host.

Channel builder (tools/ftw_repository.py).

  • Accepts auth_post_paths next to auth_post_path and validates each path.
  • Publishes the list in signed metadata, which Core reads as of feat(drivers): host.http_request with a per-driver cookie jar ftw#1505.
  • A read-only driver that calls host.http_request gets a guard for it too. GET passes, and POST passes only to a declared path. Drivers that do not call it, and a single auth_post_path, keep their old guard, so vag_vehicle is the only published artifact that changes (check-versions against drivers-beta: changed [vag_vehicle]).

Host API profile. Lists http_request and http_cookies_clear.

Paired PR

srcfl/ftw#1505 adds host.http_request, the cookie jar and auth_post_paths to Core. Merge that first. This driver runs on an older Core in cookie mode, and the guard skips http_request when the host lacks it.

Validation

  • New test_vag_vehicle_login.lua against a scripted VW sign-in and portal:
    • sign-in, with the form state carried through, HTML entities decoded and the email encoded
    • no password at the identifier step
    • the landing page is not fetched
    • a 300 s age from the Date header, growing on replay
    • re-sign-in after the session ends
    • a two-hour-old reading is not fresh
    • the 15-minute back-off after a wrong password
    • an off-host redirect is refused and never requested
    • the password is absent from logs
    • an old host is explained
  • The existing cookie-mode test is unchanged and passes.
  • Channel tests: the multi-path guard, the http_request guard only in vag_vehicle, and vag_vehicle added to the sign-in exemptions.
  • The same scripted run in FTW's gopher-lua host: sign-in, SoC 63 %, age 300 s.
  • make check: 4808 passed, 923 skipped.
  • Not yet run against the live portal or a car. The client ids and form paths come from evcc. If VW's form action differs from /signin-service/v1/<client>/login/identifier, Core refuses the POST and the log names the path.

🤖 Generated with Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-03T05:30:45.013636Z 6aeb86a PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6aeb86a95e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +899 to +903
-- An ended session answers 401/403, or redirects to the sign-in page.
if r.status == 401 or r.status == 403 or (r.status >= 300 and r.status < 400) then
session_ok = false
return nil, "HTTP 401: session ended"
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retry sessions expired during dataset download

When the session expires on the /download request, this branch sets session_ok = false, but the download error handler at driver_poll only logs download failed and returns the normal 300000 ms interval. Unlike expiry during metadata or list retrieval, renewal is therefore delayed by five minutes rather than the intended one-second retry; handle the session-ended state in the download failure path as well.

Useful? React with 👍 / 👎.

frahlg and others added 2 commits October 3, 2026 07:37
The EU Data Act portal session lasts about an hour, and the driver
needed a pasted Cookie header each time (#143). On an FTW Core with
host.http_request the driver now signs in itself, as evcc does: the
identity form, the password page's window._IDK state, then the
redirects back to the portal, skipping an optional marketing consent
page. The host keeps the session cookies; each redirect is a separate
request checked against allowed_hosts. A failed sign-in waits 15
minutes. A pasted cookie still works on older hosts.

The SoC point's timestampUtc and the portal's Date header give the
reading's age without a wall clock. The driver reports it as
vehicle_soc_age_s, and a reading older than 20 minutes is not fresh.

The channel builder accepts auth_post_paths (a list) next to
auth_post_path, publishes it in signed metadata, and guards POST
through host.http_request the same way as host.http_post. The host
API profile lists http_request and http_cookies_clear.

Paired with srcfl/ftw (host.http_request, auth_post_paths).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Adding the guard to every read-only artifact changed the bytes of all
of them, and the signed channel would have required a version bump for
each. A driver gets the http_request guard only when its source calls
host.http_request, and a single auth_post_path keeps its old guard, so
only vag_vehicle's artifact changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@frahlg
frahlg force-pushed the 143-vag-auto-login branch from 6aeb86a to 08b6391 Compare October 3, 2026 05:38
A Core without auth_post_paths refuses a read-only driver that holds
http.post but declares no auth_post_path, so beta installs on the
current Core would lose the driver. The first path now sits in
auth_post_path. That Core has no host.http_request, so the driver runs
in cookie mode and never posts there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@frahlg
frahlg merged commit 06f62f4 into main Oct 3, 2026
5 checks passed
@frahlg
frahlg deleted the 143-vag-auto-login branch October 3, 2026 05:41
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.

[driver] vag_vehicle autorising

1 participant