Skip to content

Commit 3304dcc

Browse files
authored
feat(clerk-js,types): Support Coinbase Wallet strategy during sign in/up flows (#4052)
1 parent fece720 commit 3304dcc

File tree

12 files changed

+189
-33
lines changed

12 files changed

+189
-33
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@clerk/clerk-js": minor
3+
"@clerk/clerk-react": minor
4+
"@clerk/types": minor
5+
---
6+
7+
Add support for Coinbase Wallet strategy during sign in/up flows. Users can now authenticate using their Coinbase Wallet browser extension in the same way as MetaMask

packages/clerk-js/src/core/clerk.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ import { logger } from '@clerk/shared/logger';
1818
import { eventPrebuiltComponentMounted, TelemetryCollector } from '@clerk/shared/telemetry';
1919
import type {
2020
ActiveSessionResource,
21+
AuthenticateWithCoinbaseParams,
2122
AuthenticateWithGoogleOneTapParams,
2223
AuthenticateWithMetamaskParams,
2324
Clerk as ClerkInterface,
2425
ClerkAPIError,
26+
ClerkAuthenticateWithWeb3Params,
2527
ClerkOptions,
2628
ClientResource,
2729
CreateOrganizationParams,
@@ -57,6 +59,7 @@ import type {
5759
UserButtonProps,
5860
UserProfileProps,
5961
UserResource,
62+
Web3Provider,
6063
} from '@clerk/types';
6164

6265
import type { MountComponentRenderer } from '../ui/Components';
@@ -69,7 +72,10 @@ import {
6972
createPageLifecycle,
7073
disabledOrganizationsFeature,
7174
errorThrower,
75+
generateSignatureWithCoinbase,
76+
generateSignatureWithMetamask,
7277
getClerkQueryParam,
78+
getWeb3Identifier,
7379
hasExternalAccountSignUpError,
7480
ignoreEventValue,
7581
inActiveBrowserTab,
@@ -1333,25 +1339,41 @@ export class Clerk implements ClerkInterface {
13331339
}) as Promise<SignInResource | SignUpResource>;
13341340
};
13351341

1336-
public authenticateWithMetamask = async ({
1342+
public authenticateWithMetamask = async (props: AuthenticateWithMetamaskParams = {}): Promise<void> => {
1343+
await this.authenticateWithWeb3({ ...props, strategy: 'web3_metamask_signature' });
1344+
};
1345+
1346+
public authenticateWithCoinbase = async (props: AuthenticateWithCoinbaseParams = {}): Promise<void> => {
1347+
await this.authenticateWithWeb3({ ...props, strategy: 'web3_coinbase_signature' });
1348+
};
1349+
1350+
public authenticateWithWeb3 = async ({
13371351
redirectUrl,
13381352
signUpContinueUrl,
13391353
customNavigate,
13401354
unsafeMetadata,
1341-
}: AuthenticateWithMetamaskParams = {}): Promise<void> => {
1355+
strategy,
1356+
}: ClerkAuthenticateWithWeb3Params): Promise<void> => {
13421357
if (!this.client || !this.environment) {
13431358
return;
13441359
}
1345-
1360+
const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider;
1361+
const identifier = await getWeb3Identifier({ provider });
1362+
const generateSignature = provider === 'metamask' ? generateSignatureWithMetamask : generateSignatureWithCoinbase;
13461363
const navigate = (to: string) =>
13471364
customNavigate && typeof customNavigate === 'function' ? customNavigate(to) : this.navigate(to);
13481365

13491366
let signInOrSignUp: SignInResource | SignUpResource;
13501367
try {
1351-
signInOrSignUp = await this.client.signIn.authenticateWithMetamask();
1368+
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({ identifier, generateSignature, strategy });
13521369
} catch (err) {
13531370
if (isError(err, ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND)) {
1354-
signInOrSignUp = await this.client.signUp.authenticateWithMetamask({ unsafeMetadata });
1371+
signInOrSignUp = await this.client.signUp.authenticateWithWeb3({
1372+
identifier,
1373+
generateSignature,
1374+
unsafeMetadata,
1375+
strategy,
1376+
});
13551377

13561378
if (
13571379
signUpContinueUrl &&

packages/clerk-js/src/core/resources/SignIn.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,18 @@ import type {
2727
SignInStartEmailLinkFlowParams,
2828
SignInStatus,
2929
VerificationResource,
30+
Web3Provider,
3031
Web3SignatureConfig,
3132
Web3SignatureFactor,
3233
} from '@clerk/types';
3334

34-
import { generateSignatureWithMetamask, getMetamaskIdentifier, windowNavigate } from '../../utils';
35+
import {
36+
generateSignatureWithCoinbase,
37+
generateSignatureWithMetamask,
38+
getCoinbaseIdentifier,
39+
getMetamaskIdentifier,
40+
windowNavigate,
41+
} from '../../utils';
3542
import {
3643
ClerkWebAuthnError,
3744
convertJSONToPublicKeyRequestOptions,
@@ -107,6 +114,9 @@ export class SignIn extends BaseResource implements SignInResource {
107114
case 'web3_metamask_signature':
108115
config = { web3WalletId: factor.web3WalletId } as Web3SignatureConfig;
109116
break;
117+
case 'web3_coinbase_signature':
118+
config = { web3WalletId: factor.web3WalletId } as Web3SignatureConfig;
119+
break;
110120
case 'reset_password_phone_code':
111121
config = { phoneNumberId: factor.phoneNumberId } as ResetPasswordPhoneCodeFactorConfig;
112122
break;
@@ -223,16 +233,16 @@ export class SignIn extends BaseResource implements SignInResource {
223233
};
224234

225235
public authenticateWithWeb3 = async (params: AuthenticateWithWeb3Params): Promise<SignInResource> => {
226-
const { identifier, generateSignature } = params || {};
236+
const { identifier, generateSignature, strategy = 'web3_metamask_signature' } = params || {};
237+
const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider;
238+
227239
if (!(typeof generateSignature === 'function')) {
228240
clerkMissingOptionError('generateSignature');
229241
}
230242

231243
await this.create({ identifier });
232244

233-
const web3FirstFactor = this.supportedFirstFactors?.find(
234-
f => f.strategy === 'web3_metamask_signature',
235-
) as Web3SignatureFactor;
245+
const web3FirstFactor = this.supportedFirstFactors?.find(f => f.strategy === strategy) as Web3SignatureFactor;
236246

237247
if (!web3FirstFactor) {
238248
clerkVerifyWeb3WalletCalledBeforeCreate('SignIn');
@@ -241,14 +251,19 @@ export class SignIn extends BaseResource implements SignInResource {
241251
await this.prepareFirstFactor(web3FirstFactor);
242252

243253
const { nonce } = this.firstFactorVerification;
254+
if (!nonce) {
255+
clerkVerifyWeb3WalletCalledBeforeCreate('SignIn');
256+
}
257+
244258
const signature = await generateSignature({
245259
identifier: this.identifier!,
246-
nonce: nonce!,
260+
nonce: nonce,
261+
provider,
247262
});
248263

249264
return this.attemptFirstFactor({
250265
signature,
251-
strategy: 'web3_metamask_signature',
266+
strategy,
252267
});
253268
};
254269

@@ -257,6 +272,16 @@ export class SignIn extends BaseResource implements SignInResource {
257272
return this.authenticateWithWeb3({
258273
identifier,
259274
generateSignature: generateSignatureWithMetamask,
275+
strategy: 'web3_metamask_signature',
276+
});
277+
};
278+
279+
public authenticateWithCoinbase = async (): Promise<SignInResource> => {
280+
const identifier = await getCoinbaseIdentifier();
281+
return this.authenticateWithWeb3({
282+
identifier,
283+
generateSignature: generateSignatureWithCoinbase,
284+
strategy: 'web3_coinbase_signature',
260285
});
261286
};
262287

@@ -326,7 +351,7 @@ export class SignIn extends BaseResource implements SignInResource {
326351
validatePassword: ReturnType<typeof createValidatePassword> = (password, cb) => {
327352
if (SignIn.clerk.__unstable__environment?.userSettings.passwordSettings) {
328353
return createValidatePassword({
329-
...(SignIn.clerk.__unstable__environment?.userSettings.passwordSettings as any),
354+
...SignIn.clerk.__unstable__environment?.userSettings.passwordSettings,
330355
validatePassword: true,
331356
})(password, cb);
332357
}

packages/clerk-js/src/core/resources/SignUp.ts

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import type {
1010
PrepareEmailAddressVerificationParams,
1111
PreparePhoneNumberVerificationParams,
1212
PrepareVerificationParams,
13-
SignUpAuthenticateWithMetamaskParams,
13+
PrepareWeb3WalletVerificationParams,
14+
SignUpAuthenticateWithWeb3Params,
1415
SignUpCreateParams,
1516
SignUpField,
1617
SignUpIdentificationField,
@@ -19,14 +20,22 @@ import type {
1920
SignUpStatus,
2021
SignUpUpdateParams,
2122
StartEmailLinkFlowParams,
23+
Web3Provider,
2224
} from '@clerk/types';
2325

24-
import { generateSignatureWithMetamask, getMetamaskIdentifier, windowNavigate } from '../../utils';
26+
import {
27+
generateSignatureWithCoinbase,
28+
generateSignatureWithMetamask,
29+
getCoinbaseIdentifier,
30+
getMetamaskIdentifier,
31+
windowNavigate,
32+
} from '../../utils';
2533
import { getCaptchaToken, retrieveCaptchaInfo } from '../../utils/captcha';
2634
import { createValidatePassword } from '../../utils/passwords/password';
2735
import { normalizeUnsafeMetadata } from '../../utils/resourceParams';
2836
import {
2937
clerkInvalidFAPIResponse,
38+
clerkMissingOptionError,
3039
clerkVerifyEmailAddressCalledBeforeCreate,
3140
clerkVerifyWeb3WalletCalledBeforeCreate,
3241
} from '../errors';
@@ -170,38 +179,55 @@ export class SignUp extends BaseResource implements SignUpResource {
170179
return this.attemptVerification({ ...params, strategy: 'phone_code' });
171180
};
172181

173-
prepareWeb3WalletVerification = (): Promise<SignUpResource> => {
174-
return this.prepareVerification({ strategy: 'web3_metamask_signature' });
182+
prepareWeb3WalletVerification = (params?: PrepareWeb3WalletVerificationParams): Promise<SignUpResource> => {
183+
return this.prepareVerification({ strategy: 'web3_metamask_signature', ...params });
175184
};
176185

177186
attemptWeb3WalletVerification = async (params: AttemptWeb3WalletVerificationParams): Promise<SignUpResource> => {
178-
const { signature } = params;
179-
return this.attemptVerification({ signature, strategy: 'web3_metamask_signature' });
187+
const { signature, strategy = 'web3_metamask_signature' } = params;
188+
return this.attemptVerification({ signature, strategy });
180189
};
181190

182191
public authenticateWithWeb3 = async (
183192
params: AuthenticateWithWeb3Params & { unsafeMetadata?: SignUpUnsafeMetadata },
184193
): Promise<SignUpResource> => {
185-
const { generateSignature, identifier, unsafeMetadata } = params || {};
194+
const { generateSignature, identifier, unsafeMetadata, strategy = 'web3_metamask_signature' } = params || {};
195+
const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider;
196+
197+
if (!(typeof generateSignature === 'function')) {
198+
clerkMissingOptionError('generateSignature');
199+
}
200+
186201
const web3Wallet = identifier || this.web3wallet!;
187202
await this.create({ web3Wallet, unsafeMetadata });
188-
await this.prepareWeb3WalletVerification();
203+
await this.prepareWeb3WalletVerification({ strategy });
189204

190205
const { nonce } = this.verifications.web3Wallet;
191206
if (!nonce) {
192207
clerkVerifyWeb3WalletCalledBeforeCreate('SignUp');
193208
}
194209

195-
const signature = await generateSignature({ identifier, nonce });
196-
return this.attemptWeb3WalletVerification({ signature });
210+
const signature = await generateSignature({ identifier, nonce, provider });
211+
return this.attemptWeb3WalletVerification({ signature, strategy });
197212
};
198213

199-
public authenticateWithMetamask = async (params?: SignUpAuthenticateWithMetamaskParams): Promise<SignUpResource> => {
214+
public authenticateWithMetamask = async (params?: SignUpAuthenticateWithWeb3Params): Promise<SignUpResource> => {
200215
const identifier = await getMetamaskIdentifier();
201216
return this.authenticateWithWeb3({
202217
identifier,
203218
generateSignature: generateSignatureWithMetamask,
204219
unsafeMetadata: params?.unsafeMetadata,
220+
strategy: 'web3_metamask_signature',
221+
});
222+
};
223+
224+
public authenticateWithCoinbase = async (params?: SignUpAuthenticateWithWeb3Params): Promise<SignUpResource> => {
225+
const identifier = await getCoinbaseIdentifier();
226+
return this.authenticateWithWeb3({
227+
identifier,
228+
generateSignature: generateSignatureWithCoinbase,
229+
unsafeMetadata: params?.unsafeMetadata,
230+
strategy: 'web3_coinbase_signature',
205231
});
206232
};
207233

@@ -245,7 +271,7 @@ export class SignUp extends BaseResource implements SignUpResource {
245271
validatePassword: ReturnType<typeof createValidatePassword> = (password, cb) => {
246272
if (SignUp.clerk.__unstable__environment?.userSettings.passwordSettings) {
247273
return createValidatePassword({
248-
...(SignUp.clerk.__unstable__environment?.userSettings.passwordSettings as any),
274+
...SignUp.clerk.__unstable__environment?.userSettings.passwordSettings,
249275
validatePassword: true,
250276
})(password, cb);
251277
}

packages/clerk-js/src/ui/components/SignIn/SignInSocialButtons.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ export const SignInSocialButtons = React.memo((props: SocialButtonsProps) => {
2828
.authenticateWithRedirect({ strategy, redirectUrl, redirectUrlComplete })
2929
.catch(err => handleError(err, [], card.setError));
3030
}}
31-
web3Callback={() => {
31+
web3Callback={strategy => {
3232
return clerk
33-
.authenticateWithMetamask({
33+
.authenticateWithWeb3({
3434
customNavigate: navigate,
3535
redirectUrl: redirectUrlComplete,
3636
signUpContinueUrl: ctx.signUpContinueUrl,
37+
strategy,
3738
})
3839
.catch(err => handleError(err, [], card.setError));
3940
}}

packages/clerk-js/src/ui/components/SignUp/SignUpSocialButtons.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,14 @@ export const SignUpSocialButtons = React.memo((props: SignUpSocialButtonsProps)
3838
})
3939
.catch(err => handleError(err, [], card.setError));
4040
}}
41-
web3Callback={() => {
41+
web3Callback={strategy => {
4242
return clerk
43-
.authenticateWithMetamask({
43+
.authenticateWithWeb3({
4444
customNavigate: navigate,
4545
redirectUrl: redirectUrlComplete,
4646
signUpContinueUrl: 'continue',
4747
unsafeMetadata: ctx.unsafeMetadata,
48+
strategy,
4849
})
4950
.catch(err => handleError(err, [], card.setError));
5051
}}

packages/clerk-js/src/utils/web3.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ export async function getMetamaskIdentifier(): Promise<string> {
4545
return await getWeb3Identifier({ provider: 'metamask' });
4646
}
4747

48+
export async function getCoinbaseIdentifier(): Promise<string> {
49+
return await getWeb3Identifier({ provider: 'coinbase' });
50+
}
51+
4852
type GenerateSignatureParams = {
4953
identifier: string;
5054
nonce: string;
@@ -53,3 +57,7 @@ type GenerateSignatureParams = {
5357
export async function generateSignatureWithMetamask({ identifier, nonce }: GenerateSignatureParams): Promise<string> {
5458
return await generateWeb3Signature({ identifier, nonce, provider: 'metamask' });
5559
}
60+
61+
export async function generateSignatureWithCoinbase({ identifier, nonce }: GenerateSignatureParams): Promise<string> {
62+
return await generateWeb3Signature({ identifier, nonce, provider: 'coinbase' });
63+
}

0 commit comments

Comments
 (0)