Skip to content

Commit 22d7d23

Browse files
kronosapiensclaude
andauthored
fix: Fix probe() returning undefined after ingestSessionFromRedirect (#2409)
## Summary Fixes a regression introduced in #2401 where `SessionProvider.probe()` would always return undefined after calling `ingestSessionFromRedirect()` on a deep link redirect (Capacitor, mobile apps, etc). **Root cause:** #2401 moved session restoration into `_init()`, which runs once during construction. For deep links, the session data arrives *after* construction via `ingestSessionFromRedirect()`, so `_init()` never sees it. Result: `probe()` had no path to create the account. **Solution:** Restore on-demand session retrieval in public methods (`probe()`, `connect()`, `username()`), matching the pre-#2401 behavior. Both web redirects (query param flow) and deep links (explicit call flow) now work correctly. ## Changes - Split initialization: `_setSigningKeys()` (sync) handles signer setup, `_resolvePreset()` (async) handles preset resolution - Restore on-demand account retrieval: `tryRetrieveSessionAccount()` called by public methods when account not set - Rename `tryRetrieveFromQueryOrStorage` → `tryRetrieveSessionAccount` for clarity and mark as private - Add comments explaining the two session entry points (localStorage and URL query params) - Improve variable naming: `_ready` → `_readyPromise` Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 6e6b4b4 commit 22d7d23

File tree

1 file changed

+54
-42
lines changed

1 file changed

+54
-42
lines changed

packages/controller/src/session/provider.ts

Lines changed: 54 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ export default class SessionProvider extends BaseProvider {
5050
protected _disconnectRedirectUrl?: string;
5151
protected _policies: ParsedSessionPolicies;
5252
protected _preset?: string;
53-
private _ready: Promise<void>;
5453
protected _keychainUrl: string;
5554
protected _apiUrl: string;
5655
protected _publicKey!: string;
5756
protected _sessionKeyGuid!: string;
5857
protected _signupOptions?: AuthOptions;
58+
private _readyPromise: Promise<void>;
5959
public reopenBrowser: boolean = true;
6060

6161
constructor({
@@ -102,35 +102,23 @@ export default class SessionProvider extends BaseProvider {
102102
this._apiUrl = apiUrl ?? API_URL;
103103
this._signupOptions = signupOptions;
104104

105-
// Eagerly start async init: resolve preset policies (if any),
106-
// then try to restore an existing session from storage.
107-
// All public async methods await this before proceeding.
108-
this._ready = this._init();
105+
this._setSigningKeys();
106+
this._readyPromise = this._resolvePreset();
109107

110108
if (typeof window !== "undefined") {
111109
(window as any).starknet_controller_session = this;
112110
}
113111
}
114112

115-
private async _init(): Promise<void> {
116-
if (this._preset) {
117-
const config = await loadConfig(this._preset);
118-
if (!config) {
119-
throw new Error(`Failed to load preset: ${this._preset}`);
120-
}
121-
122-
const sessionPolicies = getPresetSessionPolicies(config, this._chainId);
123-
if (!sessionPolicies) {
124-
throw new Error(
125-
`No policies found for chain ${this._chainId} in preset ${this._preset}`,
126-
);
127-
}
128-
129-
this._policies = parsePolicies(sessionPolicies);
130-
}
131-
132-
const account = this.tryRetrieveFromQueryOrStorage();
133-
if (!account) {
113+
private _setSigningKeys(): void {
114+
const signerString = localStorage.getItem("sessionSigner");
115+
if (signerString) {
116+
const signer = JSON.parse(signerString);
117+
this._publicKey = signer.pubKey;
118+
this._sessionKeyGuid = signerToGuid({
119+
starknet: { privateKey: encode.addHexPrefix(signer.privKey) },
120+
});
121+
} else {
134122
const pk = stark.randomAddress();
135123
this._publicKey = ec.starkCurve.getStarkKey(pk);
136124

@@ -144,20 +132,27 @@ export default class SessionProvider extends BaseProvider {
144132
this._sessionKeyGuid = signerToGuid({
145133
starknet: { privateKey: encode.addHexPrefix(pk) },
146134
});
147-
} else {
148-
const pk = localStorage.getItem("sessionSigner");
149-
if (!pk) throw new Error("failed to get sessionSigner");
135+
}
136+
}
150137

151-
const jsonPk: {
152-
privKey: string;
153-
pubKey: string;
154-
} = JSON.parse(pk);
138+
// Resolve preset policies asynchronously.
139+
// Public methods await this before proceeding.
140+
private async _resolvePreset(): Promise<void> {
141+
if (!this._preset) return;
155142

156-
this._publicKey = jsonPk.pubKey;
157-
this._sessionKeyGuid = signerToGuid({
158-
starknet: { privateKey: encode.addHexPrefix(jsonPk.privKey) },
159-
});
143+
const config = await loadConfig(this._preset);
144+
if (!config) {
145+
throw new Error(`Failed to load preset: ${this._preset}`);
160146
}
147+
148+
const sessionPolicies = getPresetSessionPolicies(config, this._chainId);
149+
if (!sessionPolicies) {
150+
throw new Error(
151+
`No policies found for chain ${this._chainId} in preset ${this._preset}`,
152+
);
153+
}
154+
155+
this._policies = parsePolicies(sessionPolicies);
161156
}
162157

163158
private validatePoliciesSubset(
@@ -247,17 +242,31 @@ export default class SessionProvider extends BaseProvider {
247242
}
248243

249244
async username() {
250-
await this._ready;
245+
await this._readyPromise;
246+
247+
if (!this.account) {
248+
this.tryRetrieveSessionAccount();
249+
}
250+
251251
return this._username;
252252
}
253253

254254
async probe(): Promise<WalletAccount | undefined> {
255-
await this._ready;
255+
await this._readyPromise;
256+
257+
if (!this.account) {
258+
this.tryRetrieveSessionAccount();
259+
}
260+
256261
return this.account;
257262
}
258263

259264
async connect(): Promise<WalletAccount | undefined> {
260-
await this._ready;
265+
await this._readyPromise;
266+
267+
if (!this.account) {
268+
this.tryRetrieveSessionAccount();
269+
}
261270

262271
if (this.account) {
263272
return this.account;
@@ -319,7 +328,7 @@ export default class SessionProvider extends BaseProvider {
319328
};
320329
localStorage.setItem("session", JSON.stringify(session));
321330

322-
this.tryRetrieveFromQueryOrStorage();
331+
this.tryRetrieveSessionAccount();
323332

324333
return this.account;
325334
} catch (e) {
@@ -367,15 +376,15 @@ export default class SessionProvider extends BaseProvider {
367376
return promise;
368377
}
369378

370-
tryRetrieveFromQueryOrStorage() {
379+
// Try to retrieve the session account from localStorage or URL query params
380+
private tryRetrieveSessionAccount() {
371381
if (this.account) {
372382
return this.account;
373383
}
374384

375-
const signerString = localStorage.getItem("sessionSigner");
376-
const signer = signerString ? JSON.parse(signerString) : null;
377385
let sessionRegistration: SessionRegistration | null = null;
378386

387+
// Load session from localStorage (saved by ingestSessionFromRedirect or connect)
379388
const sessionString = localStorage.getItem("session");
380389
if (sessionString) {
381390
const parsed = JSON.parse(sessionString) as Partial<SessionRegistration>;
@@ -411,6 +420,9 @@ export default class SessionProvider extends BaseProvider {
411420
}
412421
}
413422

423+
const signerString = localStorage.getItem("sessionSigner");
424+
const signer = signerString ? JSON.parse(signerString) : null;
425+
414426
if (!sessionRegistration || !signer) {
415427
return;
416428
}

0 commit comments

Comments
 (0)