@account-kit/signer 4.57.1 → 4.57.2-alpha.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.
@@ -2,7 +2,7 @@ import { type ConnectionConfig } from "@aa-sdk/core";
2
2
  import { TurnkeyClient, type TSignedRequest } from "@turnkey/http";
3
3
  import EventEmitter from "eventemitter3";
4
4
  import { type Address, type Hex } from "viem";
5
- import type { AlchemySignerClientEvent, AlchemySignerClientEvents, AuthenticatingEventMetadata, CreateAccountParams, RemoveMfaParams, EmailAuthParams, AddMfaParams, AddMfaResult, experimental_CreateApiKeyParams, GetOauthProviderUrlArgs, GetWebAuthnAttestationResult, MfaFactor, JwtParams, JwtResponse, OauthConfig, OauthParams, OtpParams, SignerBody, SignerResponse, SignerRoutes, SignupResponse, User, VerifyMfaParams, SubmitOtpCodeResponse, ValidateMultiFactorsParams, AuthLinkingPrompt, AddOauthProviderParams, CredentialCreationOptionOverrides, OauthProviderInfo, IdTokenOnly, AuthMethods, SmsAuthParams } from "./types.js";
5
+ import type { AlchemySignerClientEvent, AlchemySignerClientEvents, AuthenticatingEventMetadata, CreateAccountParams, RemoveMfaParams, EmailAuthParams, AddMfaParams, AddMfaResult, experimental_CreateApiKeyParams, GetOauthProviderUrlArgs, GetWebAuthnAttestationResult, MfaFactor, JwtParams, JwtResponse, OauthConfig, OauthParams, OtpParams, SignerBody, SignerResponse, SignerRoutes, SignupResponse, User, VerifyMfaParams, SubmitOtpCodeResponse, ValidateMultiFactorsParams, AuthLinkingPrompt, AddOauthProviderParams, CredentialCreationOptionOverrides, OauthProviderInfo, IdTokenOnly, AuthMethods, SmsAuthParams, VerificationOtp } from "./types.js";
6
6
  export interface BaseSignerClientParams {
7
7
  stamper: TurnkeyClient["stamper"];
8
8
  connection: ConnectionConfig;
@@ -111,15 +111,26 @@ export declare abstract class BaseSignerClient<TExportWalletParams = unknown> {
111
111
  * Sets the email for the authenticated user, allowing them to login with that
112
112
  * email.
113
113
  *
114
- * You must contact Alchemy to enable this feature for your team, as there are
115
- * important security considerations. In particular, you must not call this
116
- * without first validating that the user owns this email account.
114
+ * @deprecated You must contact Alchemy to enable this feature for your team,
115
+ * as there are important security considerations. In particular, you must not
116
+ * call this without first validating that the user owns this email account.
117
+ * Use setEmail(email, otp) instead.
117
118
  *
118
119
  * @param {string} email The email to set for the user
119
120
  * @returns {Promise<void>} A promise that resolves when the email is set
120
121
  * @throws {NotAuthenticatedError} If the user is not authenticated
121
122
  */
122
- setEmail: (email: string) => Promise<void>;
123
+ setEmail(email: string): Promise<void>;
124
+ /**
125
+ * Sets the email for the authenticated user, allowing them to login with that
126
+ * email.
127
+ *
128
+ * @param {string} email The email to set for the user
129
+ * @param {VerificationOtp} otp The OTP verification object
130
+ * @returns {Promise<void>} A promise that resolves when the email is set
131
+ * @throws {NotAuthenticatedError} If the user is not authenticated
132
+ */
133
+ setEmail(email: string, otp: VerificationOtp): Promise<void>;
123
134
  /**
124
135
  * Removes the email for the authenticated user, disallowing them from login with that email.
125
136
  *
@@ -128,6 +139,35 @@ export declare abstract class BaseSignerClient<TExportWalletParams = unknown> {
128
139
  */
129
140
  removeEmail: () => Promise<void>;
130
141
  private updateEmail;
142
+ /**
143
+ * Sets the phone number for the authenticated user, allowing them to login with that
144
+ * phone number.
145
+ *
146
+ * @param {string} phone The phone number to set for the user
147
+ * @param {VerificationOtp} otp The OTP verification object
148
+ * @returns {Promise<void>} A promise that resolves when the phone number is set
149
+ * @throws {NotAuthenticatedError} If the user is not authenticated
150
+ */
151
+ setPhoneNumber: (phone: string, otp: VerificationOtp) => Promise<void>;
152
+ /**
153
+ * Removes the phone number for the authenticated user, disallowing them from login with that phone number.
154
+ *
155
+ * @returns {Promise<void>} A promise that resolves when the phone number is removed
156
+ * @throws {NotAuthenticatedError} If the user is not authenticated
157
+ */
158
+ removePhoneNumber: () => Promise<void>;
159
+ private updatePhoneNumber;
160
+ /**
161
+ * Initiates an OTP (One-Time Password) verification process for a user contact.
162
+ *
163
+ * @param {("email" | "sms")} type - The type of OTP to send, either "email" or "sms"
164
+ * @param {string} contact - The email address or phone number to send the OTP to
165
+ * @returns {Promise<{ otpId: string }>} A promise that resolves to an object containing the OTP ID
166
+ * @throws {NotAuthenticatedError} When no user is currently authenticated
167
+ */
168
+ initOtp: (type: "email" | "sms", contact: string) => Promise<{
169
+ otpId: string;
170
+ }>;
131
171
  /**
132
172
  * Handles the creation of authenticators using WebAuthn attestation and the provided options. Requires the user to be authenticated.
133
173
  *
@@ -98,66 +98,143 @@ export class BaseSignerClient {
98
98
  }
99
99
  });
100
100
  /**
101
- * Sets the email for the authenticated user, allowing them to login with that
102
- * email.
101
+ * Removes the email for the authenticated user, disallowing them from login with that email.
103
102
  *
104
- * You must contact Alchemy to enable this feature for your team, as there are
105
- * important security considerations. In particular, you must not call this
106
- * without first validating that the user owns this email account.
103
+ * @returns {Promise<void>} A promise that resolves when the email is removed
104
+ * @throws {NotAuthenticatedError} If the user is not authenticated
105
+ */
106
+ Object.defineProperty(this, "removeEmail", {
107
+ enumerable: true,
108
+ configurable: true,
109
+ writable: true,
110
+ value: async () => {
111
+ await this.updateEmail("");
112
+ }
113
+ });
114
+ Object.defineProperty(this, "updateEmail", {
115
+ enumerable: true,
116
+ configurable: true,
117
+ writable: true,
118
+ value: async (email, verificationToken) => {
119
+ if (!this.user) {
120
+ throw new NotAuthenticatedError();
121
+ }
122
+ // Unverified use is legacy & requires team flag.
123
+ const isUnverified = email && !verificationToken;
124
+ const stampedRequest = isUnverified
125
+ ? await this.turnkeyClient.stampUpdateUser({
126
+ type: "ACTIVITY_TYPE_UPDATE_USER",
127
+ timestampMs: Date.now().toString(),
128
+ organizationId: this.user.orgId,
129
+ parameters: {
130
+ userId: this.user.userId,
131
+ userEmail: email,
132
+ },
133
+ })
134
+ : await this.turnkeyClient.stampUpdateUserEmail({
135
+ type: "ACTIVITY_TYPE_UPDATE_USER_EMAIL",
136
+ timestampMs: Date.now().toString(),
137
+ organizationId: this.user.orgId,
138
+ parameters: {
139
+ userId: this.user.userId,
140
+ userEmail: email,
141
+ verificationToken,
142
+ },
143
+ });
144
+ await this.request("/v1/update-email-auth", {
145
+ stampedRequest,
146
+ });
147
+ this.user = {
148
+ ...this.user,
149
+ email: email || undefined,
150
+ };
151
+ }
152
+ });
153
+ /**
154
+ * Sets the phone number for the authenticated user, allowing them to login with that
155
+ * phone number.
107
156
  *
108
- * @param {string} email The email to set for the user
109
- * @returns {Promise<void>} A promise that resolves when the email is set
157
+ * @param {string} phone The phone number to set for the user
158
+ * @param {VerificationOtp} otp The OTP verification object
159
+ * @returns {Promise<void>} A promise that resolves when the phone number is set
110
160
  * @throws {NotAuthenticatedError} If the user is not authenticated
111
161
  */
112
- Object.defineProperty(this, "setEmail", {
162
+ Object.defineProperty(this, "setPhoneNumber", {
113
163
  enumerable: true,
114
164
  configurable: true,
115
165
  writable: true,
116
- value: async (email) => {
117
- if (!email) {
118
- throw new Error("Email must not be empty. Use removeEmail() to remove email auth.");
166
+ value: async (phone, otp) => {
167
+ if (!phone) {
168
+ throw new Error("Phone number must not be empty. Use removePhoneNumber() to remove phone auth.");
119
169
  }
120
- await this.updateEmail(email);
170
+ const { verificationToken } = await this.request("/v1/verify-otp", {
171
+ otpId: otp.id,
172
+ otpCode: otp.code,
173
+ });
174
+ await this.updatePhoneNumber(phone, verificationToken);
121
175
  }
122
176
  });
123
177
  /**
124
- * Removes the email for the authenticated user, disallowing them from login with that email.
178
+ * Removes the phone number for the authenticated user, disallowing them from login with that phone number.
125
179
  *
126
- * @returns {Promise<void>} A promise that resolves when the email is removed
180
+ * @returns {Promise<void>} A promise that resolves when the phone number is removed
127
181
  * @throws {NotAuthenticatedError} If the user is not authenticated
128
182
  */
129
- Object.defineProperty(this, "removeEmail", {
183
+ Object.defineProperty(this, "removePhoneNumber", {
130
184
  enumerable: true,
131
185
  configurable: true,
132
186
  writable: true,
133
187
  value: async () => {
134
- // This is a hack to remove the email for the user. Turnkey does not
135
- // support clearing the email once set, so we set it to a known
136
- // inaccessible address instead.
137
- await this.updateEmail("not.enabled@example.invalid");
188
+ await this.updatePhoneNumber("");
138
189
  }
139
190
  });
140
- Object.defineProperty(this, "updateEmail", {
191
+ Object.defineProperty(this, "updatePhoneNumber", {
141
192
  enumerable: true,
142
193
  configurable: true,
143
194
  writable: true,
144
- value: async (email) => {
195
+ value: async (phone, verificationToken) => {
145
196
  if (!this.user) {
146
197
  throw new NotAuthenticatedError();
147
198
  }
148
- const stampedRequest = await this.turnkeyClient.stampUpdateUser({
149
- type: "ACTIVITY_TYPE_UPDATE_USER",
199
+ if (phone.trim() && !verificationToken) {
200
+ throw new Error("Verification token is required to change phone number.");
201
+ }
202
+ const stampedRequest = await this.turnkeyClient.stampUpdateUserPhoneNumber({
203
+ type: "ACTIVITY_TYPE_UPDATE_USER_PHONE_NUMBER",
150
204
  timestampMs: Date.now().toString(),
151
205
  organizationId: this.user.orgId,
152
206
  parameters: {
153
207
  userId: this.user.userId,
154
- userEmail: email,
155
- userTagIds: [],
208
+ userPhoneNumber: phone,
209
+ verificationToken,
156
210
  },
157
211
  });
158
- await this.request("/v1/update-email-auth", {
212
+ await this.request("/v1/update-phone-auth", {
159
213
  stampedRequest,
160
214
  });
215
+ this.user = {
216
+ ...this.user,
217
+ phone: phone || undefined,
218
+ };
219
+ }
220
+ });
221
+ /**
222
+ * Initiates an OTP (One-Time Password) verification process for a user contact.
223
+ *
224
+ * @param {("email" | "sms")} type - The type of OTP to send, either "email" or "sms"
225
+ * @param {string} contact - The email address or phone number to send the OTP to
226
+ * @returns {Promise<{ otpId: string }>} A promise that resolves to an object containing the OTP ID
227
+ * @throws {NotAuthenticatedError} When no user is currently authenticated
228
+ */
229
+ Object.defineProperty(this, "initOtp", {
230
+ enumerable: true,
231
+ configurable: true,
232
+ writable: true,
233
+ value: async (type, contact) => {
234
+ return await this.request("/v1/init-otp", {
235
+ otpType: type === "email" ? "OTP_TYPE_EMAIL" : "OTP_TYPE_SMS",
236
+ contact,
237
+ });
161
238
  }
162
239
  });
163
240
  /**
@@ -583,7 +660,7 @@ export class BaseSignerClient {
583
660
  };
584
661
  return {
585
662
  stampHeaderName: "X-Stamp",
586
- stampHeaderValue: base64UrlEncode(Buffer.from(JSON.stringify(stamp))),
663
+ stampHeaderValue: base64UrlEncode(Buffer.from(JSON.stringify(stamp)).buffer),
587
664
  };
588
665
  },
589
666
  })
@@ -1057,7 +1134,7 @@ export class BaseSignerClient {
1057
1134
  openerOrigin: mode === "popup" ? window.location.origin : undefined,
1058
1135
  fetchIdTokenOnly: oauthParams.fetchIdTokenOnly,
1059
1136
  };
1060
- const state = base64UrlEncode(new TextEncoder().encode(JSON.stringify(stateObject)));
1137
+ const state = base64UrlEncode(new TextEncoder().encode(JSON.stringify(stateObject)).buffer);
1061
1138
  const authUrl = new URL(authEndpoint);
1062
1139
  const params = {
1063
1140
  redirect_uri: oauthCallbackUrl,
@@ -1239,5 +1316,27 @@ export class BaseSignerClient {
1239
1316
  this.eventEmitter.emit("connectedPasskey", this.user);
1240
1317
  return result;
1241
1318
  }
1319
+ /**
1320
+ * Implementation for setEmail method with optional OTP verification.
1321
+ *
1322
+ * @param {string} email The email to set for the user
1323
+ * @param {VerificationOtp} [otp] The OTP verification object (optional for legacy usage)
1324
+ * @returns {Promise<void>} A promise that resolves when the email is set
1325
+ */
1326
+ async setEmail(email, otp) {
1327
+ if (!email) {
1328
+ throw new Error("Email must not be empty. Use removeEmail() to remove email auth.");
1329
+ }
1330
+ if (!otp) {
1331
+ // Legacy use, requires team flag.
1332
+ await this.updateEmail(email);
1333
+ return;
1334
+ }
1335
+ const { verificationToken } = await this.request("/v1/verify-otp", {
1336
+ otpId: otp.id,
1337
+ otpCode: otp.code,
1338
+ });
1339
+ await this.updateEmail(email, verificationToken);
1340
+ }
1242
1341
  }
1243
1342
  //# sourceMappingURL=base.js.map