TickTick's OAuth server requires byte-exact match between the redirect_uri
the plugin sends and the value registered in the developer portal. Users
who register the URI with a trailing slash (common on iOS paste, or by
hand) get rejected with:
error="invalid_grant"
error_description="Invalid redirect: ...vercel.app does not match one
of the registered values: [...vercel.app/]"
(Reported in issue #2.)
Normalize the redirect URI by stripping trailing slashes and whitespace at
all four use/save sites:
- main.ts: startAuthFlow() and exchangeAuthCodeForToken() clean the URI
before sending — protects existing users with a slash already saved
- settings.ts: both Redirect URI inputs strip on save so the persisted
value matches what's displayed
- settings.ts: both descriptions now warn against trailing slashes
- README: new Troubleshooting entry for the invalid_grant error
https://claude.ai/code/session_01V2fsiatACzjUCNwSEhwER9
The previous flat list mixed one-time setup (credentials, OAuth) with
day-to-day options (selection mode, tag position) and persistent debug
fields (access/refresh tokens), making the order confusing.
Now the panel renders one of two views based on whether an access token
is stored:
Setup view (not connected):
- Step 1: Client ID / Client Secret / Redirect URI with a link to
the TickTick Developer Portal and instructions to register the
redirect URI there.
- Step 2: Connect button followed immediately by the manual-paste
authorization code field, with instructions covering both the
automatic redirect and the manual paste paths.
Connected view:
- Header confirms the connection.
- Plugin behavior (selection mode, tag position) is now the primary
content, surfaced immediately.
- Connection details (credentials, tokens, reconnect, disconnect,
manual code paste) are tucked into a collapsed <details> block so
they do not crowd the everyday options.
Also: token fields are now read-only, and a disconnect button clears
stored tokens to re-authorize cleanly.
Restore the manually-pasted authorization code field, labeled as a
fallback for when the deep-link callback into Obsidian fails (e.g. the
Vercel callback page hasn't been redeployed with the obsidian://
redirect, or the OS suppresses the deep link). The seamless deep-link
flow remains the primary path; this field is only used when needed.
- Flip isDesktopOnly to false in manifest.json; the runtime code uses
only platform-safe APIs (requestUrl, crypto.subtle, URLSearchParams).
- Register obsidian://ticktick-callback protocol handler so the OAuth
redirect returns directly into Obsidian on desktop and mobile,
removing the manual copy-paste step.
- Verify state parameter against tempState to mitigate CSRF, and clear
tempCodeVerifier/tempState after a successful exchange.
- Update Vercel callback pages (callback.html, index.html) to redirect
to the obsidian:// deep link instead of displaying the code as text.
Note: the deployed Vercel page must be redeployed for users to see
the new behavior.
- Drop the now-vestigial "Authorization code" paste field from the
settings UI.
Support single-line or paragraph selection via a setting, and allow
the #ticktick tag to be appended or prepended. Defaults to single-line
+ append to fix list formatting issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>