@polyester/sdk 0.22.3 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/account-signer/types.d.ts +1 -1
- package/dist/account-signer/types.js.map +1 -1
- package/dist/gen/auth/v1/auth_pb.d.ts +88 -46
- package/dist/gen/auth/v1/auth_pb.d.ts.map +1 -1
- package/dist/gen/auth/v1/auth_pb.js +43 -11
- package/dist/gen/auth/v1/auth_pb.js.map +1 -1
- package/dist/gen/auth/v1/subaccounts_pb.d.ts +5 -17
- package/dist/gen/auth/v1/subaccounts_pb.d.ts.map +1 -1
- package/dist/gen/auth/v1/subaccounts_pb.js +1 -1
- package/dist/gen/auth/v1/subaccounts_pb.js.map +1 -1
- package/dist/gen/chain/withdraw/v1/withdraw_pb.d.ts +2 -2
- package/dist/gen/chain/withdraw/v1/withdraw_pb.js.map +1 -1
- package/dist/gen/marketoverview/v1/marketoverview_pb.d.ts +2 -1
- package/dist/gen/marketoverview/v1/marketoverview_pb.d.ts.map +1 -1
- package/dist/gen/marketoverview/v1/marketoverview_pb.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/services/auth/account-signer-auth.d.ts +7 -3
- package/dist/services/auth/account-signer-auth.d.ts.map +1 -1
- package/dist/services/auth/account-signer-auth.js +28 -13
- package/dist/services/auth/account-signer-auth.js.map +1 -1
- package/dist/services/auth/auth.d.ts +19 -14
- package/dist/services/auth/auth.d.ts.map +1 -1
- package/dist/services/auth/auth.js +24 -14
- package/dist/services/auth/auth.js.map +1 -1
- package/dist/services/auth/auth.types.d.ts +2 -2
- package/dist/services/auth/wallet-challenge.schemas.js +20 -0
- package/dist/services/auth/wallet-challenge.schemas.js.map +1 -0
- package/dist/services/lifecycle/lifecycle.schemas.d.ts +42 -42
- package/dist/services/orders/orders-input.schemas.d.ts +1 -1
- package/dist/services/orders/orders-output.schemas.d.ts +6 -6
- package/dist/services/subaccounts/subaccounts.d.ts +1 -1
- package/dist/services/subaccounts/subaccounts.js +1 -1
- package/dist/services/subaccounts/subaccounts.js.map +1 -1
- package/dist/services/subaccounts/subaccounts.schemas.d.ts +8 -10
- package/dist/services/subaccounts/subaccounts.schemas.d.ts.map +1 -1
- package/dist/services/subaccounts/subaccounts.schemas.js +4 -5
- package/dist/services/subaccounts/subaccounts.schemas.js.map +1 -1
- package/dist/services/trading-withdraws/index.d.ts +1 -1
- package/dist/services/trading-withdraws/trading-withdraws.d.ts +10 -72
- package/dist/services/trading-withdraws/trading-withdraws.d.ts.map +1 -1
- package/dist/services/trading-withdraws/trading-withdraws.js +8 -84
- package/dist/services/trading-withdraws/trading-withdraws.js.map +1 -1
- package/dist/services/trading-withdraws/trading-withdraws.types.d.ts +2 -2
- package/dist/services/trading-withdraws/wallet-message.js +43 -0
- package/dist/services/trading-withdraws/wallet-message.js.map +1 -0
- package/dist/services/triggers/trigger-input.schemas.d.ts +2 -2
- package/dist/services/triggers/triggers-output.schemas.d.ts +18 -18
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-signer-auth.js","names":["#accountSignerConfig","#environmentFingerprint","#subaccounts","#tokenStorage","#realtime","#sessionStore","#assertAccountSignerEnvironment","#accountSigner","#accountIdentity","#identityFromSigner","#notifyStateChange","#login","#resolveAccountSigner","#getEnvironmentSession","#loginMethod","#isAuthenticated","#mainAccountId","#activeAccountId","#walletProvider","#getEnvironmentBoundToken","#clearExpiredSessionState","#getCurrentTokenStorageOptions","#resolveRefreshProvider"],"sources":["../../../src/services/auth/account-signer-auth.ts"],"sourcesContent":["import { AuthService } from \"./auth.js\";\nimport { AuthenticationError, ConfigurationError } from \"../../shared/errors.js\";\nimport { AuthSessionStore } from \"./session.js\";\nimport type { AccountSigner, AccountSignerConfig, HexAddress } from \"../../account-signer/types.js\";\nimport { assertAccountSigner, resolveAccountSigner } from \"../../account-signer/types.js\";\nimport { EventEmitter } from \"../../utils/event-emitter.js\";\nimport { isJwtValid, getJwtTimeToExpiry } from \"../../utils/jwt.js\";\nimport type { SubaccountsService } from \"../subaccounts/index.js\";\nimport type {\n AuthState,\n AuthHydrationData,\n AuthLoginMethod,\n SessionData,\n ActiveAccountInfo,\n} from \"./session.types.js\";\nimport type { PolyesterRealtime } from \"../../realtime/index.js\";\nimport type { PolyesterEnvironment } from \"../../environment.js\";\nimport type { AuthAndPublicApiTransports } from \"../../shared/transports.js\";\nimport {\n createAuthTokenStorageSetOptions,\n type AuthTokenStorage,\n type AuthTokenStorageSetOptions,\n} from \"./token-storage.js\";\n\nexport interface AccountSignerAuthEvents {\n authenticated: { accountId: string; username: string };\n loggedOut: void;\n error: { code: string; message: string };\n servicesReady: void;\n stateChange: AuthState;\n}\n\nexport interface LoginResult {\n accountId: string;\n username: string;\n expiresAt: Date;\n}\n\nexport interface LoginOptions {\n /**\n * The wallet provider to use for login.\n */\n provider: \"metamask\" | \"turnkey\" | \"other\";\n loginMethod?: AuthLoginMethod | null;\n}\n\nexport interface CreateSubaccountParams {\n /** The account signer for the new subaccount (caller derives this, e.g. via Turnkey saltNonce) */\n accountSigner: AccountSigner;\n /** Optional human-readable label for this subaccount */\n label?: string;\n /** Wallet provider hint (e.g. \"turnkey\", \"metamask\"). Defaults to \"wallet\" */\n walletProvider?: string;\n}\n\nexport interface CreateSubaccountResult {\n subaccountId: string;\n smartAccountSaltNonce: number;\n revision: string;\n}\n\ninterface AccountIdentity {\n accountAddress: HexAddress;\n ownerAddress?: HexAddress;\n}\n\n/**\n * Coordinates wallet/account-signer authentication, session storage, subaccount selection, and session refresh.\n */\nexport class AccountSignerAuthService extends AuthService {\n readonly events = new EventEmitter<AccountSignerAuthEvents>();\n\n #accountSignerConfig: AccountSignerConfig | undefined;\n #accountSigner: AccountSigner | null = null;\n #accountIdentity: AccountIdentity | null = null;\n #isAuthenticated = false;\n #mainAccountId: string | null = null;\n #activeAccountId: string | null = null;\n #subaccounts: SubaccountsService;\n #walletProvider: \"metamask\" | \"turnkey\" | \"other\" | undefined = undefined;\n #loginMethod: AuthLoginMethod | null = null;\n #environmentFingerprint: string;\n #tokenStorage: AuthTokenStorage;\n #sessionStore: AuthSessionStore;\n #realtime: PolyesterRealtime;\n\n constructor({\n transports,\n accountSignerConfig,\n environment,\n subaccounts,\n realtime,\n tokenStorage,\n sessionStore,\n }: {\n transports: AuthAndPublicApiTransports;\n accountSignerConfig?: AccountSignerConfig;\n environment: PolyesterEnvironment;\n subaccounts: SubaccountsService;\n realtime: PolyesterRealtime;\n tokenStorage: AuthTokenStorage;\n sessionStore?: AuthSessionStore;\n }) {\n super(transports, realtime);\n\n this.#accountSignerConfig = accountSignerConfig;\n this.#environmentFingerprint = environment.fingerprint;\n this.#subaccounts = subaccounts;\n this.#tokenStorage = tokenStorage;\n this.#realtime = realtime;\n this.#sessionStore =\n sessionStore ??\n new AuthSessionStore({\n environmentFingerprint: environment.fingerprint,\n });\n }\n\n /**\n * Attaches the subaccounts service used when creating a subaccount during authenticated flows.\n */\n setSubaccountsService(subaccounts: SubaccountsService): void {\n this.#subaccounts = subaccounts;\n }\n\n /**\n * Sets the account signer used to sign login and account-switch challenges.\n */\n setAccountSigner(accountSigner: AccountSigner | null): void {\n if (accountSigner) this.#assertAccountSignerEnvironment(accountSigner);\n this.#accountSigner = accountSigner;\n this.#accountIdentity = accountSigner ? this.#identityFromSigner(accountSigner) : null;\n this.#notifyStateChange();\n }\n\n /**\n * Returns the active account signer, throwing if one has not been configured.\n */\n getAccountSigner(): AccountSigner | null {\n return this.#accountSigner;\n }\n\n /**\n * Signs a short-lived login nonce with the configured account signer, exchanges it for a session token, and stores the hydrated account/subaccount session state.\n */\n async login(options: LoginOptions): Promise<LoginResult> {\n return this.#login(options);\n }\n\n async #login(\n options: LoginOptions,\n previousActiveAccount?: ActiveAccountInfo,\n ): Promise<LoginResult> {\n const { provider, loginMethod } = options;\n\n const accountSigner = await this.#resolveAccountSigner();\n\n if (!accountSigner) {\n throw new ConfigurationError(\n \"No account signer configured. Call setAccountSigner() or pass accountSigner in config.\",\n );\n }\n\n const smartAccountAddress = accountSigner.accountAddress;\n const ownerAddress = accountSigner.ownerAddress ?? accountSigner.accountAddress;\n\n const { nonce } = await this.requestLoginNonce(smartAccountAddress);\n\n const message = `Polyester Login\\n\\nNonce: ${nonce}`;\n const signature = await accountSigner.signMessage(message);\n\n const response = await this.loginWithWallet({\n smartAccountAddress,\n nonce,\n signature,\n primaryWalletAddress: ownerAddress,\n walletProvider: provider,\n });\n\n const environmentSession = this.#getEnvironmentSession();\n const resolvedLoginMethod =\n loginMethod ??\n this.#loginMethod ??\n environmentSession?.loginMethod ??\n (provider === \"metamask\" ? \"metamask\" : null);\n\n const tokenOptions = createAuthTokenStorageSetOptions(response.accessToken);\n const activeAccount =\n previousActiveAccount?.mainAccountId === response.accountId\n ? previousActiveAccount\n : undefined;\n this.#sessionStore.commitLogin(\n {\n accessToken: response.accessToken,\n tokenOptions,\n provider,\n loginMethod: resolvedLoginMethod,\n primaryWallet: ownerAddress,\n smartAccount: smartAccountAddress,\n accountId: response.accountId,\n activeAccount,\n username: response.username ?? undefined,\n },\n this.#tokenStorage,\n );\n this.#isAuthenticated = true;\n this.#mainAccountId = response.accountId;\n this.#activeAccountId = activeAccount?.accountId ?? response.accountId;\n this.#walletProvider = provider;\n this.#loginMethod = resolvedLoginMethod;\n this.#accountIdentity = this.#identityFromSigner(accountSigner);\n\n this.#notifyStateChange();\n\n this.events.emit(\"authenticated\", {\n accountId: response.accountId,\n username: response.username,\n });\n\n const expiresAt = response.expiresAt\n ? new Date(\n Number(response.expiresAt.seconds) * 1000 +\n (response.expiresAt.nanos ?? 0) / 1_000_000,\n )\n : new Date();\n\n return {\n accountId: response.accountId,\n username: response.username,\n expiresAt,\n };\n }\n\n /**\n * Builds auth state from a session token and optional active account override.\n */\n hydrateAuthState(state: AuthHydrationData): void {\n const existingToken = this.#getEnvironmentBoundToken();\n if (!existingToken || !isJwtValid(existingToken)) return;\n\n const existingSession = this.#getEnvironmentSession();\n this.#walletProvider = existingSession?.provider ?? this.#walletProvider;\n this.#loginMethod = existingSession?.loginMethod ?? this.#loginMethod;\n\n this.#isAuthenticated = true;\n this.#mainAccountId = state.mainAccountId;\n this.#activeAccountId = state.activeAccountId ?? state.mainAccountId;\n this.#accountIdentity = state.smartAccountAddress\n ? {\n accountAddress: state.smartAccountAddress,\n ownerAddress: state.ownerAddress,\n }\n : null;\n\n this.#notifyStateChange();\n }\n\n /**\n * Loads the stored token, validates that it still belongs to this environment, and restores auth state when possible.\n */\n async restoreSession(): Promise<{ accountId: string; username: string } | null> {\n const existingToken = this.#getEnvironmentBoundToken();\n\n if (!existingToken || !isJwtValid(existingToken)) {\n this.#clearExpiredSessionState();\n return null;\n }\n\n try {\n const me = await this.me();\n this.#isAuthenticated = true;\n this.#mainAccountId = me.accountId;\n\n // preserve active account if already set (e.g. via hydration), otherwise use main\n const existingSession = this.#getEnvironmentSession();\n this.#walletProvider = existingSession?.provider ?? this.#walletProvider;\n this.#loginMethod = existingSession?.loginMethod ?? this.#loginMethod;\n if (!this.#activeAccountId) {\n this.#activeAccountId = existingSession?.activeAccount?.accountId ?? me.accountId;\n }\n\n // use existing account signer if set, otherwise try to resolve from config\n if (!this.#accountSigner) {\n this.#accountSigner = await resolveAccountSigner(this.#accountSignerConfig);\n if (this.#accountSigner) {\n this.#assertAccountSignerEnvironment(this.#accountSigner);\n this.#accountIdentity = this.#identityFromSigner(this.#accountSigner);\n }\n }\n\n // keep the display session available for SSR hydration\n if (this.#accountSigner?.accountAddress) {\n const session = this.#sessionStore.ensureSession(\n {\n provider: this.#walletProvider ? this.#walletProvider : \"other\",\n loginMethod:\n this.#loginMethod ??\n (this.#walletProvider === \"metamask\" ? \"metamask\" : null),\n primaryWallet:\n this.#accountSigner.ownerAddress ?? this.#accountSigner.accountAddress,\n smartAccount: this.#accountSigner.accountAddress,\n accountId: me.accountId,\n username: me.username ?? undefined,\n },\n { maxAgeSeconds: this.#getCurrentTokenStorageOptions().maxAgeSeconds },\n );\n this.#walletProvider = session.provider;\n this.#loginMethod = session.loginMethod ?? this.#loginMethod;\n }\n\n this.#notifyStateChange();\n return { accountId: me.accountId, username: me.username };\n } catch {\n this.#clearExpiredSessionState();\n return null;\n }\n }\n\n /**\n * Clears stored auth state and removes the persisted auth token.\n */\n async logout(): Promise<void> {\n this.#realtime.disconnectPrivate();\n this.#tokenStorage.clear();\n this.#isAuthenticated = false;\n this.#mainAccountId = null;\n this.#activeAccountId = null;\n this.#loginMethod = null;\n this.#accountIdentity = null;\n\n this.#sessionStore.clear();\n\n this.#notifyStateChange();\n this.events.emit(\"loggedOut\", undefined);\n }\n\n #clearExpiredSessionState(): void {\n const shouldEmitLoggedOut = this.#isAuthenticated;\n this.#realtime.disconnectPrivate();\n this.#tokenStorage.clear();\n this.#sessionStore.clear();\n this.#isAuthenticated = false;\n this.#mainAccountId = null;\n this.#activeAccountId = null;\n this.#loginMethod = null;\n this.#accountIdentity = null;\n this.#notifyStateChange();\n if (shouldEmitLoggedOut) {\n this.events.emit(\"loggedOut\", undefined);\n }\n }\n\n /**\n * Returns the remaining lifetime of the stored session token in milliseconds.\n */\n getSessionTimeToExpiry(): number {\n const token = this.#getEnvironmentBoundToken();\n if (!token) return 0;\n return getJwtTimeToExpiry(token);\n }\n\n /**\n * Refreshes the active account-signer session and updates persisted auth state.\n */\n async refreshSession(params?: {\n provider?: \"metamask\" | \"turnkey\" | \"other\";\n loginMethod?: AuthLoginMethod | null;\n }): Promise<LoginResult> {\n if (!this.#isAuthenticated) {\n throw new AuthenticationError(\"Must be authenticated to refresh session\");\n }\n\n const currentSession = this.#getEnvironmentSession();\n return this.#login(\n {\n provider: this.#resolveRefreshProvider(params?.provider),\n loginMethod: params?.loginMethod ?? this.#loginMethod,\n },\n currentSession?.activeAccount,\n );\n }\n\n /**\n * Switches the active account/subaccount by signing the required account switch flow.\n */\n switchAccount(\n accountId: string,\n options?: { smartAccountAddress?: string; label?: string },\n ): { accountId: string; isMain: boolean } {\n if (!this.#isAuthenticated || !this.#mainAccountId) {\n throw new AuthenticationError(\"Must be authenticated to switch accounts\");\n }\n\n this.#activeAccountId = accountId;\n const isMain = accountId === this.#mainAccountId;\n\n this.#sessionStore.setActiveAccount(\n {\n accountId,\n isMain,\n smartAccountAddress: options?.smartAccountAddress,\n label: options?.label,\n },\n { maxAgeSeconds: this.#getCurrentTokenStorageOptions().maxAgeSeconds },\n );\n this.#notifyStateChange();\n\n return { accountId, isMain };\n }\n\n /** Keeps the display-session identity current for the next server render. */\n syncSessionUsername(username: string | null): void {\n this.#sessionStore.setUsername(username, {\n maxAgeSeconds: this.#getCurrentTokenStorageOptions().maxAgeSeconds,\n });\n }\n\n /**\n * Creates a subaccount for the authenticated account and makes it available to the session state.\n */\n async createSubaccount(params: CreateSubaccountParams): Promise<CreateSubaccountResult> {\n if (!this.#isAuthenticated) {\n throw new AuthenticationError(\"Must be authenticated to create subaccounts\");\n }\n\n if (!this.#subaccounts) {\n throw new ConfigurationError(\n \"SubaccountsService not configured. Pass it to constructor or call setSubaccountsService().\",\n );\n }\n\n const { accountSigner, label = \"\", walletProvider = \"wallet\" } = params;\n this.#assertAccountSignerEnvironment(accountSigner);\n\n // request nonce for the subaccount's smart account address\n const { nonce } = await this.requestLoginNonce(accountSigner.accountAddress);\n\n // sign canonical login message with the subaccount account signer\n const message = `Polyester Login\\n\\nNonce: ${nonce}`;\n const signature = await accountSigner.signMessage(message);\n\n // use main account signer's owner address for primary wallet reference\n const primaryWalletAddress =\n this.#accountSigner?.ownerAddress ??\n accountSigner.ownerAddress ??\n this.#accountSigner?.accountAddress ??\n \"\";\n\n const response = await this.#subaccounts.create({\n label,\n smartAccountAddress: accountSigner.accountAddress,\n nonce,\n signature,\n primaryWalletAddress,\n walletProvider,\n });\n\n return {\n subaccountId: response.subaccountId,\n smartAccountSaltNonce: response.smartAccountSaltNonce,\n revision: response.revision,\n };\n }\n\n /**\n * Returns the current account-signer auth state snapshot.\n */\n getState(): AuthState {\n const accountIdentity = this.#accountSigner ?? this.#accountIdentity;\n\n return {\n isAuthenticated: this.#isAuthenticated,\n accountAddress: accountIdentity?.accountAddress ?? null,\n ownerAddress: accountIdentity?.ownerAddress ?? null,\n mainAccountId: this.#mainAccountId,\n activeAccount:\n this.#activeAccountId && this.#mainAccountId\n ? {\n accountId: this.#activeAccountId,\n isMain: this.#activeAccountId === this.#mainAccountId,\n mainAccountId: this.#mainAccountId,\n smartAccountAddress: accountIdentity?.accountAddress,\n }\n : null,\n };\n }\n\n async #resolveAccountSigner(): Promise<AccountSigner | null> {\n if (this.#accountSigner) {\n this.#assertAccountSignerEnvironment(this.#accountSigner);\n return this.#accountSigner;\n }\n\n const resolved = await resolveAccountSigner(this.#accountSignerConfig);\n if (resolved) {\n this.#assertAccountSignerEnvironment(resolved);\n this.#accountSigner = resolved;\n this.#accountIdentity = this.#identityFromSigner(resolved);\n }\n return this.#accountSigner;\n }\n\n #notifyStateChange(): void {\n this.events.emit(\"stateChange\", this.getState());\n }\n\n #getEnvironmentBoundToken(): string | null {\n return this.#sessionStore.getEnvironmentBoundToken(this.#tokenStorage);\n }\n\n #getCurrentTokenStorageOptions(): AuthTokenStorageSetOptions {\n const token = this.#tokenStorage.get();\n if (!token) return { expiresAt: null, maxAgeSeconds: null };\n return createAuthTokenStorageSetOptions(token);\n }\n\n #resolveRefreshProvider(\n provider?: \"metamask\" | \"turnkey\" | \"other\",\n ): \"metamask\" | \"turnkey\" | \"other\" {\n return (\n provider ?? this.#walletProvider ?? this.#getEnvironmentSession()?.provider ?? \"other\"\n );\n }\n\n #assertAccountSignerEnvironment(accountSigner: AccountSigner): void {\n assertAccountSigner(accountSigner);\n if (accountSigner.environmentFingerprint !== this.#environmentFingerprint) {\n throw new ConfigurationError(\n \"Account signer environment does not match client environment.\",\n );\n }\n }\n\n #identityFromSigner(accountSigner: AccountSigner): AccountIdentity {\n return {\n accountAddress: accountSigner.accountAddress,\n ownerAddress: accountSigner.ownerAddress,\n };\n }\n\n #getEnvironmentSession(): SessionData | null {\n return this.#sessionStore.get();\n }\n}\n"],"mappings":";;;;;;;;;;;AAqEA,IAAa,2BAAb,cAA8C,YAAY;CACtD,SAAkB,IAAI,aAAsC;CAE5D;CACA,iBAAuC;CACvC,mBAA2C;CAC3C,mBAAmB;CACnB,iBAAgC;CAChC,mBAAkC;CAClC;CACA,kBAAgE,KAAA;CAChE,eAAuC;CACvC;CACA;CACA;CACA;CAEA,YAAY,EACR,YACA,qBACA,aACA,aACA,UACA,cACA,gBASD;EACC,MAAM,YAAY,QAAQ;EAE1B,KAAKA,uBAAuB;EAC5B,KAAKC,0BAA0B,YAAY;EAC3C,KAAKC,eAAe;EACpB,KAAKC,gBAAgB;EACrB,KAAKC,YAAY;EACjB,KAAKC,gBACD,gBACA,IAAI,iBAAiB,EACjB,wBAAwB,YAAY,YACxC,CAAC;CACT;;;;CAKA,sBAAsB,aAAuC;EACzD,KAAKH,eAAe;CACxB;;;;CAKA,iBAAiB,eAA2C;EACxD,IAAI,eAAe,KAAKI,gCAAgC,aAAa;EACrE,KAAKC,iBAAiB;EACtB,KAAKC,mBAAmB,gBAAgB,KAAKC,oBAAoB,aAAa,IAAI;EAClF,KAAKC,mBAAmB;CAC5B;;;;CAKA,mBAAyC;EACrC,OAAO,KAAKH;CAChB;;;;CAKA,MAAM,MAAM,SAA6C;EACrD,OAAO,KAAKI,OAAO,OAAO;CAC9B;CAEA,MAAMA,OACF,SACA,uBACoB;EACpB,MAAM,EAAE,UAAU,gBAAgB;EAElC,MAAM,gBAAgB,MAAM,KAAKC,sBAAsB;EAEvD,IAAI,CAAC,eACD,MAAM,IAAI,mBACN,wFACJ;EAGJ,MAAM,sBAAsB,cAAc;EAC1C,MAAM,eAAe,cAAc,gBAAgB,cAAc;EAEjE,MAAM,EAAE,UAAU,MAAM,KAAK,kBAAkB,mBAAmB;EAElE,MAAM,UAAU,6BAA6B;EAC7C,MAAM,YAAY,MAAM,cAAc,YAAY,OAAO;EAEzD,MAAM,WAAW,MAAM,KAAK,gBAAgB;GACxC;GACA;GACA;GACA,sBAAsB;GACtB,gBAAgB;EACpB,CAAC;EAED,MAAM,qBAAqB,KAAKC,uBAAuB;EACvD,MAAM,sBACF,eACA,KAAKC,gBACL,oBAAoB,gBACnB,aAAa,aAAa,aAAa;EAE5C,MAAM,eAAe,iCAAiC,SAAS,WAAW;EAC1E,MAAM,gBACF,uBAAuB,kBAAkB,SAAS,YAC5C,wBACA,KAAA;EACV,KAAKT,cAAc,YACf;GACI,aAAa,SAAS;GACtB;GACA;GACA,aAAa;GACb,eAAe;GACf,cAAc;GACd,WAAW,SAAS;GACpB;GACA,UAAU,SAAS,YAAY,KAAA;EACnC,GACA,KAAKF,aACT;EACA,KAAKY,mBAAmB;EACxB,KAAKC,iBAAiB,SAAS;EAC/B,KAAKC,mBAAmB,eAAe,aAAa,SAAS;EAC7D,KAAKC,kBAAkB;EACvB,KAAKJ,eAAe;EACpB,KAAKN,mBAAmB,KAAKC,oBAAoB,aAAa;EAE9D,KAAKC,mBAAmB;EAExB,KAAK,OAAO,KAAK,iBAAiB;GAC9B,WAAW,SAAS;GACpB,UAAU,SAAS;EACvB,CAAC;EAED,MAAM,YAAY,SAAS,4BACrB,IAAI,KACA,OAAO,SAAS,UAAU,OAAO,IAAI,OAChC,SAAS,UAAU,SAAS,KAAK,GAC1C,oBACA,IAAI,KAAK;EAEf,OAAO;GACH,WAAW,SAAS;GACpB,UAAU,SAAS;GACnB;EACJ;CACJ;;;;CAKA,iBAAiB,OAAgC;EAC7C,MAAM,gBAAgB,KAAKS,0BAA0B;EACrD,IAAI,CAAC,iBAAiB,CAAC,WAAW,aAAa,GAAG;EAElD,MAAM,kBAAkB,KAAKN,uBAAuB;EACpD,KAAKK,kBAAkB,iBAAiB,YAAY,KAAKA;EACzD,KAAKJ,eAAe,iBAAiB,eAAe,KAAKA;EAEzD,KAAKC,mBAAmB;EACxB,KAAKC,iBAAiB,MAAM;EAC5B,KAAKC,mBAAmB,MAAM,mBAAmB,MAAM;EACvD,KAAKT,mBAAmB,MAAM,sBACxB;GACI,gBAAgB,MAAM;GACtB,cAAc,MAAM;EACxB,IACA;EAEN,KAAKE,mBAAmB;CAC5B;;;;CAKA,MAAM,iBAA0E;EAC5E,MAAM,gBAAgB,KAAKS,0BAA0B;EAErD,IAAI,CAAC,iBAAiB,CAAC,WAAW,aAAa,GAAG;GAC9C,KAAKC,0BAA0B;GAC/B,OAAO;EACX;EAEA,IAAI;GACA,MAAM,KAAK,MAAM,KAAK,GAAG;GACzB,KAAKL,mBAAmB;GACxB,KAAKC,iBAAiB,GAAG;GAGzB,MAAM,kBAAkB,KAAKH,uBAAuB;GACpD,KAAKK,kBAAkB,iBAAiB,YAAY,KAAKA;GACzD,KAAKJ,eAAe,iBAAiB,eAAe,KAAKA;GACzD,IAAI,CAAC,KAAKG,kBACN,KAAKA,mBAAmB,iBAAiB,eAAe,aAAa,GAAG;GAI5E,IAAI,CAAC,KAAKV,gBAAgB;IACtB,KAAKA,iBAAiB,MAAM,qBAAqB,KAAKP,oBAAoB;IAC1E,IAAI,KAAKO,gBAAgB;KACrB,KAAKD,gCAAgC,KAAKC,cAAc;KACxD,KAAKC,mBAAmB,KAAKC,oBAAoB,KAAKF,cAAc;IACxE;GACJ;GAGA,IAAI,KAAKA,gBAAgB,gBAAgB;IACrC,MAAM,UAAU,KAAKF,cAAc,cAC/B;KACI,UAAU,KAAKa,kBAAkB,KAAKA,kBAAkB;KACxD,aACI,KAAKJ,iBACJ,KAAKI,oBAAoB,aAAa,aAAa;KACxD,eACI,KAAKX,eAAe,gBAAgB,KAAKA,eAAe;KAC5D,cAAc,KAAKA,eAAe;KAClC,WAAW,GAAG;KACd,UAAU,GAAG,YAAY,KAAA;IAC7B,GACA,EAAE,eAAe,KAAKc,+BAA+B,CAAC,CAAC,cAAc,CACzE;IACA,KAAKH,kBAAkB,QAAQ;IAC/B,KAAKJ,eAAe,QAAQ,eAAe,KAAKA;GACpD;GAEA,KAAKJ,mBAAmB;GACxB,OAAO;IAAE,WAAW,GAAG;IAAW,UAAU,GAAG;GAAS;EAC5D,QAAQ;GACJ,KAAKU,0BAA0B;GAC/B,OAAO;EACX;CACJ;;;;CAKA,MAAM,SAAwB;EAC1B,KAAKhB,UAAU,kBAAkB;EACjC,KAAKD,cAAc,MAAM;EACzB,KAAKY,mBAAmB;EACxB,KAAKC,iBAAiB;EACtB,KAAKC,mBAAmB;EACxB,KAAKH,eAAe;EACpB,KAAKN,mBAAmB;EAExB,KAAKH,cAAc,MAAM;EAEzB,KAAKK,mBAAmB;EACxB,KAAK,OAAO,KAAK,aAAa,KAAA,CAAS;CAC3C;CAEA,4BAAkC;EAC9B,MAAM,sBAAsB,KAAKK;EACjC,KAAKX,UAAU,kBAAkB;EACjC,KAAKD,cAAc,MAAM;EACzB,KAAKE,cAAc,MAAM;EACzB,KAAKU,mBAAmB;EACxB,KAAKC,iBAAiB;EACtB,KAAKC,mBAAmB;EACxB,KAAKH,eAAe;EACpB,KAAKN,mBAAmB;EACxB,KAAKE,mBAAmB;EACxB,IAAI,qBACA,KAAK,OAAO,KAAK,aAAa,KAAA,CAAS;CAE/C;;;;CAKA,yBAAiC;EAC7B,MAAM,QAAQ,KAAKS,0BAA0B;EAC7C,IAAI,CAAC,OAAO,OAAO;EACnB,OAAO,mBAAmB,KAAK;CACnC;;;;CAKA,MAAM,eAAe,QAGI;EACrB,IAAI,CAAC,KAAKJ,kBACN,MAAM,IAAI,oBAAoB,0CAA0C;EAG5E,MAAM,iBAAiB,KAAKF,uBAAuB;EACnD,OAAO,KAAKF,OACR;GACI,UAAU,KAAKW,wBAAwB,QAAQ,QAAQ;GACvD,aAAa,QAAQ,eAAe,KAAKR;EAC7C,GACA,gBAAgB,aACpB;CACJ;;;;CAKA,cACI,WACA,SACsC;EACtC,IAAI,CAAC,KAAKC,oBAAoB,CAAC,KAAKC,gBAChC,MAAM,IAAI,oBAAoB,0CAA0C;EAG5E,KAAKC,mBAAmB;EACxB,MAAM,SAAS,cAAc,KAAKD;EAElC,KAAKX,cAAc,iBACf;GACI;GACA;GACA,qBAAqB,SAAS;GAC9B,OAAO,SAAS;EACpB,GACA,EAAE,eAAe,KAAKgB,+BAA+B,CAAC,CAAC,cAAc,CACzE;EACA,KAAKX,mBAAmB;EAExB,OAAO;GAAE;GAAW;EAAO;CAC/B;;CAGA,oBAAoB,UAA+B;EAC/C,KAAKL,cAAc,YAAY,UAAU,EACrC,eAAe,KAAKgB,+BAA+B,CAAC,CAAC,cACzD,CAAC;CACL;;;;CAKA,MAAM,iBAAiB,QAAiE;EACpF,IAAI,CAAC,KAAKN,kBACN,MAAM,IAAI,oBAAoB,6CAA6C;EAG/E,IAAI,CAAC,KAAKb,cACN,MAAM,IAAI,mBACN,4FACJ;EAGJ,MAAM,EAAE,eAAe,QAAQ,IAAI,iBAAiB,aAAa;EACjE,KAAKI,gCAAgC,aAAa;EAGlD,MAAM,EAAE,UAAU,MAAM,KAAK,kBAAkB,cAAc,cAAc;EAG3E,MAAM,UAAU,6BAA6B;EAC7C,MAAM,YAAY,MAAM,cAAc,YAAY,OAAO;EAGzD,MAAM,uBACF,KAAKC,gBAAgB,gBACrB,cAAc,gBACd,KAAKA,gBAAgB,kBACrB;EAEJ,MAAM,WAAW,MAAM,KAAKL,aAAa,OAAO;GAC5C;GACA,qBAAqB,cAAc;GACnC;GACA;GACA;GACA;EACJ,CAAC;EAED,OAAO;GACH,cAAc,SAAS;GACvB,uBAAuB,SAAS;GAChC,UAAU,SAAS;EACvB;CACJ;;;;CAKA,WAAsB;EAClB,MAAM,kBAAkB,KAAKK,kBAAkB,KAAKC;EAEpD,OAAO;GACH,iBAAiB,KAAKO;GACtB,gBAAgB,iBAAiB,kBAAkB;GACnD,cAAc,iBAAiB,gBAAgB;GAC/C,eAAe,KAAKC;GACpB,eACI,KAAKC,oBAAoB,KAAKD,iBACxB;IACI,WAAW,KAAKC;IAChB,QAAQ,KAAKA,qBAAqB,KAAKD;IACvC,eAAe,KAAKA;IACpB,qBAAqB,iBAAiB;GAC1C,IACA;EACd;CACJ;CAEA,MAAMJ,wBAAuD;EACzD,IAAI,KAAKL,gBAAgB;GACrB,KAAKD,gCAAgC,KAAKC,cAAc;GACxD,OAAO,KAAKA;EAChB;EAEA,MAAM,WAAW,MAAM,qBAAqB,KAAKP,oBAAoB;EACrE,IAAI,UAAU;GACV,KAAKM,gCAAgC,QAAQ;GAC7C,KAAKC,iBAAiB;GACtB,KAAKC,mBAAmB,KAAKC,oBAAoB,QAAQ;EAC7D;EACA,OAAO,KAAKF;CAChB;CAEA,qBAA2B;EACvB,KAAK,OAAO,KAAK,eAAe,KAAK,SAAS,CAAC;CACnD;CAEA,4BAA2C;EACvC,OAAO,KAAKF,cAAc,yBAAyB,KAAKF,aAAa;CACzE;CAEA,iCAA6D;EACzD,MAAM,QAAQ,KAAKA,cAAc,IAAI;EACrC,IAAI,CAAC,OAAO,OAAO;GAAE,WAAW;GAAM,eAAe;EAAK;EAC1D,OAAO,iCAAiC,KAAK;CACjD;CAEA,wBACI,UACgC;EAChC,OACI,YAAY,KAAKe,mBAAmB,KAAKL,uBAAuB,CAAC,EAAE,YAAY;CAEvF;CAEA,gCAAgC,eAAoC;EAChE,oBAAoB,aAAa;EACjC,IAAI,cAAc,2BAA2B,KAAKZ,yBAC9C,MAAM,IAAI,mBACN,+DACJ;CAER;CAEA,oBAAoB,eAA+C;EAC/D,OAAO;GACH,gBAAgB,cAAc;GAC9B,cAAc,cAAc;EAChC;CACJ;CAEA,yBAA6C;EACzC,OAAO,KAAKI,cAAc,IAAI;CAClC;AACJ"}
|
|
1
|
+
{"version":3,"file":"account-signer-auth.js","names":["#accountSignerConfig","#environmentFingerprint","#subaccounts","#tokenStorage","#realtime","#sessionStore","#assertAccountSignerEnvironment","#accountSigner","#accountIdentity","#identityFromSigner","#notifyStateChange","#login","#resolveAccountSigner","#challengeUri","#getEnvironmentSession","#loginMethod","#isAuthenticated","#mainAccountId","#activeAccountId","#walletProvider","#getEnvironmentBoundToken","#clearExpiredSessionState","#getCurrentTokenStorageOptions","#resolveRefreshProvider"],"sources":["../../../src/services/auth/account-signer-auth.ts"],"sourcesContent":["import { AuthService } from \"./auth.js\";\nimport { AuthenticationError, ConfigurationError } from \"../../shared/errors.js\";\nimport { AuthSessionStore } from \"./session.js\";\nimport type { AccountSigner, AccountSignerConfig, HexAddress } from \"../../account-signer/types.js\";\nimport { assertAccountSigner, resolveAccountSigner } from \"../../account-signer/types.js\";\nimport { EventEmitter } from \"../../utils/event-emitter.js\";\nimport { isJwtValid, getJwtTimeToExpiry } from \"../../utils/jwt.js\";\nimport type { SubaccountsService } from \"../subaccounts/index.js\";\nimport type {\n AuthState,\n AuthHydrationData,\n AuthLoginMethod,\n SessionData,\n ActiveAccountInfo,\n} from \"./session.types.js\";\nimport type { PolyesterRealtime } from \"../../realtime/index.js\";\nimport type { PolyesterEnvironment } from \"../../environment.js\";\nimport type { AuthAndPublicApiTransports } from \"../../shared/transports.js\";\nimport {\n createAuthTokenStorageSetOptions,\n type AuthTokenStorage,\n type AuthTokenStorageSetOptions,\n} from \"./token-storage.js\";\n\nexport interface AccountSignerAuthEvents {\n authenticated: { accountId: string; username: string };\n loggedOut: void;\n error: { code: string; message: string };\n servicesReady: void;\n stateChange: AuthState;\n}\n\nexport interface LoginResult {\n accountId: string;\n username: string;\n expiresAt: Date;\n}\n\nexport interface LoginOptions {\n /**\n * The wallet provider to use for login.\n */\n provider: \"metamask\" | \"turnkey\" | \"other\";\n /** Browser origin requesting the signature; defaults to location.origin in browsers. Required outside browsers. */\n uri?: string;\n loginMethod?: AuthLoginMethod | null;\n}\n\nexport interface CreateSubaccountParams {\n /** The account signer for the new subaccount (caller derives this, e.g. via Turnkey saltNonce) */\n accountSigner: AccountSigner;\n /** Optional human-readable label for this subaccount */\n label?: string;\n /** Browser origin requesting the signature; defaults to location.origin in browsers. Required outside browsers. */\n uri?: string;\n}\n\nexport interface CreateSubaccountResult {\n subaccountId: string;\n smartAccountSaltNonce: number;\n revision: string;\n}\n\ninterface AccountIdentity {\n accountAddress: HexAddress;\n ownerAddress?: HexAddress;\n}\n\n/**\n * Coordinates wallet/account-signer authentication, session storage, subaccount selection, and session refresh.\n */\nexport class AccountSignerAuthService extends AuthService {\n readonly events = new EventEmitter<AccountSignerAuthEvents>();\n\n #accountSignerConfig: AccountSignerConfig | undefined;\n #accountSigner: AccountSigner | null = null;\n #accountIdentity: AccountIdentity | null = null;\n #isAuthenticated = false;\n #mainAccountId: string | null = null;\n #activeAccountId: string | null = null;\n #subaccounts: SubaccountsService;\n #walletProvider: \"metamask\" | \"turnkey\" | \"other\" | undefined = undefined;\n #loginMethod: AuthLoginMethod | null = null;\n #challengeUri: string | undefined = undefined;\n #environmentFingerprint: string;\n #tokenStorage: AuthTokenStorage;\n #sessionStore: AuthSessionStore;\n #realtime: PolyesterRealtime;\n\n constructor({\n transports,\n accountSignerConfig,\n environment,\n subaccounts,\n realtime,\n tokenStorage,\n sessionStore,\n }: {\n transports: AuthAndPublicApiTransports;\n accountSignerConfig?: AccountSignerConfig;\n environment: PolyesterEnvironment;\n subaccounts: SubaccountsService;\n realtime: PolyesterRealtime;\n tokenStorage: AuthTokenStorage;\n sessionStore?: AuthSessionStore;\n }) {\n super(transports, realtime);\n\n this.#accountSignerConfig = accountSignerConfig;\n this.#environmentFingerprint = environment.fingerprint;\n this.#subaccounts = subaccounts;\n this.#tokenStorage = tokenStorage;\n this.#realtime = realtime;\n this.#sessionStore =\n sessionStore ??\n new AuthSessionStore({\n environmentFingerprint: environment.fingerprint,\n });\n }\n\n /**\n * Attaches the subaccounts service used when creating a subaccount during authenticated flows.\n */\n setSubaccountsService(subaccounts: SubaccountsService): void {\n this.#subaccounts = subaccounts;\n }\n\n /**\n * Sets the account signer used to sign login and account-switch challenges.\n */\n setAccountSigner(accountSigner: AccountSigner | null): void {\n if (accountSigner) this.#assertAccountSignerEnvironment(accountSigner);\n this.#accountSigner = accountSigner;\n this.#accountIdentity = accountSigner ? this.#identityFromSigner(accountSigner) : null;\n this.#notifyStateChange();\n }\n\n /**\n * Returns the active account signer, throwing if one has not been configured.\n */\n getAccountSigner(): AccountSigner | null {\n return this.#accountSigner;\n }\n\n /**\n * Signs a server-issued SIWE message with the configured account signer, exchanges it for a session token, and stores the hydrated account/subaccount session state.\n */\n async login(options: LoginOptions): Promise<LoginResult> {\n return this.#login(options);\n }\n\n async #login(\n options: LoginOptions,\n previousActiveAccount?: ActiveAccountInfo,\n ): Promise<LoginResult> {\n const { provider, loginMethod } = options;\n\n const accountSigner = await this.#resolveAccountSigner();\n\n if (!accountSigner) {\n throw new ConfigurationError(\n \"No account signer configured. Call setAccountSigner() or pass accountSigner in config.\",\n );\n }\n\n const smartAccountAddress = accountSigner.accountAddress;\n const ownerAddress = accountSigner.ownerAddress ?? accountSigner.accountAddress;\n\n const uri = resolveChallengeUri(options.uri ?? this.#challengeUri);\n const { message } = await this.createWalletChallenge({\n smartAccountAddress,\n signerAddress: accountSigner.accountAddress,\n uri,\n purpose: \"login\",\n });\n const signature = await accountSigner.signMessage(message);\n\n const response = await this.loginWithWallet({\n smartAccountAddress,\n message,\n signature,\n walletProvider: provider,\n });\n\n const environmentSession = this.#getEnvironmentSession();\n const resolvedLoginMethod =\n loginMethod ??\n this.#loginMethod ??\n environmentSession?.loginMethod ??\n (provider === \"metamask\" ? \"metamask\" : null);\n\n const tokenOptions = createAuthTokenStorageSetOptions(response.accessToken);\n const activeAccount =\n previousActiveAccount?.mainAccountId === response.accountId\n ? previousActiveAccount\n : undefined;\n this.#sessionStore.commitLogin(\n {\n accessToken: response.accessToken,\n tokenOptions,\n provider,\n loginMethod: resolvedLoginMethod,\n primaryWallet: ownerAddress,\n smartAccount: smartAccountAddress,\n accountId: response.accountId,\n activeAccount,\n username: response.username ?? undefined,\n },\n this.#tokenStorage,\n );\n this.#isAuthenticated = true;\n this.#mainAccountId = response.accountId;\n this.#activeAccountId = activeAccount?.accountId ?? response.accountId;\n this.#walletProvider = provider;\n this.#loginMethod = resolvedLoginMethod;\n this.#challengeUri = uri;\n this.#accountIdentity = this.#identityFromSigner(accountSigner);\n\n this.#notifyStateChange();\n\n this.events.emit(\"authenticated\", {\n accountId: response.accountId,\n username: response.username,\n });\n\n const expiresAt = response.expiresAt\n ? new Date(\n Number(response.expiresAt.seconds) * 1000 +\n (response.expiresAt.nanos ?? 0) / 1_000_000,\n )\n : new Date();\n\n return {\n accountId: response.accountId,\n username: response.username,\n expiresAt,\n };\n }\n\n /**\n * Builds auth state from a session token and optional active account override.\n */\n hydrateAuthState(state: AuthHydrationData): void {\n const existingToken = this.#getEnvironmentBoundToken();\n if (!existingToken || !isJwtValid(existingToken)) return;\n\n const existingSession = this.#getEnvironmentSession();\n this.#walletProvider = existingSession?.provider ?? this.#walletProvider;\n this.#loginMethod = existingSession?.loginMethod ?? this.#loginMethod;\n\n this.#isAuthenticated = true;\n this.#mainAccountId = state.mainAccountId;\n this.#activeAccountId = state.activeAccountId ?? state.mainAccountId;\n this.#accountIdentity = state.smartAccountAddress\n ? {\n accountAddress: state.smartAccountAddress,\n ownerAddress: state.ownerAddress,\n }\n : null;\n\n this.#notifyStateChange();\n }\n\n /**\n * Loads the stored token, validates that it still belongs to this environment, and restores auth state when possible.\n */\n async restoreSession(): Promise<{ accountId: string; username: string } | null> {\n const existingToken = this.#getEnvironmentBoundToken();\n\n if (!existingToken || !isJwtValid(existingToken)) {\n this.#clearExpiredSessionState();\n return null;\n }\n\n try {\n const me = await this.me();\n this.#isAuthenticated = true;\n this.#mainAccountId = me.accountId;\n\n // preserve active account if already set (e.g. via hydration), otherwise use main\n const existingSession = this.#getEnvironmentSession();\n this.#walletProvider = existingSession?.provider ?? this.#walletProvider;\n this.#loginMethod = existingSession?.loginMethod ?? this.#loginMethod;\n if (!this.#activeAccountId) {\n this.#activeAccountId = existingSession?.activeAccount?.accountId ?? me.accountId;\n }\n\n // use existing account signer if set, otherwise try to resolve from config\n if (!this.#accountSigner) {\n this.#accountSigner = await resolveAccountSigner(this.#accountSignerConfig);\n if (this.#accountSigner) {\n this.#assertAccountSignerEnvironment(this.#accountSigner);\n this.#accountIdentity = this.#identityFromSigner(this.#accountSigner);\n }\n }\n\n // keep the display session available for SSR hydration\n if (this.#accountSigner?.accountAddress) {\n const session = this.#sessionStore.ensureSession(\n {\n provider: this.#walletProvider ? this.#walletProvider : \"other\",\n loginMethod:\n this.#loginMethod ??\n (this.#walletProvider === \"metamask\" ? \"metamask\" : null),\n primaryWallet:\n this.#accountSigner.ownerAddress ?? this.#accountSigner.accountAddress,\n smartAccount: this.#accountSigner.accountAddress,\n accountId: me.accountId,\n username: me.username ?? undefined,\n },\n { maxAgeSeconds: this.#getCurrentTokenStorageOptions().maxAgeSeconds },\n );\n this.#walletProvider = session.provider;\n this.#loginMethod = session.loginMethod ?? this.#loginMethod;\n }\n\n this.#notifyStateChange();\n return { accountId: me.accountId, username: me.username };\n } catch {\n this.#clearExpiredSessionState();\n return null;\n }\n }\n\n /**\n * Clears stored auth state and removes the persisted auth token.\n */\n async logout(): Promise<void> {\n this.#realtime.disconnectPrivate();\n this.#tokenStorage.clear();\n this.#isAuthenticated = false;\n this.#mainAccountId = null;\n this.#activeAccountId = null;\n this.#loginMethod = null;\n this.#challengeUri = undefined;\n this.#accountIdentity = null;\n\n this.#sessionStore.clear();\n\n this.#notifyStateChange();\n this.events.emit(\"loggedOut\", undefined);\n }\n\n #clearExpiredSessionState(): void {\n const shouldEmitLoggedOut = this.#isAuthenticated;\n this.#realtime.disconnectPrivate();\n this.#tokenStorage.clear();\n this.#sessionStore.clear();\n this.#isAuthenticated = false;\n this.#mainAccountId = null;\n this.#activeAccountId = null;\n this.#loginMethod = null;\n this.#challengeUri = undefined;\n this.#accountIdentity = null;\n this.#notifyStateChange();\n if (shouldEmitLoggedOut) {\n this.events.emit(\"loggedOut\", undefined);\n }\n }\n\n /**\n * Returns the remaining lifetime of the stored session token in milliseconds.\n */\n getSessionTimeToExpiry(): number {\n const token = this.#getEnvironmentBoundToken();\n if (!token) return 0;\n return getJwtTimeToExpiry(token);\n }\n\n /**\n * Refreshes the active account-signer session and updates persisted auth state.\n */\n async refreshSession(params?: {\n /** Overrides the origin remembered from login. */\n uri?: string;\n provider?: \"metamask\" | \"turnkey\" | \"other\";\n loginMethod?: AuthLoginMethod | null;\n }): Promise<LoginResult> {\n if (!this.#isAuthenticated) {\n throw new AuthenticationError(\"Must be authenticated to refresh session\");\n }\n\n const currentSession = this.#getEnvironmentSession();\n return this.#login(\n {\n provider: this.#resolveRefreshProvider(params?.provider),\n uri: params?.uri,\n loginMethod: params?.loginMethod ?? this.#loginMethod,\n },\n currentSession?.activeAccount,\n );\n }\n\n /**\n * Switches the active account/subaccount by signing the required account switch flow.\n */\n switchAccount(\n accountId: string,\n options?: { smartAccountAddress?: string; label?: string },\n ): { accountId: string; isMain: boolean } {\n if (!this.#isAuthenticated || !this.#mainAccountId) {\n throw new AuthenticationError(\"Must be authenticated to switch accounts\");\n }\n\n this.#activeAccountId = accountId;\n const isMain = accountId === this.#mainAccountId;\n\n this.#sessionStore.setActiveAccount(\n {\n accountId,\n isMain,\n smartAccountAddress: options?.smartAccountAddress,\n label: options?.label,\n },\n { maxAgeSeconds: this.#getCurrentTokenStorageOptions().maxAgeSeconds },\n );\n this.#notifyStateChange();\n\n return { accountId, isMain };\n }\n\n /** Keeps the display-session identity current for the next server render. */\n syncSessionUsername(username: string | null): void {\n this.#sessionStore.setUsername(username, {\n maxAgeSeconds: this.#getCurrentTokenStorageOptions().maxAgeSeconds,\n });\n }\n\n /**\n * Creates a subaccount for the authenticated account and makes it available to the session state.\n */\n async createSubaccount(params: CreateSubaccountParams): Promise<CreateSubaccountResult> {\n if (!this.#isAuthenticated) {\n throw new AuthenticationError(\"Must be authenticated to create subaccounts\");\n }\n\n if (!this.#subaccounts) {\n throw new ConfigurationError(\n \"SubaccountsService not configured. Pass it to constructor or call setSubaccountsService().\",\n );\n }\n\n const { accountSigner, label = \"\" } = params;\n this.#assertAccountSignerEnvironment(accountSigner);\n\n const { message } = await this.createWalletChallenge({\n smartAccountAddress: accountSigner.accountAddress,\n signerAddress: accountSigner.accountAddress,\n uri: resolveChallengeUri(params.uri ?? this.#challengeUri),\n purpose: \"create_subaccount\",\n });\n const signature = await accountSigner.signMessage(message);\n\n const response = await this.#subaccounts.create({\n label,\n smartAccountAddress: accountSigner.accountAddress,\n message,\n signature,\n });\n\n return {\n subaccountId: response.subaccountId,\n smartAccountSaltNonce: response.smartAccountSaltNonce,\n revision: response.revision,\n };\n }\n\n /**\n * Returns the current account-signer auth state snapshot.\n */\n getState(): AuthState {\n const accountIdentity = this.#accountSigner ?? this.#accountIdentity;\n\n return {\n isAuthenticated: this.#isAuthenticated,\n accountAddress: accountIdentity?.accountAddress ?? null,\n ownerAddress: accountIdentity?.ownerAddress ?? null,\n mainAccountId: this.#mainAccountId,\n activeAccount:\n this.#activeAccountId && this.#mainAccountId\n ? {\n accountId: this.#activeAccountId,\n isMain: this.#activeAccountId === this.#mainAccountId,\n mainAccountId: this.#mainAccountId,\n smartAccountAddress: accountIdentity?.accountAddress,\n }\n : null,\n };\n }\n\n async #resolveAccountSigner(): Promise<AccountSigner | null> {\n if (this.#accountSigner) {\n this.#assertAccountSignerEnvironment(this.#accountSigner);\n return this.#accountSigner;\n }\n\n const resolved = await resolveAccountSigner(this.#accountSignerConfig);\n if (resolved) {\n this.#assertAccountSignerEnvironment(resolved);\n this.#accountSigner = resolved;\n this.#accountIdentity = this.#identityFromSigner(resolved);\n }\n return this.#accountSigner;\n }\n\n #notifyStateChange(): void {\n this.events.emit(\"stateChange\", this.getState());\n }\n\n #getEnvironmentBoundToken(): string | null {\n return this.#sessionStore.getEnvironmentBoundToken(this.#tokenStorage);\n }\n\n #getCurrentTokenStorageOptions(): AuthTokenStorageSetOptions {\n const token = this.#tokenStorage.get();\n if (!token) return { expiresAt: null, maxAgeSeconds: null };\n return createAuthTokenStorageSetOptions(token);\n }\n\n #resolveRefreshProvider(\n provider?: \"metamask\" | \"turnkey\" | \"other\",\n ): \"metamask\" | \"turnkey\" | \"other\" {\n return (\n provider ?? this.#walletProvider ?? this.#getEnvironmentSession()?.provider ?? \"other\"\n );\n }\n\n #assertAccountSignerEnvironment(accountSigner: AccountSigner): void {\n assertAccountSigner(accountSigner);\n if (accountSigner.environmentFingerprint !== this.#environmentFingerprint) {\n throw new ConfigurationError(\n \"Account signer environment does not match client environment.\",\n );\n }\n }\n\n #identityFromSigner(accountSigner: AccountSigner): AccountIdentity {\n return {\n accountAddress: accountSigner.accountAddress,\n ownerAddress: accountSigner.ownerAddress,\n };\n }\n\n #getEnvironmentSession(): SessionData | null {\n return this.#sessionStore.get();\n }\n}\n\nfunction resolveChallengeUri(uri: string | undefined): string {\n const resolved = uri ?? (typeof location === \"undefined\" ? undefined : location.origin);\n if (!resolved)\n throw new ConfigurationError(\n \"Wallet authentication requires a browser origin URI. Pass uri outside a browser.\",\n );\n return resolved;\n}\n"],"mappings":"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"}
|
|
@@ -6,13 +6,20 @@ import "../../realtime/index.js";
|
|
|
6
6
|
import { ProfileService } from "./profile/profile.js";
|
|
7
7
|
import * as v from "valibot";
|
|
8
8
|
//#region src/services/auth/auth.d.ts
|
|
9
|
+
declare const CreateWalletChallengeInputSchema: v.StrictObjectSchema<{
|
|
10
|
+
readonly smartAccountAddress: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "Invalid wallet address.">]>;
|
|
11
|
+
readonly signerAddress: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "Invalid wallet address.">]>;
|
|
12
|
+
readonly uri: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MaxLengthAction<string, 2048, undefined>, v.CheckAction<string, "Wallet challenge URI must be an HTTP(S) origin without a path, query, fragment, or user information.">]>;
|
|
13
|
+
readonly purpose: v.PicklistSchema<["login", "create_subaccount"], undefined>;
|
|
14
|
+
}, undefined>;
|
|
15
|
+
type CreateWalletChallengeInput = v.InferInput<typeof CreateWalletChallengeInputSchema>;
|
|
16
|
+
type WalletChallengePurpose = CreateWalletChallengeInput["purpose"];
|
|
9
17
|
declare const LoginWithWalletInputSchema: v.StrictObjectSchema<{
|
|
10
|
-
readonly smartAccountAddress: v.StringSchema<undefined>;
|
|
11
|
-
readonly
|
|
12
|
-
readonly signature: v.StringSchema<undefined>;
|
|
18
|
+
readonly smartAccountAddress: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "Invalid wallet address.">]>;
|
|
19
|
+
readonly message: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.CheckAction<string, "Wallet challenge message exceeds 4096 UTF-8 bytes.">]>;
|
|
20
|
+
readonly signature: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 8192, undefined>]>;
|
|
13
21
|
readonly userAgent: v.OptionalSchema<v.StringSchema<undefined>, "">;
|
|
14
22
|
readonly ip: v.OptionalSchema<v.StringSchema<undefined>, "">;
|
|
15
|
-
readonly primaryWalletAddress: v.OptionalSchema<v.StringSchema<undefined>, "">;
|
|
16
23
|
readonly walletProvider: v.OptionalSchema<v.StringSchema<undefined>, "">;
|
|
17
24
|
}, undefined>;
|
|
18
25
|
type LoginWithWalletInput = v.InferInput<typeof LoginWithWalletInputSchema>;
|
|
@@ -54,8 +61,8 @@ declare const LoginWithWalletResponseSchema: v.ObjectSchema<{
|
|
|
54
61
|
readonly username: v.StringSchema<undefined>;
|
|
55
62
|
}, undefined>;
|
|
56
63
|
type LoginWithWalletResponse = v.InferOutput<typeof LoginWithWalletResponseSchema>;
|
|
57
|
-
declare const
|
|
58
|
-
readonly
|
|
64
|
+
declare const WalletChallengeSchema: v.ObjectSchema<{
|
|
65
|
+
readonly message: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.CheckAction<string, "Wallet challenge message exceeds 4096 UTF-8 bytes.">]>;
|
|
59
66
|
readonly expiresAt: v.SchemaWithPipe<readonly [v.OptionalSchema<v.ObjectSchema<{
|
|
60
67
|
readonly seconds: v.BigintSchema<undefined>;
|
|
61
68
|
readonly nanos: v.OptionalSchema<v.NumberSchema<undefined>, 0>;
|
|
@@ -64,7 +71,7 @@ declare const NonceSchema: v.ObjectSchema<{
|
|
|
64
71
|
nanos: number;
|
|
65
72
|
} | undefined, number | undefined>]>;
|
|
66
73
|
}, undefined>;
|
|
67
|
-
type
|
|
74
|
+
type WalletChallenge = v.InferOutput<typeof WalletChallengeSchema>;
|
|
68
75
|
/**
|
|
69
76
|
* Handles wallet-based authentication, caller introspection, and authenticated profile operations.
|
|
70
77
|
*/
|
|
@@ -84,17 +91,15 @@ declare class AuthService {
|
|
|
84
91
|
*/
|
|
85
92
|
acceptTerms(options?: PolyesterMutationOptions): Promise<void>;
|
|
86
93
|
/**
|
|
87
|
-
* Requests a
|
|
88
|
-
*
|
|
89
|
-
* after about five minutes. The optional expiry is milliseconds since the
|
|
90
|
-
* Unix epoch.
|
|
94
|
+
* Requests a server-issued SIWE message. Sign its exact UTF-8 bytes with
|
|
95
|
+
* personal_sign; do not hash or reconstruct it. Expiry is epoch milliseconds.
|
|
91
96
|
*/
|
|
92
|
-
|
|
97
|
+
createWalletChallenge(input: CreateWalletChallengeInput, options?: PolyesterRequestOptions): Promise<WalletChallenge>;
|
|
93
98
|
/**
|
|
94
|
-
* Exchanges a signed
|
|
99
|
+
* Exchanges a signed SIWE message for an authenticated session token and account identity returned by the auth API.
|
|
95
100
|
*/
|
|
96
101
|
protected loginWithWallet(input: LoginWithWalletInput, options?: PolyesterMutationOptions): Promise<LoginWithWalletResponse>;
|
|
97
102
|
}
|
|
98
103
|
//#endregion
|
|
99
|
-
export { AuthService, LoginWithWalletInput, LoginWithWalletInputSchema, LoginWithWalletResponse, Me,
|
|
104
|
+
export { AuthService, CreateWalletChallengeInput, CreateWalletChallengeInputSchema, LoginWithWalletInput, LoginWithWalletInputSchema, LoginWithWalletResponse, Me, WalletChallenge, WalletChallengePurpose };
|
|
100
105
|
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/services/auth/auth.ts"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/services/auth/auth.ts"],"mappings":";;;;;;;;cA2Ba,kCAAgC,EAAA;;;;;;KAMjC,6BAA6B,EAAE,kBAAkB;KACjD,yBAAyB;cAExB,4BAA0B,EAAA;;;;;;;;KAS3B,uBAAuB,EAAE,kBAAkB;cAEjD,UAAQ,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;KAOF,KAAK,EAAE,mBAAmB;cAEhC,+BAA6B,EAAA;;;;;;;;;KAOvB,0BAA0B,EAAE,mBAAmB;cAErD,uBAAqB,EAAA;;;;;;;;;;KAKf,kBAAkB,EAAE,mBAAmB;;;;cAKtC;;EAGT,SAAS;EAEG,YAAA,YAAY,4BAA4B,UAAU;;;;EASxD,GAAG,UAAU,0BAA0B,QAAQ;;;;;;;EAW/C,YAAY,UAAU,2BAA2B;;;;;EAQjD,sBACF,OAAO,4BACP,UAAU,0BACX,QAAQ;;;;YA4BK,gBACZ,OAAO,sBACP,UAAU,2BACX,QAAQ"}
|
|
@@ -1,19 +1,26 @@
|
|
|
1
|
+
import { ValidationError } from "../../shared/errors.js";
|
|
1
2
|
import { AuthService as AuthService$1 } from "../../gen/auth/v1/auth_pb.js";
|
|
2
3
|
import { OptionalTimestampMsSchema, PublicIdSchema, TimestampSchema } from "../../shared/schemas.js";
|
|
3
4
|
import { parse } from "../../shared/validation.js";
|
|
4
5
|
import { toConnectCallOptions } from "../../shared/request-options.js";
|
|
5
6
|
import { ProfileService } from "./profile/profile.js";
|
|
6
7
|
import { MfaSessionInfoSchema } from "../mfa/mfa.schemas.js";
|
|
8
|
+
import { WalletAddressSchema, WalletChallengeMessageSchema, WalletChallengeUriSchema, WalletSignatureSchema } from "./wallet-challenge.schemas.js";
|
|
7
9
|
import { createClient } from "@connectrpc/connect";
|
|
8
10
|
import * as v from "valibot";
|
|
9
11
|
//#region src/services/auth/auth.ts
|
|
12
|
+
const CreateWalletChallengeInputSchema = v.strictObject({
|
|
13
|
+
smartAccountAddress: WalletAddressSchema,
|
|
14
|
+
signerAddress: WalletAddressSchema,
|
|
15
|
+
uri: WalletChallengeUriSchema,
|
|
16
|
+
purpose: v.picklist(["login", "create_subaccount"])
|
|
17
|
+
});
|
|
10
18
|
const LoginWithWalletInputSchema = v.strictObject({
|
|
11
|
-
smartAccountAddress:
|
|
12
|
-
|
|
13
|
-
signature:
|
|
19
|
+
smartAccountAddress: WalletAddressSchema,
|
|
20
|
+
message: WalletChallengeMessageSchema,
|
|
21
|
+
signature: WalletSignatureSchema,
|
|
14
22
|
userAgent: v.optional(v.string(), ""),
|
|
15
23
|
ip: v.optional(v.string(), ""),
|
|
16
|
-
primaryWalletAddress: v.optional(v.string(), ""),
|
|
17
24
|
walletProvider: v.optional(v.string(), "")
|
|
18
25
|
});
|
|
19
26
|
const MeSchema = v.object({
|
|
@@ -28,8 +35,8 @@ const LoginWithWalletResponseSchema = v.object({
|
|
|
28
35
|
accountId: PublicIdSchema,
|
|
29
36
|
username: v.string()
|
|
30
37
|
});
|
|
31
|
-
const
|
|
32
|
-
|
|
38
|
+
const WalletChallengeSchema = v.object({
|
|
39
|
+
message: WalletChallengeMessageSchema,
|
|
33
40
|
expiresAt: OptionalTimestampMsSchema
|
|
34
41
|
});
|
|
35
42
|
/**
|
|
@@ -61,16 +68,19 @@ var AuthService = class {
|
|
|
61
68
|
await this.#authClient.acceptTerms({}, toConnectCallOptions(options));
|
|
62
69
|
}
|
|
63
70
|
/**
|
|
64
|
-
* Requests a
|
|
65
|
-
*
|
|
66
|
-
* after about five minutes. The optional expiry is milliseconds since the
|
|
67
|
-
* Unix epoch.
|
|
71
|
+
* Requests a server-issued SIWE message. Sign its exact UTF-8 bytes with
|
|
72
|
+
* personal_sign; do not hash or reconstruct it. Expiry is epoch milliseconds.
|
|
68
73
|
*/
|
|
69
|
-
async
|
|
70
|
-
|
|
74
|
+
async createWalletChallenge(input, options) {
|
|
75
|
+
const validated = parse(CreateWalletChallengeInputSchema, input);
|
|
76
|
+
if (validated.purpose === "create_subaccount" && validated.signerAddress.toLowerCase() !== validated.smartAccountAddress.toLowerCase()) throw new ValidationError("Subaccount challenge signer must equal the smart account address.");
|
|
77
|
+
return parse(WalletChallengeSchema, await this.#publicClient.createWalletChallenge({
|
|
78
|
+
...validated,
|
|
79
|
+
purpose: validated.purpose === "login" ? 1 : 2
|
|
80
|
+
}, toConnectCallOptions(options)));
|
|
71
81
|
}
|
|
72
82
|
/**
|
|
73
|
-
* Exchanges a signed
|
|
83
|
+
* Exchanges a signed SIWE message for an authenticated session token and account identity returned by the auth API.
|
|
74
84
|
*/
|
|
75
85
|
async loginWithWallet(input, options) {
|
|
76
86
|
const validatedInput = parse(LoginWithWalletInputSchema, input);
|
|
@@ -79,6 +89,6 @@ var AuthService = class {
|
|
|
79
89
|
}
|
|
80
90
|
};
|
|
81
91
|
//#endregion
|
|
82
|
-
export { AuthService, LoginWithWalletInputSchema };
|
|
92
|
+
export { AuthService, CreateWalletChallengeInputSchema, LoginWithWalletInputSchema };
|
|
83
93
|
|
|
84
94
|
//# sourceMappingURL=auth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","names":["#publicClient","Proto.AuthService","#authClient"],"sources":["../../../src/services/auth/auth.ts"],"sourcesContent":["import { createClient, type Client } from \"@connectrpc/connect\";\nimport * as Proto from \"../../gen/auth/v1/auth_pb.js\";\nimport * as v from \"valibot\";\nimport { parse } from \"../../shared/validation.js\";\nimport { ProfileService } from \"./profile/profile.js\";\nimport {\n OptionalTimestampMsSchema,\n PublicIdSchema,\n TimestampSchema,\n} from \"../../shared/schemas.js\";\nimport {\n toConnectCallOptions,\n type PolyesterMutationOptions,\n type PolyesterRequestOptions,\n} from \"../../shared/request-options.js\";\nimport { MfaSessionInfoSchema } from \"../mfa/mfa.schemas.js\";\nimport type { PolyesterRealtime } from \"../../realtime/index.js\";\nimport type { AuthAndPublicApiTransports } from \"../../shared/transports.js\";\n\nexport const
|
|
1
|
+
{"version":3,"file":"auth.js","names":["#publicClient","Proto.AuthService","#authClient"],"sources":["../../../src/services/auth/auth.ts"],"sourcesContent":["import { createClient, type Client } from \"@connectrpc/connect\";\nimport * as Proto from \"../../gen/auth/v1/auth_pb.js\";\nimport * as v from \"valibot\";\nimport { ValidationError } from \"../../shared/errors.js\";\nimport { parse } from \"../../shared/validation.js\";\nimport { ProfileService } from \"./profile/profile.js\";\nimport {\n OptionalTimestampMsSchema,\n PublicIdSchema,\n TimestampSchema,\n} from \"../../shared/schemas.js\";\nimport {\n toConnectCallOptions,\n type PolyesterMutationOptions,\n type PolyesterRequestOptions,\n} from \"../../shared/request-options.js\";\nimport { MfaSessionInfoSchema } from \"../mfa/mfa.schemas.js\";\nimport type { PolyesterRealtime } from \"../../realtime/index.js\";\nimport type { AuthAndPublicApiTransports } from \"../../shared/transports.js\";\n\nimport {\n WalletAddressSchema,\n WalletChallengeUriSchema,\n WalletChallengeMessageSchema,\n WalletSignatureSchema,\n} from \"./wallet-challenge.schemas.js\";\n\nexport const CreateWalletChallengeInputSchema = v.strictObject({\n smartAccountAddress: WalletAddressSchema,\n signerAddress: WalletAddressSchema,\n uri: WalletChallengeUriSchema,\n purpose: v.picklist([\"login\", \"create_subaccount\"]),\n});\nexport type CreateWalletChallengeInput = v.InferInput<typeof CreateWalletChallengeInputSchema>;\nexport type WalletChallengePurpose = CreateWalletChallengeInput[\"purpose\"];\n\nexport const LoginWithWalletInputSchema = v.strictObject({\n smartAccountAddress: WalletAddressSchema,\n message: WalletChallengeMessageSchema,\n signature: WalletSignatureSchema,\n userAgent: v.optional(v.string(), \"\"),\n ip: v.optional(v.string(), \"\"),\n walletProvider: v.optional(v.string(), \"\"),\n});\n\nexport type LoginWithWalletInput = v.InferInput<typeof LoginWithWalletInputSchema>;\n\nconst MeSchema = v.object({\n accountId: PublicIdSchema,\n apiKeyId: v.optional(v.string()),\n username: v.string(),\n session: v.optional(MfaSessionInfoSchema),\n});\n\nexport type Me = v.InferOutput<typeof MeSchema>;\n\nconst LoginWithWalletResponseSchema = v.object({\n accessToken: v.string(),\n expiresAt: v.optional(TimestampSchema),\n accountId: PublicIdSchema,\n username: v.string(),\n});\n\nexport type LoginWithWalletResponse = v.InferOutput<typeof LoginWithWalletResponseSchema>;\n\nconst WalletChallengeSchema = v.object({\n message: WalletChallengeMessageSchema,\n expiresAt: OptionalTimestampMsSchema,\n});\n\nexport type WalletChallenge = v.InferOutput<typeof WalletChallengeSchema>;\n\n/**\n * Handles wallet-based authentication, caller introspection, and authenticated profile operations.\n */\nexport class AuthService {\n #publicClient: Client<typeof Proto.AuthService>;\n #authClient: Client<typeof Proto.AuthService>;\n profile: ProfileService;\n\n constructor(transports: AuthAndPublicApiTransports, realtime: PolyesterRealtime) {\n this.#publicClient = createClient(Proto.AuthService, transports.publicApi);\n this.#authClient = createClient(Proto.AuthService, transports.authApi);\n this.profile = new ProfileService(transports, realtime);\n }\n\n /**\n * Returns the authenticated caller's account context, including account ID, optional API key ID, username, and session assurance details from the presented token or API key.\n */\n async me(options?: PolyesterRequestOptions): Promise<Me> {\n const res = await this.#authClient.me({}, toConnectCallOptions(options));\n return parse(MeSchema, res);\n }\n\n /**\n * Records explicit consent to the current terms for the caller's root account.\n * Call only after the user consents. Requires an interactive JWT session;\n * API keys are not allowed. Repeated acceptance succeeds without changing\n * the first acceptance time. No MFA is required.\n */\n async acceptTerms(options?: PolyesterMutationOptions): Promise<void> {\n await this.#authClient.acceptTerms({}, toConnectCallOptions(options));\n }\n\n /**\n * Requests a server-issued SIWE message. Sign its exact UTF-8 bytes with\n * personal_sign; do not hash or reconstruct it. Expiry is epoch milliseconds.\n */\n async createWalletChallenge(\n input: CreateWalletChallengeInput,\n options?: PolyesterRequestOptions,\n ): Promise<WalletChallenge> {\n const validated = parse(CreateWalletChallengeInputSchema, input);\n if (\n validated.purpose === \"create_subaccount\" &&\n validated.signerAddress.toLowerCase() !== validated.smartAccountAddress.toLowerCase()\n ) {\n throw new ValidationError(\n \"Subaccount challenge signer must equal the smart account address.\",\n );\n }\n return parse(\n WalletChallengeSchema,\n await this.#publicClient.createWalletChallenge(\n {\n ...validated,\n purpose:\n validated.purpose === \"login\"\n ? Proto.WalletChallengePurpose.LOGIN\n : Proto.WalletChallengePurpose.CREATE_SUBACCOUNT,\n },\n toConnectCallOptions(options),\n ),\n );\n }\n\n /**\n * Exchanges a signed SIWE message for an authenticated session token and account identity returned by the auth API.\n */\n protected async loginWithWallet(\n input: LoginWithWalletInput,\n options?: PolyesterMutationOptions,\n ): Promise<LoginWithWalletResponse> {\n const validatedInput = parse(LoginWithWalletInputSchema, input);\n const res = await this.#publicClient.loginWithWallet(\n validatedInput,\n toConnectCallOptions(options),\n );\n return parse(LoginWithWalletResponseSchema, res);\n }\n}\n"],"mappings":";;;;;;;;;;;AA2BA,MAAa,mCAAmC,EAAE,aAAa;CAC3D,qBAAqB;CACrB,eAAe;CACf,KAAK;CACL,SAAS,EAAE,SAAS,CAAC,SAAS,mBAAmB,CAAC;AACtD,CAAC;AAID,MAAa,6BAA6B,EAAE,aAAa;CACrD,qBAAqB;CACrB,SAAS;CACT,WAAW;CACX,WAAW,EAAE,SAAS,EAAE,OAAO,GAAG,EAAE;CACpC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAG,EAAE;CAC7B,gBAAgB,EAAE,SAAS,EAAE,OAAO,GAAG,EAAE;AAC7C,CAAC;AAID,MAAM,WAAW,EAAE,OAAO;CACtB,WAAW;CACX,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC;CAC/B,UAAU,EAAE,OAAO;CACnB,SAAS,EAAE,SAAS,oBAAoB;AAC5C,CAAC;AAID,MAAM,gCAAgC,EAAE,OAAO;CAC3C,aAAa,EAAE,OAAO;CACtB,WAAW,EAAE,SAAS,eAAe;CACrC,WAAW;CACX,UAAU,EAAE,OAAO;AACvB,CAAC;AAID,MAAM,wBAAwB,EAAE,OAAO;CACnC,SAAS;CACT,WAAW;AACf,CAAC;;;;AAOD,IAAa,cAAb,MAAyB;CACrB;CACA;CACA;CAEA,YAAY,YAAwC,UAA6B;EAC7E,KAAKA,gBAAgB,aAAaC,eAAmB,WAAW,SAAS;EACzE,KAAKC,cAAc,aAAaD,eAAmB,WAAW,OAAO;EACrE,KAAK,UAAU,IAAI,eAAe,YAAY,QAAQ;CAC1D;;;;CAKA,MAAM,GAAG,SAAgD;EACrD,MAAM,MAAM,MAAM,KAAKC,YAAY,GAAG,CAAC,GAAG,qBAAqB,OAAO,CAAC;EACvE,OAAO,MAAM,UAAU,GAAG;CAC9B;;;;;;;CAQA,MAAM,YAAY,SAAmD;EACjE,MAAM,KAAKA,YAAY,YAAY,CAAC,GAAG,qBAAqB,OAAO,CAAC;CACxE;;;;;CAMA,MAAM,sBACF,OACA,SACwB;EACxB,MAAM,YAAY,MAAM,kCAAkC,KAAK;EAC/D,IACI,UAAU,YAAY,uBACtB,UAAU,cAAc,YAAY,MAAM,UAAU,oBAAoB,YAAY,GAEpF,MAAM,IAAI,gBACN,mEACJ;EAEJ,OAAO,MACH,uBACA,MAAM,KAAKF,cAAc,sBACrB;GACI,GAAG;GACH,SACI,UAAU,YAAY,UAAA,IAAA;EAG9B,GACA,qBAAqB,OAAO,CAChC,CACJ;CACJ;;;;CAKA,MAAgB,gBACZ,OACA,SACgC;EAChC,MAAM,iBAAiB,MAAM,4BAA4B,KAAK;EAC9D,MAAM,MAAM,MAAM,KAAKA,cAAc,gBACjC,gBACA,qBAAqB,OAAO,CAChC;EACA,OAAO,MAAM,+BAA+B,GAAG;CACnD;AACJ"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { LoginWithWalletInput, LoginWithWalletResponse, Me,
|
|
1
|
+
import { CreateWalletChallengeInput, LoginWithWalletInput, LoginWithWalletResponse, Me, WalletChallenge, WalletChallengePurpose } from "./auth.js";
|
|
2
2
|
import { AccountSignerAuthEvents, CreateSubaccountParams, CreateSubaccountResult, LoginOptions, LoginResult } from "./account-signer-auth.js";
|
|
3
|
-
export type { AccountSignerAuthEvents, CreateSubaccountResult as AccountSignerCreateSubaccountResult, CreateSubaccountParams, LoginOptions, LoginResult, LoginWithWalletInput, LoginWithWalletResponse, Me,
|
|
3
|
+
export type { AccountSignerAuthEvents, CreateSubaccountResult as AccountSignerCreateSubaccountResult, CreateSubaccountParams, CreateWalletChallengeInput, LoginOptions, LoginResult, LoginWithWalletInput, LoginWithWalletResponse, Me, WalletChallenge, WalletChallengePurpose };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { isEvmAddress } from "../../utils/evm.js";
|
|
2
|
+
import * as v from "valibot";
|
|
3
|
+
//#region src/services/auth/wallet-challenge.schemas.ts
|
|
4
|
+
const WalletAddressSchema = v.pipe(v.string(), v.check(isEvmAddress, "Invalid wallet address."));
|
|
5
|
+
const WalletChallengeUriSchema = v.pipe(v.string(), v.maxLength(2048), v.check((value) => {
|
|
6
|
+
if (!/^https?:\/\/[^/?#\s\\]+$/.test(value)) return false;
|
|
7
|
+
try {
|
|
8
|
+
const url = new URL(value);
|
|
9
|
+
return !url.username && !url.password;
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}, "Wallet challenge URI must be an HTTP(S) origin without a path, query, fragment, or user information."));
|
|
14
|
+
/** Preserve the server-issued UTF-8 message exactly as signed. */
|
|
15
|
+
const WalletChallengeMessageSchema = v.pipe(v.string(), v.minLength(1), v.check((value) => new TextEncoder().encode(value).length <= 4096, "Wallet challenge message exceeds 4096 UTF-8 bytes."));
|
|
16
|
+
const WalletSignatureSchema = v.pipe(v.string(), v.minLength(1), v.maxLength(8192));
|
|
17
|
+
//#endregion
|
|
18
|
+
export { WalletAddressSchema, WalletChallengeMessageSchema, WalletChallengeUriSchema, WalletSignatureSchema };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=wallet-challenge.schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wallet-challenge.schemas.js","names":[],"sources":["../../../src/services/auth/wallet-challenge.schemas.ts"],"sourcesContent":["import * as v from \"valibot\";\nimport { isEvmAddress } from \"../../utils/evm.js\";\n\nexport const WalletAddressSchema = v.pipe(\n v.string(),\n v.check(isEvmAddress, \"Invalid wallet address.\"),\n);\n\nexport const WalletChallengeUriSchema = v.pipe(\n v.string(),\n v.maxLength(2048),\n v.check((value) => {\n if (!/^https?:\\/\\/[^/?#\\s\\\\]+$/.test(value)) return false;\n try {\n const url = new URL(value);\n return !url.username && !url.password;\n } catch {\n return false;\n }\n }, \"Wallet challenge URI must be an HTTP(S) origin without a path, query, fragment, or user information.\"),\n);\n\n/** Preserve the server-issued UTF-8 message exactly as signed. */\nexport const WalletChallengeMessageSchema = v.pipe(\n v.string(),\n v.minLength(1),\n v.check(\n (value) => new TextEncoder().encode(value).length <= 4096,\n \"Wallet challenge message exceeds 4096 UTF-8 bytes.\",\n ),\n);\n\nexport const WalletSignatureSchema = v.pipe(v.string(), v.minLength(1), v.maxLength(8192));\n"],"mappings":";;;AAGA,MAAa,sBAAsB,EAAE,KACjC,EAAE,OAAO,GACT,EAAE,MAAM,cAAc,yBAAyB,CACnD;AAEA,MAAa,2BAA2B,EAAE,KACtC,EAAE,OAAO,GACT,EAAE,UAAU,IAAI,GAChB,EAAE,OAAO,UAAU;CACf,IAAI,CAAC,2BAA2B,KAAK,KAAK,GAAG,OAAO;CACpD,IAAI;EACA,MAAM,MAAM,IAAI,IAAI,KAAK;EACzB,OAAO,CAAC,IAAI,YAAY,CAAC,IAAI;CACjC,QAAQ;EACJ,OAAO;CACX;AACJ,GAAG,sGAAsG,CAC7G;;AAGA,MAAa,+BAA+B,EAAE,KAC1C,EAAE,OAAO,GACT,EAAE,UAAU,CAAC,GACb,EAAE,OACG,UAAU,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,UAAU,MACrD,oDACJ,CACJ;AAEA,MAAa,wBAAwB,EAAE,KAAK,EAAE,OAAO,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,UAAU,IAAI,CAAC"}
|