Skip to content

Commit 0993f57

Browse files
authored
feat(elements): Add sign up/sign in with Metamask (#3879)
1 parent 35fb0c7 commit 0993f57

File tree

6 files changed

+90
-4
lines changed

6 files changed

+90
-4
lines changed

.changeset/rich-parrots-crash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@clerk/elements": minor
3+
---
4+
5+
Add Metamask (Web3) support for sign in and sign up

packages/elements/src/internals/machines/sign-in/router.machine.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,9 @@ export const SignInRouterMachine = setup({
340340
'AUTHENTICATE.PASSKEY.AUTOFILL': {
341341
actions: sendTo('start', ({ event }) => event),
342342
},
343+
'AUTHENTICATE.WEB3': {
344+
actions: sendTo('start', ({ event }) => event),
345+
},
343346
NEXT: [
344347
{
345348
guard: 'isComplete',

packages/elements/src/internals/machines/sign-in/start.machine.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import type { SignInResource } from '@clerk/types';
2-
import { fromPromise, not, sendTo, setup } from 'xstate';
1+
import type { SignInResource, Web3Strategy } from '@clerk/types';
2+
import { assertEvent, fromPromise, not, sendTo, setup } from 'xstate';
33

44
import { SIGN_IN_DEFAULT_BASE_PATH } from '~/internals/constants';
5+
import { ClerkElementsRuntimeError } from '~/internals/errors';
56
import type { FormFields } from '~/internals/machines/form';
67
import { sendToLoading } from '~/internals/machines/shared';
78
import { assertActorEventError } from '~/internals/machines/utils/assert';
@@ -23,6 +24,14 @@ export const SignInStartMachine = setup({
2324
flow,
2425
});
2526
}),
27+
attemptWeb3: fromPromise<SignInResource, { parent: SignInRouterMachineActorRef; strategy: Web3Strategy }>(
28+
({ input: { parent, strategy } }) => {
29+
if (strategy === 'web3_metamask_signature') {
30+
return parent.getSnapshot().context.clerk.client.signIn.authenticateWithMetamask();
31+
}
32+
throw new ClerkElementsRuntimeError(`Unsupported Web3 strategy: ${strategy}`);
33+
},
34+
),
2635
attempt: fromPromise<SignInResource, { parent: SignInRouterMachineActorRef; fields: FormFields }>(
2736
({ input: { fields, parent } }) => {
2837
const clerk = parent.getSnapshot().context.clerk;
@@ -94,6 +103,11 @@ export const SignInStartMachine = setup({
94103
target: 'AttemptingPasskeyAutoFill',
95104
reenter: false,
96105
},
106+
'AUTHENTICATE.WEB3': {
107+
guard: not('isExampleMode'),
108+
target: 'AttemptingWeb3',
109+
reenter: true,
110+
},
97111
},
98112
},
99113
Attempting: {
@@ -163,5 +177,27 @@ export const SignInStartMachine = setup({
163177
},
164178
},
165179
},
180+
AttemptingWeb3: {
181+
tags: ['state:attempting', 'state:loading'],
182+
entry: 'sendToLoading',
183+
invoke: {
184+
id: 'attemptWeb3',
185+
src: 'attemptWeb3',
186+
input: ({ context, event }) => {
187+
assertEvent(event, 'AUTHENTICATE.WEB3');
188+
return {
189+
parent: context.parent,
190+
strategy: event.strategy,
191+
};
192+
},
193+
onDone: {
194+
actions: ['sendToNext', 'sendToLoading'],
195+
},
196+
onError: {
197+
actions: ['setFormErrors', 'sendToLoading'],
198+
target: 'Pending',
199+
},
200+
},
201+
},
166202
},
167203
});

packages/elements/src/internals/machines/sign-in/start.types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ClerkAPIResponseError } from '@clerk/shared/error';
2+
import type { Web3Strategy } from '@clerk/types';
23
import type { ActorRefFrom, DoneActorEvent, ErrorActorEvent } from 'xstate';
34

45
import type { FormMachine } from '~/internals/machines/form';
@@ -14,12 +15,14 @@ export type SignInStartTags = 'state:pending' | 'state:attempting' | 'state:load
1415
export type SignInStartSubmitEvent = { type: 'SUBMIT' };
1516
export type SignInStartPasskeyEvent = { type: 'AUTHENTICATE.PASSKEY' };
1617
export type SignInStartPasskeyAutofillEvent = { type: 'AUTHENTICATE.PASSKEY.AUTOFILL' };
18+
export type SignInStartWeb3Event = { type: 'AUTHENTICATE.WEB3'; strategy: Web3Strategy };
1719

1820
export type SignInStartEvents =
1921
| ErrorActorEvent
2022
| SignInStartSubmitEvent
2123
| SignInStartPasskeyEvent
2224
| SignInStartPasskeyAutofillEvent
25+
| SignInStartWeb3Event
2326
| DoneActorEvent;
2427

2528
// ---------------------------------- Input ---------------------------------- //

packages/elements/src/internals/machines/sign-up/router.machine.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,9 @@ export const SignUpRouterMachine = setup({
206206
},
207207
})),
208208
},
209+
'AUTHENTICATE.WEB3': {
210+
actions: sendTo('start', ({ event }) => event),
211+
},
209212
'FORM.ATTACH': {
210213
description: 'Attach/re-attach the form to the router.',
211214
actions: enqueueActions(({ enqueue, event }) => {

packages/elements/src/internals/machines/sign-up/start.machine.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import type { SignUpResource } from '@clerk/types';
2-
import { fromPromise, not, sendTo, setup } from 'xstate';
1+
import type { SignUpResource, Web3Strategy } from '@clerk/types';
2+
import { assertEvent, fromPromise, not, sendTo, setup } from 'xstate';
33

44
import { SIGN_UP_DEFAULT_BASE_PATH } from '~/internals/constants';
5+
import { ClerkElementsRuntimeError } from '~/internals/errors';
56
import type { FormFields } from '~/internals/machines/form';
67
import { sendToLoading } from '~/internals/machines/shared';
78
import { fieldsToSignUpParams } from '~/internals/machines/sign-up/utils';
@@ -29,6 +30,14 @@ export const SignUpStartMachine = setup({
2930
return parent.getSnapshot().context.clerk.client.signUp.create(params);
3031
},
3132
),
33+
attemptWeb3: fromPromise<SignUpResource, { parent: SignInRouterMachineActorRef; strategy: Web3Strategy }>(
34+
({ input: { parent, strategy } }) => {
35+
if (strategy === 'web3_metamask_signature') {
36+
return parent.getSnapshot().context.clerk.client.signUp.authenticateWithMetamask();
37+
}
38+
throw new ClerkElementsRuntimeError(`Unsupported Web3 strategy: ${strategy}`);
39+
},
40+
),
3241
thirdParty: ThirdPartyMachine,
3342
},
3443
actions: {
@@ -84,6 +93,11 @@ export const SignUpStartMachine = setup({
8493
target: 'Attempting',
8594
reenter: true,
8695
},
96+
'AUTHENTICATE.WEB3': {
97+
guard: not('isExampleMode'),
98+
target: 'AttemptingWeb3',
99+
reenter: true,
100+
},
87101
},
88102
},
89103
Attempting: {
@@ -105,5 +119,27 @@ export const SignUpStartMachine = setup({
105119
},
106120
},
107121
},
122+
AttemptingWeb3: {
123+
tags: ['state:attempting', 'state:loading'],
124+
entry: 'sendToLoading',
125+
invoke: {
126+
id: 'attemptCreateWeb3',
127+
src: 'attemptWeb3',
128+
input: ({ context, event }) => {
129+
assertEvent(event, 'AUTHENTICATE.WEB3');
130+
return {
131+
parent: context.parent,
132+
strategy: event.strategy,
133+
};
134+
},
135+
onDone: {
136+
actions: ['sendToNext', 'sendToLoading'],
137+
},
138+
onError: {
139+
actions: ['setFormErrors', 'sendToLoading'],
140+
target: 'Pending',
141+
},
142+
},
143+
},
108144
},
109145
});

0 commit comments

Comments
 (0)