Skip to content

Fix Alexa To Reminders: persistent WebView auth + XHR API calls#34

Open
BrutalByte wants to merge 22 commits into
mvan231:mainfrom
BrutalByte:main
Open

Fix Alexa To Reminders: persistent WebView auth + XHR API calls#34
BrutalByte wants to merge 22 commits into
mvan231:mainfrom
BrutalByte:main

Conversation

@BrutalByte

@BrutalByte BrutalByte commented May 31, 2026

Copy link
Copy Markdown

Summary

  • Root cause of auth failure: loadURL navigation to the Alexa API endpoint doesn't send the headers Amazon's AJAX endpoints require (X-Requested-With: XMLHttpRequest, Accept: application/json). Switched fetchListJSON to the same loadHTML + synchronous XHR helper pattern already used for DELETE calls — this sends the correct headers and works within the WebView's authenticated session.
  • Sign-in page: Changed from www.amazon.com homepage to alexa.amazon.com so the user establishes a full Alexa session (not just a general Amazon login) before the API is retried.
  • One-time sign-in: After signing in through the WebView once, Amazon's session cookies persist across all future Scriptable runs. The sign-in prompt only reappears if Amazon expires the session.
  • Reminders list picker: If reminderListName doesn't match any list, an alert lets the user pick from all available lists. The choice is saved to AlexaToReminders/settings.json in iCloud.
  • Verbose logging: All steps are logged with local timestamps for easier debugging.
  • README: Updated the script entry with current setup steps, auth explanation, and list picker behaviour.

Test plan

  • First run: confirm sign-in alert appears, alexa.amazon.com loads in WebView, items sync after tapping Done
  • Subsequent runs: confirm no sign-in prompt, items sync directly
  • Invalid list name: confirm picker alert appears and selection is persisted
  • Session expiry: confirm sign-in prompt reappears automatically

BrutalByte and others added 21 commits May 31, 2026 13:45
- Alexa To Reminders Access: fix makeLogin always returning false after
  WebView login, await main() before Script.complete(), null-guard
  item.value before split, move Reminder.all() outside loop, remove
  debug log calls
- MEE6 LeaderBoard Info: fix async forEach → for...of with await,
  single regex.exec call, break after first player match, remove dead
  WebView block, declare dir with let
- RH-Downloads: parseInt for delta math, name null-guard, declare ab/dir
  with let
- ScriptBackup: recursive backup including subdirectories, skip
  ScriptBackup folder to avoid re-copying old backups
- Twitter Widget: fix checkUpdates===true strict comparison, remove
  hardcoded bearer token
- Weather Overview: fix now.getTime() parentheses, ms→seconds for night
  detection, amtLabel scope, Script.setWidget before complete, and
  several other rendering fixes
- Both Calendar Indicators: fix async splice→filter, isCalEvent guard
  ordering for item.calendar.title, multipleAllDay cross-month fix,
  item.endDate NaN guard for reminders, version bump to 2.8, remove
  duplicate Scriptable headers and dead const backgroundColor
- tests/: full unit test suite (148 tests) covering all scripts with
  injectable mocks for all Scriptable APIs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PostToolUse hook: auto-runs test suite after every file edit
- PreToolUse hook: warns before editing shared scriptable-mocks.js
- /new-script skill: scaffolds new Scriptable widget with required headers
- /gen-test skill: generates test files following project conventions
- scriptable-reviewer agent: audits widgets for size/API/pattern issues
- context7 MCP server: live Scriptable API documentation lookup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parse response as string first and catch JSON.parse errors to avoid
crashing when Amazon returns HTML (login wall, CAPTCHA, session expiry).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Guard against null reminderCalendar when list name doesn't match
- Log actual error message in catch block instead of generic string

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows an action sheet of all available reminder lists on the device
instead of silently exiting when reminderListName doesn't match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Saves the user-selected list to AlexaToReminders/settings.json so
subsequent runs use it automatically without prompting again.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds timestamped vlog() helper and covers every major step:
auth check, login flow, list lookup, JSON parse, per-item
create/skip/delete, and sync summary counts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Validate parsed JSON is a plain object before iterating lists;
  Amazon was returning a quoted string which for..in treated as chars
- makeLogin now always re-checks the API after loading the homepage
  instead of assuming success when the sign-in indicator is absent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Script fixes:
- vlog now uses local device time instead of UTC
- checkIfUserIsAuthenticated returns true only for 2xx (was accepting 5xx)
- makeLogin presents fallback WebView when homepage looks logged-in but
  API still returns 401, so user can always recover a stale session

Test fixes:
- makeCheckIfUserIsAuthenticated helper updated to match 2xx-only logic
- makeMakeLogin helper updated to match fallback WebView behavior
- Added: 5xx/302 rejection tests for auth check
- Added: fallback WebView test for makeLogin
- Added: JSON response type validation suite (string/null/array rejection)
- Added: settings persistence suite (save, load, fallback, key preservation)

160 tests, 0 failures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Loading the Amazon homepage wasn't establishing the right cookies for
the shopping list API. Loading the API URL directly forces Amazon to
set the correct session cookies for that endpoint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Request and WebView use separate cookie stores on iOS — logging in
via WebView had no effect on subsequent Request calls. Now a single
sessionView WebView instance is used for both auth checking and data
fetching, ensuring cookies are consistent throughout the session.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Scriptable's Request and WebView have completely separate cookie stores
on iOS — no amount of WebView login would affect Request calls.

Replaced all Amazon API calls with wvFetch(), which executes fetch()
inside the WebView's JS context using its own cookie jar. The same
sessionView is loaded on amazon.com first to establish same-origin
context, then all GET and DELETE calls run through it.

Also simplified auth flow: ensureAuthenticated() tries the API once,
presents WebView if it fails, then retries once — and passes the
already-fetched JSON directly to synchronizeReminders() to avoid
a redundant second API call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Scriptable's evaluateJavaScript completion callback does not support
Promises. fetch() returns a Promise as the expression value before
completion() fires, causing the "unsupported type" error.

Replaced fetch() with XMLHttpRequest using onload/onerror callbacks,
which call completion() directly and work correctly with Scriptable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The completion() callback pattern causes "unsupported type" errors when
called asynchronously. Scriptable natively resolves JS Promises returned
from evaluateJavaScript, so wrapping the XHR in a Promise avoids the
issue entirely. Removed useCallback flag.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dropped the WebView-for-everything approach. Instead:
- Show a WebView for login once, then extract document.cookie
- Inject extracted cookies as a Cookie header on all Request calls
- Cache cookies in settings.json so subsequent runs skip the WebView
- Fall back to login WebView only when cached cookies are missing/expired

evaluateJavaScript on a simple synchronous expression (document.cookie)
works reliably; async XHR/Promise patterns do not in this Scriptable env.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eliminates evaluateJavaScript for all auth-sensitive operations.
Amazon's HttpOnly auth cookies stay in WKWebView and are used
automatically by loadURL navigations and loadHTML XHR calls.

- fetchListJSON: loadURL to API endpoint, getHTML to read response,
  extract JSON from HTML (pre tag or stripped body text)
- deleteListItem: loadHTML with inline synchronous XHR helper page,
  getHTML to read the status result — no evaluateJavaScript needed
- ensureAuthenticated: try API directly, present WebView if it fails,
  retry once after user dismisses

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… cookies

amazon.com homepage auth is insufficient for the Alexa Shopping List API.
The WebView must visit /alexa-lists first so Amazon sets the Alexa session
state before the API call is retried. Also adds an instruction alert so
the user knows to wait for the list to load before tapping Done.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Direct loadURL navigation to the API doesn't send the headers Amazon
requires for its AJAX endpoints (X-Requested-With, Accept: application/json).
Switched to the same loadHTML+XHR pattern used for DELETE so those headers
are included and status can be checked. Also changed the sign-in WebView
to load https://alexa.amazon.com (the full Alexa app) instead of the
www.amazon.com/alexa-lists stub page (only 2KB, no real auth state).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rewrites the script entry to cover the new authentication approach
(persistent WKWebView session via alexa.amazon.com), setup steps,
the Reminders list picker, and settings persistence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@BrutalByte BrutalByte changed the title QA fixes and full test suite across all scripts Fix Alexa To Reminders: persistent WebView auth + XHR API calls Jun 6, 2026
@dehsgr

dehsgr commented Jun 7, 2026

Copy link
Copy Markdown

@BrutalByte thanks for your work! I just tested successfully in my environment. One thing you IMHO should correct: You shouldn’t hard-code alexa.amazon.com URL, as this also differs for region. Either this should be configurable too. IMHO a better solution might be to derive it from configured Amazon URL. Isn‘t it?

Fixes regional support — e.g. amazon.co.uk now correctly uses alexa.amazon.co.uk.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@BrutalByte

Copy link
Copy Markdown
Author

Good point!

@BrutalByte thanks for your work! I just tested successfully in my environment. One thing you IMHO should correct: You shouldn’t hard-code alexa.amazon.com URL, as this also differs for region. Either this should be configurable too. IMHO a better solution might be to derive it from configured Amazon URL. Isn‘t it?

Good call — fixed in this PR. The Alexa URL is now derived from baseURL using a simple subdomain swap (www.→alexa.), so regional users only need to set baseURL and everything follows from that.

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