Skip to content

feat: wallet instance lifecycle - list, suspend/revoke, deactivate (SID-AUTH-06) - #170

Merged
leifj merged 4 commits into
mainfrom
feat/wallet-instance-lifecycle
Sep 8, 2026
Merged

feat: wallet instance lifecycle - list, suspend/revoke, deactivate (SID-AUTH-06)#170
leifj merged 4 commits into
mainfrom
feat/wallet-instance-lifecycle

Conversation

@leifj

@leifj leifj commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Client half of sirosfoundation/go-wallet-backend#319 (design in sirosfoundation/go-wallet-backend#195; iOS twin: sirosfoundation/siros-sdk-swift#135). Tracked on the shared rollout page: https://claude.ai/code/artifact/49eb6eed-38ed-41b8-8168-3b5a300c33da

What changes

  • Passkey link at WIA generation. BackendApiClient.generateWIA gains credentialId, and ensureWalletInstanceAttestation passes sessionStore.credentialId. The backend records it as WalletInstance.CredentialID, so suspending or revoking this installation's instance also refuses login with its passkey. Without it, only whole-wallet deactivation gates login.
  • WalletInstance type (sdk/auth), and BackendApiClient.listWalletInstances(), setWalletInstanceStatus(instanceId, status, reason), revokeAllWalletInstances(reason) over GET/PUT /user/session/instances… and POST …/revoke-all. Adds the client's first put() primitive.
  • Facade: SirosWallet.listWalletInstances(), setWalletInstanceStatus(...), deactivateWallet(reason). Deactivate revokes every instance server-side, then forgets the cached account and logs out, since the vault it decrypts no longer exists.
  • Login refusal: AuthServerClient now parses the JSON error member of a failed AS response into AuthException.errorCode (falling back to auth_failed), so a 403 WALLET_SUSPENDED / WALLET_REVOKED is distinguishable from a plain authentication failure. Neither is retryable; apps should say so instead of retrying.

Compatibility

Old backends ignore credential_id and answer 404 on the new endpoints (surfaced as BackendApiException). New backends work with old SDKs. No lockstep release needed.

Tests

BackendApiClientTest: credential_id present only when given; list decodes the backend shape; PUT body and path; revoke-all count. AuthServerClientTest: the 403 code survives into errorCode, non-JSON bodies fall back. SirosWalletTest: deactivateWallet calls revoke-all and removes the active account. testDebugUnitTest and lintDebug pass for auth and wallet; assembleDebugAndroidTest is running as this PR opens and will be confirmed in a comment.

Sample-app UI is out of scope here; the SDK exposes the calls.

🤖 Generated with Claude Code

…ID-AUTH-06)

Client half of go-wallet-backend#319 (issue go-wallet-backend#195).

- generateWIA sends the logged-in passkey's credential_id so the backend
  links the wallet instance to the passkey and a suspended or revoked
  instance also refuses that passkey at login.
- WalletInstance type; BackendApiClient.listWalletInstances /
  setWalletInstanceStatus / revokeAllWalletInstances (+ a PUT primitive).
- SirosWallet.listWalletInstances(), setWalletInstanceStatus(),
  deactivateWallet() - the last revokes every instance server-side and
  then forgets the local account, since the vault it decrypts no longer
  exists.
- AuthServerClient carries the AS's JSON error code into
  AuthException.errorCode, so a 403 WALLET_SUSPENDED / WALLET_REVOKED
  login refusal is distinguishable from a plain auth failure.

Older backends ignore credential_id and answer 404 on the new endpoints,
surfaced as BackendApiException; nothing needs to ship in lockstep.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@leifj

leifj commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Local verification complete: :sdk:auth:testDebugUnitTest, :sdk:wallet:testDebugUnitTest (lifecycle + attestation tests), lintDebug for both modules, and :sdk:wallet:assembleDebugAndroidTest all pass.

leifj and others added 3 commits September 8, 2026 16:18
…tus reply

Parity with the Copilot review on siros-sdk-swift#135: a response without
the instances array is a malformed reply, not an empty wallet; a status
update decodes the whole WalletInstance when the backend returns one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Parity with siros-sdk-swift#135: a malformed reply is an error, not zero
revoked.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…erals (Sonar S1192)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Sep 8, 2026

Copy link
Copy Markdown

@leifj
leifj merged commit 25743ac into main Sep 8, 2026
14 checks passed
@leifj leifj mentioned this pull request Sep 10, 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.

1 participant