From 51e143c58146b6b1d87585c7be5e2180ce9c41df Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:08:41 -0700 Subject: [PATCH 1/4] feat(elements): Add sign in with Metamask --- .../machines/sign-in/router.machine.ts | 3 ++ .../machines/sign-in/start.machine.ts | 35 ++++++++++++++++++- .../internals/machines/sign-in/start.types.ts | 3 ++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/elements/src/internals/machines/sign-in/router.machine.ts b/packages/elements/src/internals/machines/sign-in/router.machine.ts index 32e53ca8f2a..a99d9b0efe9 100644 --- a/packages/elements/src/internals/machines/sign-in/router.machine.ts +++ b/packages/elements/src/internals/machines/sign-in/router.machine.ts @@ -334,6 +334,9 @@ export const SignInRouterMachine = setup({ 'AUTHENTICATE.PASSKEY.AUTOFILL': { actions: sendTo('start', ({ event }) => event), }, + 'AUTHENTICATE.WEB3': { + actions: sendTo('start', ({ event }) => event), + }, NEXT: [ { guard: 'isComplete', diff --git a/packages/elements/src/internals/machines/sign-in/start.machine.ts b/packages/elements/src/internals/machines/sign-in/start.machine.ts index fac82a42ec7..f716c680998 100644 --- a/packages/elements/src/internals/machines/sign-in/start.machine.ts +++ b/packages/elements/src/internals/machines/sign-in/start.machine.ts @@ -1,4 +1,4 @@ -import type { SignInResource } from '@clerk/types'; +import type { SignInResource, Web3Strategy } from '@clerk/types'; import { fromPromise, not, sendTo, setup } from 'xstate'; import { SIGN_IN_DEFAULT_BASE_PATH } from '~/internals/constants'; @@ -23,6 +23,14 @@ export const SignInStartMachine = setup({ flow, }); }), + attemptWeb3: fromPromise( + ({ input: { parent, strategy } }) => { + if (strategy === 'web3_metamask_signature') { + return parent.getSnapshot().context.clerk.client.signIn.authenticateWithMetamask(); + } + throw new Error(); + }, + ), attempt: fromPromise( ({ input: { fields, parent } }) => { const clerk = parent.getSnapshot().context.clerk; @@ -94,6 +102,11 @@ export const SignInStartMachine = setup({ target: 'AttemptingPasskeyAutoFill', reenter: false, }, + 'AUTHENTICATE.WEB3': { + guard: not('isExampleMode'), + target: 'AttemptingWeb3', + reenter: true, + }, }, }, Attempting: { @@ -163,5 +176,25 @@ export const SignInStartMachine = setup({ }, }, }, + AttemptingWeb3: { + tags: ['state:attempting', 'state:loading'], + entry: 'sendToLoading', + invoke: { + id: 'attemptWeb3', + src: 'attemptWeb3', + input: ({ context, event }) => ({ + parent: context.parent, + // TODO: figure out how to type this correctly + strategy: event.strategy, + }), + onDone: { + actions: ['sendToNext', 'sendToLoading'], + }, + onError: { + actions: ['setFormErrors', 'sendToLoading'], + target: 'Pending', + }, + }, + }, }, }); diff --git a/packages/elements/src/internals/machines/sign-in/start.types.ts b/packages/elements/src/internals/machines/sign-in/start.types.ts index 7f935eb84b9..4ad02ee0163 100644 --- a/packages/elements/src/internals/machines/sign-in/start.types.ts +++ b/packages/elements/src/internals/machines/sign-in/start.types.ts @@ -1,4 +1,5 @@ import type { ClerkAPIResponseError } from '@clerk/shared/error'; +import type { Web3Strategy } from '@clerk/types'; import type { ActorRefFrom, DoneActorEvent, ErrorActorEvent } from 'xstate'; import type { FormMachine } from '~/internals/machines/form'; @@ -14,12 +15,14 @@ export type SignInStartTags = 'state:pending' | 'state:attempting' | 'state:load export type SignInStartSubmitEvent = { type: 'SUBMIT' }; export type SignInStartPasskeyEvent = { type: 'AUTHENTICATE.PASSKEY' }; export type SignInStartPasskeyAutofillEvent = { type: 'AUTHENTICATE.PASSKEY.AUTOFILL' }; +export type SignInStartWeb3Event = { type: 'AUTHENTICATE.WEB3'; strategy: Web3Strategy }; export type SignInStartEvents = | ErrorActorEvent | SignInStartSubmitEvent | SignInStartPasskeyEvent | SignInStartPasskeyAutofillEvent + | SignInStartWeb3Event | DoneActorEvent; // ---------------------------------- Input ---------------------------------- // From d0304c8566af4de58c8aafe6a8e312a3d99f6e3b Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:16:50 -0700 Subject: [PATCH 2/4] fix(elements): Assert event type and throw ClerkElementsRuntimeError --- .../internals/machines/sign-in/start.machine.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/elements/src/internals/machines/sign-in/start.machine.ts b/packages/elements/src/internals/machines/sign-in/start.machine.ts index f716c680998..0569abecc0b 100644 --- a/packages/elements/src/internals/machines/sign-in/start.machine.ts +++ b/packages/elements/src/internals/machines/sign-in/start.machine.ts @@ -1,7 +1,8 @@ import type { SignInResource, Web3Strategy } from '@clerk/types'; -import { fromPromise, not, sendTo, setup } from 'xstate'; +import { assertEvent, fromPromise, not, sendTo, setup } from 'xstate'; import { SIGN_IN_DEFAULT_BASE_PATH } from '~/internals/constants'; +import { ClerkElementsRuntimeError } from '~/internals/errors'; import type { FormFields } from '~/internals/machines/form'; import { sendToLoading } from '~/internals/machines/shared'; import { assertActorEventError } from '~/internals/machines/utils/assert'; @@ -28,7 +29,7 @@ export const SignInStartMachine = setup({ if (strategy === 'web3_metamask_signature') { return parent.getSnapshot().context.clerk.client.signIn.authenticateWithMetamask(); } - throw new Error(); + throw new ClerkElementsRuntimeError(`Unsupported Web3 strategy: ${strategy}`); }, ), attempt: fromPromise( @@ -182,11 +183,13 @@ export const SignInStartMachine = setup({ invoke: { id: 'attemptWeb3', src: 'attemptWeb3', - input: ({ context, event }) => ({ - parent: context.parent, - // TODO: figure out how to type this correctly - strategy: event.strategy, - }), + input: ({ context, event }) => { + assertEvent(event, 'AUTHENTICATE.WEB3'); + return { + parent: context.parent, + strategy: event.strategy, + }; + }, onDone: { actions: ['sendToNext', 'sendToLoading'], }, From ee99e8a07850b1c3ba353d930f0a53fed361218c Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:58:37 -0700 Subject: [PATCH 3/4] feat(elements): Add Metamask sign up support --- .../machines/sign-up/router.machine.ts | 3 ++ .../machines/sign-up/start.machine.ts | 40 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/elements/src/internals/machines/sign-up/router.machine.ts b/packages/elements/src/internals/machines/sign-up/router.machine.ts index 59edc04d9a5..6177ff8a915 100644 --- a/packages/elements/src/internals/machines/sign-up/router.machine.ts +++ b/packages/elements/src/internals/machines/sign-up/router.machine.ts @@ -195,6 +195,9 @@ export const SignUpRouterMachine = setup({ params: { strategy: 'saml' }, }), }, + 'AUTHENTICATE.WEB3': { + actions: sendTo('start', ({ event }) => event), + }, 'FORM.ATTACH': { description: 'Attach/re-attach the form to the router.', actions: enqueueActions(({ enqueue, event }) => { diff --git a/packages/elements/src/internals/machines/sign-up/start.machine.ts b/packages/elements/src/internals/machines/sign-up/start.machine.ts index 5be184e9eaf..27cd3cab926 100644 --- a/packages/elements/src/internals/machines/sign-up/start.machine.ts +++ b/packages/elements/src/internals/machines/sign-up/start.machine.ts @@ -1,7 +1,8 @@ -import type { SignUpResource } from '@clerk/types'; -import { fromPromise, not, sendTo, setup } from 'xstate'; +import type { SignUpResource, Web3Strategy } from '@clerk/types'; +import { assertEvent, fromPromise, not, sendTo, setup } from 'xstate'; import { SIGN_UP_DEFAULT_BASE_PATH } from '~/internals/constants'; +import { ClerkElementsRuntimeError } from '~/internals/errors'; import type { FormFields } from '~/internals/machines/form'; import { sendToLoading } from '~/internals/machines/shared'; import { fieldsToSignUpParams } from '~/internals/machines/sign-up/utils'; @@ -29,6 +30,14 @@ export const SignUpStartMachine = setup({ return parent.getSnapshot().context.clerk.client.signUp.create(params); }, ), + attemptWeb3: fromPromise( + ({ input: { parent, strategy } }) => { + if (strategy === 'web3_metamask_signature') { + return parent.getSnapshot().context.clerk.client.signUp.authenticateWithMetamask(); + } + throw new ClerkElementsRuntimeError(`Unsupported Web3 strategy: ${strategy}`); + }, + ), thirdParty: ThirdPartyMachine, }, actions: { @@ -84,6 +93,11 @@ export const SignUpStartMachine = setup({ target: 'Attempting', reenter: true, }, + 'AUTHENTICATE.WEB3': { + guard: not('isExampleMode'), + target: 'AttemptingWeb3', + reenter: true, + }, }, }, Attempting: { @@ -105,5 +119,27 @@ export const SignUpStartMachine = setup({ }, }, }, + AttemptingWeb3: { + tags: ['state:attempting', 'state:loading'], + entry: 'sendToLoading', + invoke: { + id: 'attemptCreateWeb3', + src: 'attemptWeb3', + input: ({ context, event }) => { + assertEvent(event, 'AUTHENTICATE.WEB3'); + return { + parent: context.parent, + strategy: event.strategy, + }; + }, + onDone: { + actions: ['sendToNext', 'sendToLoading'], + }, + onError: { + actions: ['setFormErrors', 'sendToLoading'], + target: 'Pending', + }, + }, + }, }, }); From e6f6aa588b3380bdac14267f3b887608595cbc0b Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:59:48 -0700 Subject: [PATCH 4/4] chore(elements): Add changeset --- .changeset/rich-parrots-crash.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rich-parrots-crash.md diff --git a/.changeset/rich-parrots-crash.md b/.changeset/rich-parrots-crash.md new file mode 100644 index 00000000000..a2230f6bffa --- /dev/null +++ b/.changeset/rich-parrots-crash.md @@ -0,0 +1,5 @@ +--- +"@clerk/elements": minor +--- + +Add Metamask (Web3) support for sign in and sign up