An easy-to-host PDS on the ATProtocol, iPhone and MacOS. Maintain control of your keys and data, always.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(identity-wallet): Add OAuthError type and startup token restoration (MM-149 phase 7 tasks 1-2)

authored by

Malpercio and committed by
Tangled
94ca3be1 cfbb955d

+43 -1
+23 -1
apps/identity-wallet/src-tauri/src/lib.rs
··· 7 7 use crypto::{build_did_plc_genesis_op_with_external_signer, CryptoError, DidKeyUri}; 8 8 use serde::{Deserialize, Serialize}; 9 9 use std::sync::LazyLock; 10 - use tauri::Manager; 10 + use tauri::{Emitter, Manager}; 11 11 use tauri_plugin_deep_link::DeepLinkExt; 12 12 13 13 // ── Request / response types ──────────────────────────────────────────────── ··· 411 411 let state = app_handle.state::<oauth::AppState>(); 412 412 oauth::handle_deep_link(event.urls(), &state); 413 413 }); 414 + 415 + // On relaunch: restore persisted session from Keychain and notify frontend. 416 + // The 300 ms delay lets the SvelteKit app boot and register its event listener 417 + // before the event fires — emitting synchronously here would be dropped. 418 + if let Some((access, refresh)) = keychain::load_oauth_tokens() { 419 + { 420 + let state = app.state::<oauth::AppState>(); 421 + *state.oauth_session.lock().unwrap() = Some(oauth::OAuthSession { 422 + access_token: access, 423 + refresh_token: refresh, 424 + // expires_at = 0 ensures OAuthClient refreshes immediately on first use. 425 + expires_at: 0, 426 + dpop_nonce: None, 427 + }); 428 + } 429 + let handle = app.handle().clone(); 430 + tauri::async_runtime::spawn(async move { 431 + tokio::time::sleep(std::time::Duration::from_millis(300)).await; 432 + handle.emit("auth_ready", ()).ok(); 433 + }); 434 + } 435 + 414 436 Ok(()) 415 437 }) 416 438 .invoke_handler(tauri::generate_handler![
+20
apps/identity-wallet/src/lib/ipc.ts
··· 155 155 password: string, 156 156 ): Promise<DIDCeremonyResult> => 157 157 invoke('perform_did_ceremony', { handle, password }); 158 + 159 + // ── OAuth ─────────────────────────────────────────────────────────────────── 160 + // 161 + // These variants must exactly match the Rust `OAuthError` enum in oauth.rs. 162 + // Rust serializes them as `{ "code": "SCREAMING_SNAKE_CASE" }` via: 163 + // #[serde(rename_all = "SCREAMING_SNAKE_CASE", tag = "code")] 164 + 165 + export type OAuthError = 166 + | { code: 'DPOP_KEY_GEN_FAILED' } 167 + | { code: 'DPOP_KEY_INVALID' } 168 + | { code: 'DPOP_PROOF_FAILED' } 169 + | { code: 'KEYCHAIN_ERROR' } 170 + | { code: 'STATE_MISMATCH' } 171 + | { code: 'CALLBACK_ABANDONED' } 172 + | { code: 'PAR_FAILED' } 173 + | { code: 'TOKEN_EXCHANGE_FAILED' } 174 + | { code: 'TOKEN_REFRESH_FAILED' } 175 + | { code: 'NOT_AUTHENTICATED' }; 176 + 177 + export const startOAuthFlow = (): Promise<void> => invoke('start_oauth_flow');