Skip to content

fix(oauth): keep multiple Kimi accounts via JWT user_id#156

Closed
Wibias wants to merge 1 commit into
lidge-jun:mainfrom
Wibias:fix/kimi-multiauth-identity
Closed

fix(oauth): keep multiple Kimi accounts via JWT user_id#156
Wibias wants to merge 1 commit into
lidge-jun:mainfrom
Wibias:fix/kimi-multiauth-identity

Conversation

@Wibias

@Wibias Wibias commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Bug: logging into a second Kimi (Moonshot) account replaced the active slot and effectively logged out the first, because Kimi tokens were stored with no accountId/email.
  • Cause: saveCredential only appends when identity is present; identity-less providers replace the active account in place.
  • Fix: decode the Kimi access/refresh JWT and set accountId from user_id (fallback sub), plus optional lowercased email. Distinct users append; same user_id upserts.

Notes

  • Pre-fix identity-less Kimi rows may remain as a one-time sibling after the next login of the same human; remove the stale row via accounts UI/API if needed.
  • Opaque/non-JWT tokens still use replace-active semantics (no worse than before).

Test plan

  • bun test tests/kimi-oauth-identity.test.ts tests/oauth-store-multi.test.ts
  • GUI: login Kimi account A, then account B — both appear; active is B; switch back to A
  • Re-login account A — still one A row (upsert), B remains

Kimi login stored no accountId, so saveCredential replaced the active slot and a second login wiped the first. Map JWT user_id/sub onto accountId (email lowercased when present).
lidge-jun pushed a commit that referenced this pull request Jul 19, 2026
Kimi login stored no accountId, so saveCredential replaced the active slot and a second login wiped the first. Map JWT user_id/sub onto accountId (email lowercased when present).

(cherry picked from commit a6d824e)

Community PR #156 absorb; source head a6d824e.
lidge-jun added a commit that referenced this pull request Jul 19, 2026
… across tokens

Repairs for community PR #156 absorb (source head a6d824e), Sol review
findings:

- saveCredential: when an identified credential arrives and the provider's
  ACTIVE row is identity-less (pre-fix legacy), upgrade that row in place
  instead of appending — the stale row previously stayed selectable and could
  re-refresh into a duplicate of the same human. Distinct users still append.
- identityFromKimiTokens: user_id is now preferred ACROSS both tokens
  (access.user_id ?? refresh.user_id ?? access.sub ?? refresh.sub) so a
  refresh-token user_id beats an access-token sub, matching the documented
  preference.
- Wiring test: refreshKimiToken (production parseTokenPayload path) asserted
  to carry JWT identity, so removing the identity spread can no longer pass.
- Migration + cross-token + wiring activation tests added; oauth-store-multi
  contract suite still green (13 pass).

Co-authored-by: Wibias <37517432+Wibias@users.noreply.github.com>
@lidge-jun

Copy link
Copy Markdown
Owner

Thanks — the root cause (identity-less Kimi tokens replacing the active slot) and the JWT user_id extraction were right, and it's landed on dev with your authorship preserved plus maintainer repairs:

  • a4086c87 — your PR fix(oauth): keep multiple Kimi accounts via JWT user_id #156 commit, absorbed as-is (author: @Wibias, source head a6d824e).
  • 29506b85 — maintainer repairs (Co-authored-by: @Wibias) closing three review findings:
    1. a pre-fix identity-less Kimi row was duplicated (not migrated) on the first post-fix login — the active legacy row is now upgraded in place, while distinct users still append;
    2. user_id was not actually preferred across tokens (access sub beat refresh user_id) — preference is now access.user_id ?? refresh.user_id ?? access.sub ?? refresh.sub;
    3. tests bypassed the production parseTokenPayload wiring — a mocked refreshKimiToken test now asserts the returned credential carries identity.

Full suite green (3,087 tests). Closing in favor of the dev-landed commits; ships with the next release.

@lidge-jun lidge-jun closed this Jul 19, 2026
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