@account-kit/signer 4.15.3 → 4.16.1-alpha.3
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/dist/esm/base.d.ts +59 -1
- package/dist/esm/base.js +159 -55
- package/dist/esm/base.js.map +1 -1
- package/dist/esm/client/base.d.ts +38 -3
- package/dist/esm/client/base.js +10 -3
- package/dist/esm/client/base.js.map +1 -1
- package/dist/esm/client/index.d.ts +40 -1
- package/dist/esm/client/index.js +164 -7
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/types.d.ts +69 -1
- package/dist/esm/client/types.js.map +1 -1
- package/dist/esm/errors.d.ts +6 -0
- package/dist/esm/errors.js +18 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/session/manager.d.ts +1 -0
- package/dist/esm/session/manager.js.map +1 -1
- package/dist/esm/signer.d.ts +3 -1
- package/dist/esm/signer.js.map +1 -1
- package/dist/esm/solanaSigner.d.ts +12 -0
- package/dist/esm/solanaSigner.js +67 -0
- package/dist/esm/solanaSigner.js.map +1 -0
- package/dist/esm/types.d.ts +8 -0
- package/dist/esm/types.js +5 -0
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils/parseMfaError.d.ts +2 -0
- package/dist/esm/utils/parseMfaError.js +15 -0
- package/dist/esm/utils/parseMfaError.js.map +1 -0
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/types/base.d.ts +59 -1
- package/dist/types/base.d.ts.map +1 -1
- package/dist/types/client/base.d.ts +38 -3
- package/dist/types/client/base.d.ts.map +1 -1
- package/dist/types/client/index.d.ts +40 -1
- package/dist/types/client/index.d.ts.map +1 -1
- package/dist/types/client/types.d.ts +69 -1
- package/dist/types/client/types.d.ts.map +1 -1
- package/dist/types/errors.d.ts +6 -0
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/session/manager.d.ts +1 -0
- package/dist/types/session/manager.d.ts.map +1 -1
- package/dist/types/signer.d.ts +3 -1
- package/dist/types/signer.d.ts.map +1 -1
- package/dist/types/solanaSigner.d.ts +13 -0
- package/dist/types/solanaSigner.d.ts.map +1 -0
- package/dist/types/types.d.ts +8 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/parseMfaError.d.ts +3 -0
- package/dist/types/utils/parseMfaError.d.ts.map +1 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/types/version.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/base.ts +206 -62
- package/src/client/base.ts +59 -4
- package/src/client/index.ts +174 -7
- package/src/client/types.ts +79 -1
- package/src/errors.ts +11 -1
- package/src/index.ts +6 -1
- package/src/session/manager.ts +5 -1
- package/src/signer.ts +6 -1
- package/src/solanaSigner.ts +83 -0
- package/src/types.ts +9 -0
- package/src/utils/parseMfaError.ts +15 -0
- package/src/version.ts +1 -1
package/src/client/index.ts
CHANGED
|
@@ -17,9 +17,23 @@ import type {
|
|
|
17
17
|
OauthConfig,
|
|
18
18
|
OtpParams,
|
|
19
19
|
User,
|
|
20
|
+
MfaFactor,
|
|
21
|
+
EnableMfaParams,
|
|
22
|
+
EnableMfaResult,
|
|
23
|
+
VerifyMfaParams,
|
|
24
|
+
RemoveMfaParams,
|
|
20
25
|
} from "./types.js";
|
|
26
|
+
import { MfaRequiredError, NotAuthenticatedError } from "../errors.js";
|
|
27
|
+
import { parseMfaError } from "../utils/parseMfaError.js";
|
|
21
28
|
|
|
22
29
|
const CHECK_CLOSE_INTERVAL = 500;
|
|
30
|
+
const MFA_PAYLOAD = {
|
|
31
|
+
GET: "get_mfa",
|
|
32
|
+
ADD: "add_mfa",
|
|
33
|
+
DELETE: "delete_mfas",
|
|
34
|
+
VERIFY: "verify_mfa",
|
|
35
|
+
LIST: "list_mfas",
|
|
36
|
+
};
|
|
23
37
|
|
|
24
38
|
export const AlchemySignerClientParamsSchema = z.object({
|
|
25
39
|
connection: ConnectionConfigSchema,
|
|
@@ -198,13 +212,25 @@ export class AlchemySignerWebClient extends BaseSignerClient<ExportWalletParams>
|
|
|
198
212
|
const { email, emailMode, expirationSeconds } = params;
|
|
199
213
|
const publicKey = await this.initIframeStamper();
|
|
200
214
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
215
|
+
try {
|
|
216
|
+
return await this.request("/v1/auth", {
|
|
217
|
+
email,
|
|
218
|
+
emailMode,
|
|
219
|
+
targetPublicKey: publicKey,
|
|
220
|
+
expirationSeconds,
|
|
221
|
+
redirectParams: params.redirectParams?.toString(),
|
|
222
|
+
multiFactors: params.multiFactors,
|
|
223
|
+
});
|
|
224
|
+
} catch (error) {
|
|
225
|
+
const multiFactors = parseMfaError(error);
|
|
226
|
+
|
|
227
|
+
// If MFA is required, and emailMode is Magic Link, the user must submit mfa with the request or
|
|
228
|
+
// the the server will return an error with the required mfa factors.
|
|
229
|
+
if (multiFactors) {
|
|
230
|
+
throw new MfaRequiredError(multiFactors);
|
|
231
|
+
}
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
208
234
|
};
|
|
209
235
|
|
|
210
236
|
/**
|
|
@@ -242,6 +268,12 @@ export class AlchemySignerWebClient extends BaseSignerClient<ExportWalletParams>
|
|
|
242
268
|
...args,
|
|
243
269
|
targetPublicKey,
|
|
244
270
|
});
|
|
271
|
+
|
|
272
|
+
if (!credentialBundle) {
|
|
273
|
+
throw new Error(
|
|
274
|
+
"Failed to submit OTP code. Check if multiFactor is required."
|
|
275
|
+
);
|
|
276
|
+
}
|
|
245
277
|
return { bundle: credentialBundle };
|
|
246
278
|
}
|
|
247
279
|
|
|
@@ -670,6 +702,141 @@ export class AlchemySignerWebClient extends BaseSignerClient<ExportWalletParams>
|
|
|
670
702
|
const nonce = this.getOauthNonce(publicKey);
|
|
671
703
|
return this.request("/v1/prepare-oauth", { nonce });
|
|
672
704
|
};
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Retrieves the list of MFA factors configured for the current user.
|
|
708
|
+
*
|
|
709
|
+
* @returns {Promise<{ multiFactors: MfaFactor[] }>} A promise that resolves to an array of configured MFA factors
|
|
710
|
+
* @throws {NotAuthenticatedError} If no user is authenticated
|
|
711
|
+
*/
|
|
712
|
+
public override getMfaFactors = async (): Promise<{
|
|
713
|
+
multiFactors: MfaFactor[];
|
|
714
|
+
}> => {
|
|
715
|
+
if (!this.user) {
|
|
716
|
+
throw new NotAuthenticatedError();
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const stampedRequest = await this.turnkeyClient.stampSignRawPayload({
|
|
720
|
+
organizationId: this.user.orgId,
|
|
721
|
+
type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2",
|
|
722
|
+
timestampMs: Date.now().toString(),
|
|
723
|
+
parameters: {
|
|
724
|
+
encoding: "PAYLOAD_ENCODING_HEXADECIMAL",
|
|
725
|
+
hashFunction: "HASH_FUNCTION_NO_OP",
|
|
726
|
+
payload: MFA_PAYLOAD.LIST,
|
|
727
|
+
signWith: this.user.address,
|
|
728
|
+
},
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
return this.request("/v1/auth-list-multi-factors", {
|
|
732
|
+
stampedRequest,
|
|
733
|
+
});
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Initiates the setup of a new MFA factor for the current user. Mfa will need to be verified before it is active.
|
|
738
|
+
*
|
|
739
|
+
* @param {EnableMfaParams} params The parameters required to enable a new MFA factor
|
|
740
|
+
* @returns {Promise<EnableMfaResult>} A promise that resolves to the factor setup information
|
|
741
|
+
* @throws {NotAuthenticatedError} If no user is authenticated
|
|
742
|
+
* @throws {Error} If an unsupported factor type is provided
|
|
743
|
+
*/
|
|
744
|
+
public override addMfa = async (
|
|
745
|
+
params: EnableMfaParams
|
|
746
|
+
): Promise<EnableMfaResult> => {
|
|
747
|
+
if (!this.user) {
|
|
748
|
+
throw new NotAuthenticatedError();
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const stampedRequest = await this.turnkeyClient.stampSignRawPayload({
|
|
752
|
+
organizationId: this.user.orgId,
|
|
753
|
+
type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2",
|
|
754
|
+
timestampMs: Date.now().toString(),
|
|
755
|
+
parameters: {
|
|
756
|
+
encoding: "PAYLOAD_ENCODING_HEXADECIMAL",
|
|
757
|
+
hashFunction: "HASH_FUNCTION_NO_OP",
|
|
758
|
+
payload: MFA_PAYLOAD.ADD,
|
|
759
|
+
signWith: this.user.address,
|
|
760
|
+
},
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
switch (params.multiFactorType) {
|
|
764
|
+
case "totp":
|
|
765
|
+
return this.request("/v1/auth-request-multi-factor", {
|
|
766
|
+
stampedRequest,
|
|
767
|
+
multiFactorType: params.multiFactorType,
|
|
768
|
+
});
|
|
769
|
+
default:
|
|
770
|
+
throw new Error(
|
|
771
|
+
`Unsupported MFA factor type: ${params.multiFactorType}`
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Verifies a newly created MFA factor to complete the setup process.
|
|
778
|
+
*
|
|
779
|
+
* @param {VerifyMfaParams} params The parameters required to verify the MFA factor
|
|
780
|
+
* @returns {Promise<{ multiFactors: MfaFactor[] }>} A promise that resolves to the updated list of MFA factors
|
|
781
|
+
* @throws {NotAuthenticatedError} If no user is authenticated
|
|
782
|
+
*/
|
|
783
|
+
public override verifyMfa = async (
|
|
784
|
+
params: VerifyMfaParams
|
|
785
|
+
): Promise<{ multiFactors: MfaFactor[] }> => {
|
|
786
|
+
if (!this.user) {
|
|
787
|
+
throw new NotAuthenticatedError();
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const stampedRequest = await this.turnkeyClient.stampSignRawPayload({
|
|
791
|
+
organizationId: this.user.orgId,
|
|
792
|
+
type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2",
|
|
793
|
+
timestampMs: Date.now().toString(),
|
|
794
|
+
parameters: {
|
|
795
|
+
encoding: "PAYLOAD_ENCODING_HEXADECIMAL",
|
|
796
|
+
hashFunction: "HASH_FUNCTION_NO_OP",
|
|
797
|
+
payload: MFA_PAYLOAD.VERIFY,
|
|
798
|
+
signWith: this.user.address,
|
|
799
|
+
},
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
return this.request("/v1/auth-verify-multi-factor", {
|
|
803
|
+
stampedRequest,
|
|
804
|
+
multiFactorId: params.multiFactorId,
|
|
805
|
+
multiFactorCode: params.multiFactorCode,
|
|
806
|
+
});
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Removes existing MFA factors by ID.
|
|
811
|
+
*
|
|
812
|
+
* @param {RemoveMfaParams} params The parameters specifying which factors to disable
|
|
813
|
+
* @returns {Promise<{ multiFactors: MfaFactor[] }>} A promise that resolves to the updated list of MFA factors
|
|
814
|
+
* @throws {NotAuthenticatedError} If no user is authenticated
|
|
815
|
+
*/
|
|
816
|
+
public override removeMfa = async (
|
|
817
|
+
params: RemoveMfaParams
|
|
818
|
+
): Promise<{ multiFactors: MfaFactor[] }> => {
|
|
819
|
+
if (!this.user) {
|
|
820
|
+
throw new NotAuthenticatedError();
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
const stampedRequest = await this.turnkeyClient.stampSignRawPayload({
|
|
824
|
+
organizationId: this.user.orgId,
|
|
825
|
+
type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2",
|
|
826
|
+
timestampMs: Date.now().toString(),
|
|
827
|
+
parameters: {
|
|
828
|
+
encoding: "PAYLOAD_ENCODING_HEXADECIMAL",
|
|
829
|
+
hashFunction: "HASH_FUNCTION_NO_OP",
|
|
830
|
+
payload: MFA_PAYLOAD.DELETE,
|
|
831
|
+
signWith: this.user.address,
|
|
832
|
+
},
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
return this.request("/v1/auth-delete-multi-factors", {
|
|
836
|
+
stampedRequest,
|
|
837
|
+
multiFactorIds: params.multiFactorIds,
|
|
838
|
+
});
|
|
839
|
+
};
|
|
673
840
|
}
|
|
674
841
|
|
|
675
842
|
/**
|
package/src/client/types.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type User = {
|
|
|
13
13
|
orgId: string;
|
|
14
14
|
userId: string;
|
|
15
15
|
address: Address;
|
|
16
|
+
solanaAddress?: string;
|
|
16
17
|
credentialId?: string;
|
|
17
18
|
idToken?: string;
|
|
18
19
|
claims?: Record<string, unknown>;
|
|
@@ -51,6 +52,7 @@ export type EmailAuthParams = {
|
|
|
51
52
|
expirationSeconds?: number;
|
|
52
53
|
targetPublicKey: string;
|
|
53
54
|
redirectParams?: URLSearchParams;
|
|
55
|
+
multiFactors?: VerifyMfaParams[];
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
export type OauthParams = Extract<AuthParams, { type: "oauth" }> & {
|
|
@@ -63,6 +65,7 @@ export type OtpParams = {
|
|
|
63
65
|
otpCode: string;
|
|
64
66
|
targetPublicKey: string;
|
|
65
67
|
expirationSeconds?: number;
|
|
68
|
+
multiFactors?: VerifyMfaParams[];
|
|
66
69
|
};
|
|
67
70
|
|
|
68
71
|
export type SignupResponse = {
|
|
@@ -121,10 +124,12 @@ export type SignerEndpoints = [
|
|
|
121
124
|
Route: "/v1/auth";
|
|
122
125
|
Body: Omit<EmailAuthParams, "redirectParams"> & {
|
|
123
126
|
redirectParams?: string;
|
|
127
|
+
multiFactors?: VerifyMfaParams[];
|
|
124
128
|
};
|
|
125
129
|
Response: {
|
|
126
130
|
orgId: string;
|
|
127
131
|
otpId?: string;
|
|
132
|
+
multiFactors?: MfaFactor[];
|
|
128
133
|
};
|
|
129
134
|
},
|
|
130
135
|
{
|
|
@@ -155,7 +160,45 @@ export type SignerEndpoints = [
|
|
|
155
160
|
{
|
|
156
161
|
Route: "/v1/otp";
|
|
157
162
|
Body: OtpParams;
|
|
158
|
-
Response: {
|
|
163
|
+
Response: {
|
|
164
|
+
credentialBundle: string | null;
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
Route: "/v1/auth-list-multi-factors";
|
|
169
|
+
Body: {
|
|
170
|
+
stampedRequest: TSignedRequest;
|
|
171
|
+
};
|
|
172
|
+
Response: {
|
|
173
|
+
multiFactors: MfaFactor[];
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
Route: "/v1/auth-delete-multi-factors";
|
|
178
|
+
Body: {
|
|
179
|
+
stampedRequest: TSignedRequest;
|
|
180
|
+
multiFactorIds: string[];
|
|
181
|
+
};
|
|
182
|
+
Response: {
|
|
183
|
+
multiFactors: MfaFactor[];
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
Route: "/v1/auth-request-multi-factor";
|
|
188
|
+
Body: {
|
|
189
|
+
stampedRequest: TSignedRequest;
|
|
190
|
+
multiFactorType: MultiFactorType;
|
|
191
|
+
};
|
|
192
|
+
Response: EnableMfaResult;
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
Route: "/v1/auth-verify-multi-factor";
|
|
196
|
+
Body: VerifyMfaParams & {
|
|
197
|
+
stampedRequest: TSignedRequest;
|
|
198
|
+
};
|
|
199
|
+
Response: {
|
|
200
|
+
multiFactors: MfaFactor[];
|
|
201
|
+
};
|
|
159
202
|
}
|
|
160
203
|
];
|
|
161
204
|
|
|
@@ -199,3 +242,38 @@ export type GetOauthProviderUrlArgs = {
|
|
|
199
242
|
oauthConfig?: OauthConfig;
|
|
200
243
|
usesRelativeUrl?: boolean;
|
|
201
244
|
};
|
|
245
|
+
|
|
246
|
+
export type MfaFactor = {
|
|
247
|
+
multiFactorId: string;
|
|
248
|
+
multiFactorType: string;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
type MultiFactorType = "totp";
|
|
252
|
+
|
|
253
|
+
export type EnableMfaParams = {
|
|
254
|
+
multiFactorType: MultiFactorType;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export type EnableMfaResult = {
|
|
258
|
+
multiFactorType: MultiFactorType;
|
|
259
|
+
multiFactorId: string;
|
|
260
|
+
multiFactorTotpUrl: string;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
export type VerifyMfaParams = {
|
|
264
|
+
multiFactorId: string;
|
|
265
|
+
multiFactorCode: string;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
export type RemoveMfaParams = {
|
|
269
|
+
multiFactorIds: string[];
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
export type MfaChallenge = {
|
|
273
|
+
multiFactorId: string;
|
|
274
|
+
multiFactorChallenge:
|
|
275
|
+
| {
|
|
276
|
+
code: string;
|
|
277
|
+
}
|
|
278
|
+
| Record<string, any>;
|
|
279
|
+
};
|
package/src/errors.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseError } from "@aa-sdk/core";
|
|
2
|
-
|
|
2
|
+
import type { MfaFactor } from "./client/types";
|
|
3
3
|
export class NotAuthenticatedError extends BaseError {
|
|
4
4
|
override name = "NotAuthenticatedError";
|
|
5
5
|
constructor() {
|
|
@@ -23,3 +23,13 @@ export class OAuthProvidersError extends BaseError {
|
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
export class MfaRequiredError extends BaseError {
|
|
28
|
+
override name = "MfaRequiredError";
|
|
29
|
+
public multiFactors: MfaFactor[];
|
|
30
|
+
|
|
31
|
+
constructor(multiFactors: MfaFactor[]) {
|
|
32
|
+
super("MFA is required for this user");
|
|
33
|
+
this.multiFactors = multiFactors;
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -6,12 +6,17 @@ export {
|
|
|
6
6
|
OauthFailedError,
|
|
7
7
|
} from "./client/index.js";
|
|
8
8
|
export type * from "./client/types.js";
|
|
9
|
+
export {
|
|
10
|
+
NotAuthenticatedError,
|
|
11
|
+
OAuthProvidersError,
|
|
12
|
+
MfaRequiredError,
|
|
13
|
+
} from "./errors.js";
|
|
9
14
|
export {
|
|
10
15
|
DEFAULT_SESSION_MS,
|
|
11
16
|
SessionManagerParamsSchema,
|
|
12
17
|
} from "./session/manager.js";
|
|
13
18
|
export type * from "./signer.js";
|
|
14
19
|
export { AlchemyWebSigner } from "./signer.js";
|
|
20
|
+
export type * from "./solanaSigner.js";
|
|
15
21
|
export type * from "./types.js";
|
|
16
22
|
export { AlchemySignerStatus } from "./types.js";
|
|
17
|
-
export { OAuthProvidersError, NotAuthenticatedError } from "./errors.js";
|
package/src/session/manager.ts
CHANGED
|
@@ -43,7 +43,11 @@ type Store = Mutate<
|
|
|
43
43
|
[["zustand/subscribeWithSelector", never], ["zustand/persist", SessionState]]
|
|
44
44
|
>;
|
|
45
45
|
|
|
46
|
-
type TemporarySession = {
|
|
46
|
+
type TemporarySession = {
|
|
47
|
+
orgId: string;
|
|
48
|
+
isNewUser?: boolean;
|
|
49
|
+
isMfaRequired?: boolean;
|
|
50
|
+
};
|
|
47
51
|
|
|
48
52
|
export class SessionManager {
|
|
49
53
|
private sessionKey: string;
|
package/src/signer.ts
CHANGED
|
@@ -4,7 +4,10 @@ import {
|
|
|
4
4
|
AlchemySignerClientParamsSchema,
|
|
5
5
|
AlchemySignerWebClient,
|
|
6
6
|
} from "./client/index.js";
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
CredentialCreationOptionOverrides,
|
|
9
|
+
VerifyMfaParams,
|
|
10
|
+
} from "./client/types.js";
|
|
8
11
|
import { SessionManagerParamsSchema } from "./session/manager.js";
|
|
9
12
|
|
|
10
13
|
export type AuthParams =
|
|
@@ -13,6 +16,7 @@ export type AuthParams =
|
|
|
13
16
|
email: string;
|
|
14
17
|
emailMode?: "magicLink" | "otp";
|
|
15
18
|
redirectParams?: URLSearchParams;
|
|
19
|
+
multiFactors?: VerifyMfaParams[];
|
|
16
20
|
}
|
|
17
21
|
| { type: "email"; bundle: string; orgId?: string; isNewUser?: boolean }
|
|
18
22
|
| {
|
|
@@ -46,6 +50,7 @@ export type AuthParams =
|
|
|
46
50
|
| {
|
|
47
51
|
type: "otp";
|
|
48
52
|
otpCode: string;
|
|
53
|
+
multiFactors?: VerifyMfaParams[];
|
|
49
54
|
};
|
|
50
55
|
|
|
51
56
|
export type OauthProviderConfig =
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PublicKey,
|
|
3
|
+
Transaction,
|
|
4
|
+
type VersionedTransaction,
|
|
5
|
+
} from "@solana/web3.js";
|
|
6
|
+
import { size, slice, toBytes, toHex, type ByteArray, type Hex } from "viem";
|
|
7
|
+
import type { BaseSignerClient } from "./client/base";
|
|
8
|
+
import { NotAuthenticatedError } from "./errors.js";
|
|
9
|
+
|
|
10
|
+
// TODO: I don't want this to be a class so that the flow is closer to how we do this for `toViemAccount`
|
|
11
|
+
export class SolanaSigner {
|
|
12
|
+
private alchemyClient: BaseSignerClient;
|
|
13
|
+
public address: string;
|
|
14
|
+
|
|
15
|
+
constructor(client: BaseSignerClient) {
|
|
16
|
+
this.alchemyClient = client;
|
|
17
|
+
if (!client.getUser()) throw new Error("Must be authenticated!");
|
|
18
|
+
|
|
19
|
+
// TODO: also throw here
|
|
20
|
+
this.address = client.getUser()!.solanaAddress!;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async addSignature(
|
|
24
|
+
tx: Transaction | VersionedTransaction
|
|
25
|
+
): Promise<Transaction | VersionedTransaction> {
|
|
26
|
+
const user = this.alchemyClient.getUser();
|
|
27
|
+
if (!user) {
|
|
28
|
+
throw new NotAuthenticatedError();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!user.solanaAddress) {
|
|
32
|
+
throw new Error("no solana address");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const fromKey = new PublicKey(user.solanaAddress);
|
|
36
|
+
const messageToSign = this.getMessageToSign(tx);
|
|
37
|
+
const signature = await this.alchemyClient.signRawMessage(
|
|
38
|
+
messageToSign,
|
|
39
|
+
"SOLANA"
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
tx.addSignature(
|
|
43
|
+
fromKey,
|
|
44
|
+
Buffer.from(toBytes(this.formatSignatureForSolana(signature)))
|
|
45
|
+
);
|
|
46
|
+
return tx;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async signMessage(message: Uint8Array): Promise<ByteArray> {
|
|
50
|
+
const user = this.alchemyClient.getUser();
|
|
51
|
+
if (!user) {
|
|
52
|
+
throw new NotAuthenticatedError();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!user.solanaAddress) {
|
|
56
|
+
throw new Error("no solana address");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const messageToSign = toHex(message);
|
|
60
|
+
const signature = await this.alchemyClient.signRawMessage(
|
|
61
|
+
messageToSign,
|
|
62
|
+
"SOLANA"
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return toBytes(this.formatSignatureForSolana(signature));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private formatSignatureForSolana(signature: Hex): Hex {
|
|
69
|
+
if (size(signature) === 64) return signature;
|
|
70
|
+
|
|
71
|
+
return slice(signature, 0, 64);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private getMessageToSign(tx: Transaction | VersionedTransaction): Hex {
|
|
75
|
+
let messageToSign;
|
|
76
|
+
if (tx instanceof Transaction) {
|
|
77
|
+
messageToSign = tx.serializeMessage();
|
|
78
|
+
} else {
|
|
79
|
+
messageToSign = Buffer.from(tx.message.serialize());
|
|
80
|
+
}
|
|
81
|
+
return toHex(messageToSign);
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -6,6 +6,10 @@ export type AlchemySignerEvents = {
|
|
|
6
6
|
disconnected(): void;
|
|
7
7
|
statusChanged(status: AlchemySignerStatus): void;
|
|
8
8
|
errorChanged(error: ErrorInfo | undefined): void;
|
|
9
|
+
mfaStatusChanged(mfaStatus: {
|
|
10
|
+
mfaRequired: boolean;
|
|
11
|
+
mfaFactorId?: string;
|
|
12
|
+
}): void;
|
|
9
13
|
};
|
|
10
14
|
|
|
11
15
|
export type AlchemySignerEvent = keyof AlchemySignerEvents;
|
|
@@ -21,6 +25,11 @@ export enum AlchemySignerStatus {
|
|
|
21
25
|
AWAITING_OTP_AUTH = "AWAITING_OTP_AUTH",
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
export enum AlchemyMfaStatus {
|
|
29
|
+
NOT_REQUIRED = "not_required",
|
|
30
|
+
REQUIRED = "required",
|
|
31
|
+
}
|
|
32
|
+
|
|
24
33
|
export interface ErrorInfo {
|
|
25
34
|
name: string;
|
|
26
35
|
message: string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MfaFactor } from "../client/types.js";
|
|
2
|
+
|
|
3
|
+
export function parseMfaError(error: unknown): MfaFactor[] | null {
|
|
4
|
+
if (error instanceof Error) {
|
|
5
|
+
try {
|
|
6
|
+
const parsed = JSON.parse(error.message);
|
|
7
|
+
if (parsed?.data?.multiFactors) {
|
|
8
|
+
return parsed.data.multiFactors;
|
|
9
|
+
}
|
|
10
|
+
} catch {
|
|
11
|
+
// ignore JSON parse failures
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
package/src/version.ts
CHANGED