Skip to content

Commit 7fc9a59

Browse files
kronosapiensclaude
andcommitted
fix: Fix SessionProvider.probe() returning undefined after ingestSessionFromRedirect
Fixes a regression introduced in #2401 where probe() would always return undefined after calling ingestSessionFromRedirect on a deep link redirect (Capacitor, etc). The issue: #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. Changes: - Separate concerns: _setSigningKeys() (sync) and _resolvePreset() (async) - Restore on-demand retrieval: probe/connect/username call tryRetrieveSessionAccount - Rename tryRetrieveFromQueryOrStorage to tryRetrieveSessionAccount for clarity - Mark tryRetrieveSessionAccount as private since it's internal restoration logic - Add comments explaining the two session entry points (localStorage and URL query) This restores the pre-#2401 behavior of checking storage on every public method call, allowing both web redirects (query param flow) and deep links (explicit call flow) to work correctly. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 94dae94 commit 7fc9a59

File tree

2 files changed

+72
-42
lines changed

2 files changed

+72
-42
lines changed

packages/controller/src/session/provider.ts

Lines changed: 55 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>;
@@ -388,6 +397,7 @@ export default class SessionProvider extends BaseProvider {
388397
}
389398
}
390399

400+
// Ingest session from URL query params (web redirect flow)
391401
if (window.location.search.includes("startapp")) {
392402
const params = new URLSearchParams(window.location.search);
393403
const session = params.get("startapp");
@@ -411,6 +421,9 @@ export default class SessionProvider extends BaseProvider {
411421
}
412422
}
413423

424+
const signerString = localStorage.getItem("sessionSigner");
425+
const signer = signerString ? JSON.parse(signerString) : null;
426+
414427
if (!sessionRegistration || !signer) {
415428
return;
416429
}

packages/keychain/src/utils/api/generated.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5465,6 +5465,7 @@ export type Session = Node & {
54655465
/** Whether the session has been revoked */
54665466
isRevoked: Scalars["Boolean"];
54675467
metadata?: Maybe<SessionMetadata>;
5468+
sessionKeyGUID?: Maybe<Scalars["String"]>;
54685469
signer?: Maybe<Signer>;
54695470
updatedAt: Scalars["Time"];
54705471
};
@@ -5604,6 +5605,22 @@ export type SessionWhereInput = {
56045605
isRevokedNEQ?: InputMaybe<Scalars["Boolean"]>;
56055606
not?: InputMaybe<SessionWhereInput>;
56065607
or?: InputMaybe<Array<SessionWhereInput>>;
5608+
/** session_key_guid field predicates */
5609+
sessionKeyGUID?: InputMaybe<Scalars["String"]>;
5610+
sessionKeyGUIDContains?: InputMaybe<Scalars["String"]>;
5611+
sessionKeyGUIDContainsFold?: InputMaybe<Scalars["String"]>;
5612+
sessionKeyGUIDEqualFold?: InputMaybe<Scalars["String"]>;
5613+
sessionKeyGUIDGT?: InputMaybe<Scalars["String"]>;
5614+
sessionKeyGUIDGTE?: InputMaybe<Scalars["String"]>;
5615+
sessionKeyGUIDHasPrefix?: InputMaybe<Scalars["String"]>;
5616+
sessionKeyGUIDHasSuffix?: InputMaybe<Scalars["String"]>;
5617+
sessionKeyGUIDIn?: InputMaybe<Array<Scalars["String"]>>;
5618+
sessionKeyGUIDIsNil?: InputMaybe<Scalars["Boolean"]>;
5619+
sessionKeyGUIDLT?: InputMaybe<Scalars["String"]>;
5620+
sessionKeyGUIDLTE?: InputMaybe<Scalars["String"]>;
5621+
sessionKeyGUIDNEQ?: InputMaybe<Scalars["String"]>;
5622+
sessionKeyGUIDNotIn?: InputMaybe<Array<Scalars["String"]>>;
5623+
sessionKeyGUIDNotNil?: InputMaybe<Scalars["Boolean"]>;
56075624
/** updated_at field predicates */
56085625
updatedAt?: InputMaybe<Scalars["Time"]>;
56095626
updatedAtGT?: InputMaybe<Scalars["Time"]>;

0 commit comments

Comments
 (0)