@turnkey/sdk-react 5.0.2 → 5.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @turnkey/sdk-react
2
2
 
3
+ ## 5.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#632](https://github.com/tkhq/sdk/pull/632) [`a38a6e3`](https://github.com/tkhq/sdk/commit/a38a6e36dc2bf9abdea64bc817d1cad95b8a289a) Author [@amircheikh](https://github.com/amircheikh) - Added optional `socialLinking` boolean to the `authConfig`. If true, this will enable social linking for new Google <-> Gmail users. For more information on social linking, visit [our docs](https://docs.turnkey.com/authentication/social-logins#social-linking).
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [[`5afbe51`](https://github.com/tkhq/sdk/commit/5afbe51949bdd1997fad083a4c1e4272ff7409dc), [`10ee5c5`](https://github.com/tkhq/sdk/commit/10ee5c524b477ce998e4fc635152cd101ae5a9cc), [`a38a6e3`](https://github.com/tkhq/sdk/commit/a38a6e36dc2bf9abdea64bc817d1cad95b8a289a), [`40c4035`](https://github.com/tkhq/sdk/commit/40c40359ec7096d0bca39ffc93e89361b3b11a1a), [`593de2d`](https://github.com/tkhq/sdk/commit/593de2d9404ec8cf53426f9cf832c13eefa3fbf2)]:
12
+ - @turnkey/sdk-browser@5.2.0
13
+ - @turnkey/wallet-stamper@1.0.4
14
+ - @turnkey/crypto@2.4.0
15
+ - @turnkey/sdk-server@4.1.0
16
+
17
+ ## 5.1.0
18
+
19
+ ### Minor Changes
20
+
21
+ - Update @turnkey/sdk-types readme and install dependency in packages with common types
22
+
23
+ - [#650](https://github.com/tkhq/sdk/pull/650) [`27fe590`](https://github.com/tkhq/sdk/commit/27fe590cdc3eb6a8cde093eeefda2ee1cdc79412) Author [@turnekybc](https://github.com/turnekybc) - Update @turnkey/sdk-types readme and install dependency in packages with common types
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies [[`27fe590`](https://github.com/tkhq/sdk/commit/27fe590cdc3eb6a8cde093eeefda2ee1cdc79412)]:
28
+ - @turnkey/sdk-browser@5.1.0
29
+ - @turnkey/sdk-types@0.1.0
30
+ - @turnkey/sdk-server@4.0.1
31
+
3
32
  ## 5.0.2
4
33
 
5
34
  ### Patch Changes
package/README.md CHANGED
@@ -121,6 +121,7 @@ function AuthPage() {
121
121
  googleEnabled: true,
122
122
  appleEnabled: false,
123
123
  facebookEnabled: false,
124
+ socialLinking: true, // Enable social linking
124
125
  sessionLengthSeconds: 3600, //1 hour r/w session
125
126
  };
126
127
 
@@ -1,9 +1,5 @@
1
1
  import React from "react";
2
2
  import type { TurnkeyApiTypes, WalletAccount } from "@turnkey/sdk-browser";
3
- export declare enum SessionType {
4
- READ_ONLY = "SESSION_TYPE_READ_ONLY",
5
- READ_WRITE = "SESSION_TYPE_READ_WRITE"
6
- }
7
3
  export interface PasskeyConfig {
8
4
  displayName?: string;
9
5
  name?: string;
@@ -24,6 +20,7 @@ interface AuthProps {
24
20
  googleEnabled: boolean;
25
21
  walletEnabled: boolean;
26
22
  openOAuthInPage?: boolean;
23
+ socialLinking?: boolean;
27
24
  sessionLengthSeconds?: number;
28
25
  googleClientId?: string;
29
26
  appleClientId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Auth.d.ts","sourceRoot":"","sources":["../../../src/components/auth/Auth.tsx"],"names":[],"mappings":"AAGA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAcnD,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI3E,oBAAY,WAAW;IACrB,SAAS,2BAA2B;IACpC,UAAU,4BAA4B;CACvC;AACD,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAoCD,UAAU,SAAS;IACjB,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,UAAU,EAAE;QACV,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,YAAY,EAAE,OAAO,CAAC;QACtB,YAAY,EAAE,OAAO,CAAC;QACtB,eAAe,EAAE,OAAO,CAAC;QACzB,aAAa,EAAE,OAAO,CAAC;QACvB,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,kBAAkB,CAAC,EAAE,eAAe,CAAC,4BAA4B,CAAC,CAAC;IACnE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,QAAA,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,SAAS,CA4pB7B,CAAC;AAEF,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"Auth.d.ts","sourceRoot":"","sources":["../../../src/components/auth/Auth.tsx"],"names":[],"mappings":"AAGA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAenD,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI3E,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAoCD,UAAU,SAAS;IACjB,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,UAAU,EAAE;QACV,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,YAAY,EAAE,OAAO,CAAC;QACtB,YAAY,EAAE,OAAO,CAAC;QACtB,eAAe,EAAE,OAAO,CAAC;QACzB,aAAa,EAAE,OAAO,CAAC;QACvB,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,kBAAkB,CAAC,EAAE,eAAe,CAAC,4BAA4B,CAAC,CAAC;IACnE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,QAAA,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,SAAS,CAquB7B,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -7,6 +7,7 @@ var jsxRuntime = require('react/jsx-runtime');
7
7
  var Auth_module = require('./Auth.module.css.js');
8
8
  var React = require('react');
9
9
  var PhoneInput = require('./PhoneInput.js');
10
+ var jwtDecode = require('jwt-decode');
10
11
  var Google = require('./Google.js');
11
12
  var Apple = require('./Apple.js');
12
13
  var Facebook = require('./Facebook.js');
@@ -21,12 +22,8 @@ var useTurnkey = require('../../hooks/use-turnkey.js');
21
22
  var constants = require('./constants.js');
22
23
  var sdkServer = require('@turnkey/sdk-server');
23
24
  var parsePhoneNumberFromString = require('libphonenumber-js');
25
+ var sdkTypes = require('@turnkey/sdk-types');
24
26
 
25
- exports.SessionType = void 0;
26
- (function (SessionType) {
27
- SessionType["READ_ONLY"] = "SESSION_TYPE_READ_ONLY";
28
- SessionType["READ_WRITE"] = "SESSION_TYPE_READ_WRITE";
29
- })(exports.SessionType || (exports.SessionType = {}));
30
27
  const passkeyIcon = jsxRuntime.jsxs("svg", {
31
28
  xmlns: "http://www.w3.org/2000/svg",
32
29
  width: "43",
@@ -174,7 +171,7 @@ const Auth = ({
174
171
  await indexedDbClient.resetKeyPair();
175
172
  const pubKey = await indexedDbClient.getPublicKey();
176
173
  await passkeyClient?.loginWithPasskey({
177
- sessionType: exports.SessionType.READ_WRITE,
174
+ sessionType: sdkTypes.SessionType.READ_WRITE,
178
175
  publicKey: pubKey,
179
176
  expirationSeconds: authConfig.sessionLengthSeconds?.toString()
180
177
  });
@@ -190,7 +187,7 @@ const Auth = ({
190
187
  await indexedDbClient?.resetKeyPair();
191
188
  const pubKey = await indexedDbClient.getPublicKey();
192
189
  await passkeyClient?.loginWithPasskey({
193
- sessionType: exports.SessionType.READ_WRITE,
190
+ sessionType: sdkTypes.SessionType.READ_WRITE,
194
191
  publicKey: pubKey,
195
192
  expirationSeconds: authConfig.sessionLengthSeconds?.toString()
196
193
  });
@@ -230,44 +227,97 @@ const Auth = ({
230
227
  }
231
228
  setLoading(undefined);
232
229
  };
233
- const handleOAuthLogin = async (credential, providerName) => {
230
+ const handleOAuthLogin = async (credential, providerName, socialLinking = false) => {
234
231
  const pubKey = await indexedDbClient?.getPublicKey();
235
232
  if (!pubKey) {
236
233
  return;
237
234
  }
238
235
  setOauthLoading(providerName);
239
- const createSuborgData = {
240
- oauthProviders: [{
241
- providerName,
242
- oidcToken: credential
243
- }]
236
+ const oauthProviders = [{
237
+ providerName,
238
+ oidcToken: credential
239
+ }];
240
+ let createSuborgData = {
241
+ oauthProviders,
242
+ ...(customAccounts && {
243
+ customAccounts
244
+ })
244
245
  };
245
- if (customAccounts) {
246
- createSuborgData.customAccounts = customAccounts;
247
- }
248
- const resp = await sdkServer.server.getOrCreateSuborg({
249
- filterType: constants.FilterType.OidcToken,
250
- filterValue: credential,
251
- additionalData: createSuborgData
252
- });
253
- const suborgIds = resp?.subOrganizationIds;
254
- if (!suborgIds || suborgIds.length === 0) {
246
+ const completeLogin = async suborgID => {
247
+ const sessionResponse = await sdkServer.server.oauthLogin({
248
+ suborgID,
249
+ oidcToken: credential,
250
+ publicKey: pubKey,
251
+ sessionLengthSeconds: authConfig.sessionLengthSeconds
252
+ });
253
+ if (sessionResponse?.session) {
254
+ await indexedDbClient.loginWithSession(sessionResponse.session);
255
+ return onAuthSuccess();
256
+ }
255
257
  onError(constants.authErrors.oauth.loginFailed);
256
- return;
258
+ };
259
+ // Non-social-linking path: getOrCreateSuborg then login
260
+ if (!socialLinking) {
261
+ const resp = await sdkServer.server.getOrCreateSuborg({
262
+ filterType: constants.FilterType.OidcToken,
263
+ filterValue: credential,
264
+ additionalData: createSuborgData
265
+ });
266
+ const suborgIds = resp?.subOrganizationIds;
267
+ if (!suborgIds?.length) {
268
+ return onError(constants.authErrors.oauth.loginFailed);
269
+ }
270
+ return completeLogin(suborgIds[0]);
257
271
  }
258
- const suborgId = suborgIds[0];
259
- const sessionResponse = await sdkServer.server.oauthLogin({
260
- suborgID: suborgId,
261
- oidcToken: credential,
262
- publicKey: pubKey,
263
- sessionLengthSeconds: authConfig.sessionLengthSeconds
264
- });
265
- if (sessionResponse && sessionResponse.session) {
266
- await indexedDbClient.loginWithSession(sessionResponse.session);
267
- await onAuthSuccess();
268
- } else {
269
- onError(constants.authErrors.oauth.loginFailed);
272
+ // Social-linking path
273
+ // See if there's already a suborg tied to this token
274
+ const oidcOrgIds = (await sdkServer.server.getSuborgs({
275
+ filterType: constants.FilterType.OidcToken,
276
+ filterValue: credential
277
+ }))?.organizationIds;
278
+ let orgId = oidcOrgIds?.[0];
279
+ // If not, try to link by verified email (Google only)
280
+ if (!orgId) {
281
+ const {
282
+ email: oidcEmail,
283
+ iss
284
+ } = jwtDecode.jwtDecode(credential) || {};
285
+ createSuborgData = {
286
+ email: oidcEmail,
287
+ oauthProviders,
288
+ ...(customAccounts && {
289
+ customAccounts
290
+ })
291
+ };
292
+ if (oidcEmail && iss === "https://accounts.google.com") {
293
+ const emailOrgIds = (await sdkServer.server.getVerifiedSuborgs({
294
+ filterType: constants.FilterType.Email,
295
+ filterValue: oidcEmail
296
+ }))?.organizationIds;
297
+ if (emailOrgIds?.[0]) {
298
+ orgId = emailOrgIds[0];
299
+ // attach this new OIDC provider to the existing user
300
+ const userId = (await sdkServer.server.getUsers({
301
+ organizationId: orgId
302
+ }))?.users?.[0]?.userId;
303
+ if (!userId) {
304
+ return onError(constants.authErrors.oauth.loginFailed);
305
+ }
306
+ await sdkServer.server.createOauthProviders({
307
+ organizationId: orgId,
308
+ userId,
309
+ oauthProviders
310
+ });
311
+ } else {
312
+ // No orgs with the email. Create brand new suborg
313
+ orgId = (await sdkServer.server.createSuborg(createSuborgData))?.subOrganizationId;
314
+ }
315
+ } else {
316
+ // Non-Google or no email in OIDC. Create new suborg
317
+ orgId = (await sdkServer.server.createSuborg(createSuborgData))?.subOrganizationId;
318
+ }
270
319
  }
320
+ return orgId ? completeLogin(orgId) : onError(constants.authErrors.oauth.loginFailed);
271
321
  };
272
322
  const handleLoginWithWallet = async () => {
273
323
  setLoading("wallet");
@@ -300,7 +350,7 @@ const Auth = ({
300
350
  await indexedDbClient.resetKeyPair();
301
351
  const pubKey = await indexedDbClient.getPublicKey();
302
352
  await walletClient.loginWithWallet({
303
- sessionType: exports.SessionType.READ_WRITE,
353
+ sessionType: sdkTypes.SessionType.READ_WRITE,
304
354
  publicKey: pubKey,
305
355
  expirationSeconds: authConfig.sessionLengthSeconds?.toString()
306
356
  });
@@ -345,17 +395,17 @@ const Auth = ({
345
395
  layout: layout,
346
396
  clientId: authConfig.googleClientId ?? process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
347
397
  openInPage: authConfig.openOAuthInPage,
348
- onSuccess: response => handleOAuthLogin(response.idToken, "Google")
398
+ onSuccess: response => handleOAuthLogin(response.idToken, "Google", authConfig.socialLinking)
349
399
  }), appleEnabled && jsxRuntime.jsx(Apple.default, {
350
400
  layout: layout,
351
401
  clientId: authConfig.appleClientId ?? process.env.NEXT_PUBLIC_APPLE_CLIENT_ID,
352
402
  openInPage: authConfig.openOAuthInPage,
353
- onSuccess: response => handleOAuthLogin(response.idToken, "Apple")
403
+ onSuccess: response => handleOAuthLogin(response.idToken, "Apple") // No need to pass socialLinking for Apple. It's not supported
354
404
  }), facebookEnabled && jsxRuntime.jsx(Facebook.default, {
355
405
  layout: layout,
356
406
  clientId: authConfig.facebookClientId ?? process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID,
357
407
  openInPage: authConfig.openOAuthInPage,
358
- onSuccess: response => handleOAuthLogin(response.id_token, "Facebook")
408
+ onSuccess: response => handleOAuthLogin(response.id_token, "Facebook") // No need to pass socialLinking for Facebook. It's not supported
359
409
  })]
360
410
  });
361
411
  };