@wordkey/sdk 1.0.0 → 1.0.1

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/README.md CHANGED
@@ -17,7 +17,16 @@ export function LoginPage() {
17
17
  return (
18
18
  <WordKeyButton
19
19
  publishableKey="wk_pub_xxxxxxxxxxxx"
20
- onSuccess={(user) => console.log("Authenticated:", user)}
20
+ onSuccess={async (user) => {
21
+ // user.token is a single-use proof of authentication. Hand it to
22
+ // YOUR backend and verify it there (step 2) before creating a
23
+ // session — never trust user.id straight from the browser.
24
+ await fetch("/api/wordkey-login", {
25
+ method: "POST",
26
+ headers: { "Content-Type": "application/json" },
27
+ body: JSON.stringify({ token: user.token }),
28
+ });
29
+ }}
21
30
  onError={(err) => console.error(err)}
22
31
  />
23
32
  );
@@ -64,6 +73,10 @@ SDK automatically falls back to a full-page redirect and resumes your
64
73
 
65
74
  ## 2. Verify server-side
66
75
 
76
+ The token your page received in `onSuccess` is single-use and business-bound:
77
+ your server exchanges it with WordKey exactly once, over an authenticated
78
+ server-to-server call. A replayed, expired, or foreign-business token throws.
79
+
67
80
  ```ts
68
81
  import { WordKey } from "@wordkey/sdk/server";
69
82
 
@@ -71,9 +84,10 @@ const wk = new WordKey({ secretKey: process.env.WORDKEY_SECRET_KEY! });
71
84
 
72
85
  export async function POST(req: Request) {
73
86
  const { token } = await req.json();
74
- const user = await wk.verify(token);
87
+ const user = await wk.verify(token); // throws if invalid, reused, or expired
75
88
  // user.id — permanent unique id for this WordKey user
76
89
  // user.isNewUser — true if this is their first authentication
90
+ // Safe to create your session for user.id now.
77
91
  }
78
92
  ```
79
93
 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { W as WordKeyUser, a as WordKeyError, R as RequestedField } from './types-Be6QFOjD.mjs';
3
- export { A as Address, b as AuthEvent, C as CustomField, P as ProfileField, c as WordKeyProfile } from './types-Be6QFOjD.mjs';
2
+ import { W as WordKeyUser, a as WordKeyError, R as RequestedField } from './types-B4z3Zton.mjs';
3
+ export { A as Address, b as AuthEvent, C as CustomField, P as ProfileField, c as WordKeyProfile } from './types-B4z3Zton.mjs';
4
4
 
5
5
  interface WordKeyButtonProps {
6
6
  publishableKey: string;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { W as WordKeyUser, a as WordKeyError, R as RequestedField } from './types-Be6QFOjD.js';
3
- export { A as Address, b as AuthEvent, C as CustomField, P as ProfileField, c as WordKeyProfile } from './types-Be6QFOjD.js';
2
+ import { W as WordKeyUser, a as WordKeyError, R as RequestedField } from './types-B4z3Zton.js';
3
+ export { A as Address, b as AuthEvent, C as CustomField, P as ProfileField, c as WordKeyProfile } from './types-B4z3Zton.js';
4
4
 
5
5
  interface WordKeyButtonProps {
6
6
  publishableKey: string;
package/dist/index.js CHANGED
@@ -247,6 +247,7 @@ async function beginRedirectShare(input2) {
247
247
  requestId: input2.requestId,
248
248
  userId: input2.userId,
249
249
  isNewUser: input2.isNewUser,
250
+ token: input2.token,
250
251
  fields: input2.fields,
251
252
  privateKeyJwk
252
253
  };
@@ -449,6 +450,7 @@ function WordKeyButton({
449
450
  id: r.pending.userId,
450
451
  isNewUser: r.pending.isNewUser,
451
452
  authenticatedAt: (/* @__PURE__ */ new Date()).toISOString(),
453
+ token: r.pending.token,
452
454
  profile
453
455
  });
454
456
  });
@@ -524,7 +526,12 @@ function AuthModal({
524
526
  setStep("authenticating");
525
527
  try {
526
528
  const result = await verifyWord(apiUrl, publishableKey, word);
527
- await completeAuth(result.userId, result.isNewUser, result.profileTicket);
529
+ await completeAuth(
530
+ result.userId,
531
+ result.isNewUser,
532
+ result.token,
533
+ result.profileTicket
534
+ );
528
535
  } catch (e) {
529
536
  if (e instanceof DeviceApprovalRequiredError) {
530
537
  await startDeviceApproval();
@@ -536,11 +543,12 @@ function AuthModal({
536
543
  onError({ code: "auth_failed", message });
537
544
  }
538
545
  }
539
- async function completeAuth(userId, isNewUser, profileTicket) {
546
+ async function completeAuth(userId, isNewUser, token, profileTicket) {
540
547
  const finish = (profile) => onSuccess({
541
548
  id: userId,
542
549
  isNewUser,
543
550
  authenticatedAt: (/* @__PURE__ */ new Date()).toISOString(),
551
+ token,
544
552
  profile
545
553
  });
546
554
  if (fields.length === 0) {
@@ -557,7 +565,8 @@ function AuthModal({
557
565
  ticket: profileTicket,
558
566
  fields,
559
567
  userId,
560
- isNewUser
568
+ isNewUser,
569
+ token
561
570
  });
562
571
  return;
563
572
  }
@@ -629,11 +638,12 @@ function AuthModal({
629
638
  const userId = claims?.userId ?? "";
630
639
  const isNewUser = claims?.isNewUser ?? false;
631
640
  if (fields.length === 0) {
632
- await completeAuth(userId, isNewUser, data.profileTicket);
641
+ await completeAuth(userId, isNewUser, data.token, data.profileTicket);
633
642
  } else {
634
643
  pendingApproval.current = {
635
644
  userId,
636
645
  isNewUser,
646
+ token: data.token,
637
647
  profileTicket: data.profileTicket
638
648
  };
639
649
  setStep("approved");
@@ -738,7 +748,8 @@ function AuthModal({
738
748
  style: primaryBtn(false),
739
749
  onClick: () => {
740
750
  const p = pendingApproval.current;
741
- if (p) void completeAuth(p.userId, p.isNewUser, p.profileTicket);
751
+ if (p)
752
+ void completeAuth(p.userId, p.isNewUser, p.token, p.profileTicket);
742
753
  },
743
754
  children: "Continue"
744
755
  }
package/dist/index.mjs CHANGED
@@ -207,6 +207,7 @@ async function beginRedirectShare(input2) {
207
207
  requestId: input2.requestId,
208
208
  userId: input2.userId,
209
209
  isNewUser: input2.isNewUser,
210
+ token: input2.token,
210
211
  fields: input2.fields,
211
212
  privateKeyJwk
212
213
  };
@@ -409,6 +410,7 @@ function WordKeyButton({
409
410
  id: r.pending.userId,
410
411
  isNewUser: r.pending.isNewUser,
411
412
  authenticatedAt: (/* @__PURE__ */ new Date()).toISOString(),
413
+ token: r.pending.token,
412
414
  profile
413
415
  });
414
416
  });
@@ -484,7 +486,12 @@ function AuthModal({
484
486
  setStep("authenticating");
485
487
  try {
486
488
  const result = await verifyWord(apiUrl, publishableKey, word);
487
- await completeAuth(result.userId, result.isNewUser, result.profileTicket);
489
+ await completeAuth(
490
+ result.userId,
491
+ result.isNewUser,
492
+ result.token,
493
+ result.profileTicket
494
+ );
488
495
  } catch (e) {
489
496
  if (e instanceof DeviceApprovalRequiredError) {
490
497
  await startDeviceApproval();
@@ -496,11 +503,12 @@ function AuthModal({
496
503
  onError({ code: "auth_failed", message });
497
504
  }
498
505
  }
499
- async function completeAuth(userId, isNewUser, profileTicket) {
506
+ async function completeAuth(userId, isNewUser, token, profileTicket) {
500
507
  const finish = (profile) => onSuccess({
501
508
  id: userId,
502
509
  isNewUser,
503
510
  authenticatedAt: (/* @__PURE__ */ new Date()).toISOString(),
511
+ token,
504
512
  profile
505
513
  });
506
514
  if (fields.length === 0) {
@@ -517,7 +525,8 @@ function AuthModal({
517
525
  ticket: profileTicket,
518
526
  fields,
519
527
  userId,
520
- isNewUser
528
+ isNewUser,
529
+ token
521
530
  });
522
531
  return;
523
532
  }
@@ -589,11 +598,12 @@ function AuthModal({
589
598
  const userId = claims?.userId ?? "";
590
599
  const isNewUser = claims?.isNewUser ?? false;
591
600
  if (fields.length === 0) {
592
- await completeAuth(userId, isNewUser, data.profileTicket);
601
+ await completeAuth(userId, isNewUser, data.token, data.profileTicket);
593
602
  } else {
594
603
  pendingApproval.current = {
595
604
  userId,
596
605
  isNewUser,
606
+ token: data.token,
597
607
  profileTicket: data.profileTicket
598
608
  };
599
609
  setStep("approved");
@@ -698,7 +708,8 @@ function AuthModal({
698
708
  style: primaryBtn(false),
699
709
  onClick: () => {
700
710
  const p = pendingApproval.current;
701
- if (p) void completeAuth(p.userId, p.isNewUser, p.profileTicket);
711
+ if (p)
712
+ void completeAuth(p.userId, p.isNewUser, p.token, p.profileTicket);
702
713
  },
703
714
  children: "Continue"
704
715
  }
package/dist/server.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { W as WordKeyUser, b as AuthEvent } from './types-Be6QFOjD.mjs';
2
- export { A as Address, a as WordKeyError, c as WordKeyProfile } from './types-Be6QFOjD.mjs';
1
+ import { W as WordKeyUser, b as AuthEvent } from './types-B4z3Zton.mjs';
2
+ export { A as Address, a as WordKeyError, c as WordKeyProfile } from './types-B4z3Zton.mjs';
3
3
 
4
4
  interface WordKeyConfig {
5
5
  /** Your SECRET key (wk_sec_...). Server-only — never ship it to a browser. */
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { W as WordKeyUser, b as AuthEvent } from './types-Be6QFOjD.js';
2
- export { A as Address, a as WordKeyError, c as WordKeyProfile } from './types-Be6QFOjD.js';
1
+ import { W as WordKeyUser, b as AuthEvent } from './types-B4z3Zton.js';
2
+ export { A as Address, a as WordKeyError, c as WordKeyProfile } from './types-B4z3Zton.js';
3
3
 
4
4
  interface WordKeyConfig {
5
5
  /** Your SECRET key (wk_sec_...). Server-only — never ship it to a browser. */
@@ -33,6 +33,15 @@ interface WordKeyUser {
33
33
  /** True on the user's first-ever authentication (i.e. account creation). */
34
34
  isNewUser: boolean;
35
35
  authenticatedAt: string;
36
+ /**
37
+ * Single-use verification token. Send it to your backend and call
38
+ * `wk.verify(token)` there — the id/isNewUser fields on this object are
39
+ * client-side claims and MUST NOT be trusted for anything security-relevant
40
+ * until your server has verified this token. Always present on users
41
+ * delivered by WordKeyButton onSuccess (SDK >= 1.0.1); absent on the result
42
+ * of `wk.verify` itself, which spends the token.
43
+ */
44
+ token?: string;
36
45
  /** Present only when requestedFields were granted; unfilled fields omitted. */
37
46
  profile?: WordKeyProfile;
38
47
  }
@@ -33,6 +33,15 @@ interface WordKeyUser {
33
33
  /** True on the user's first-ever authentication (i.e. account creation). */
34
34
  isNewUser: boolean;
35
35
  authenticatedAt: string;
36
+ /**
37
+ * Single-use verification token. Send it to your backend and call
38
+ * `wk.verify(token)` there — the id/isNewUser fields on this object are
39
+ * client-side claims and MUST NOT be trusted for anything security-relevant
40
+ * until your server has verified this token. Always present on users
41
+ * delivered by WordKeyButton onSuccess (SDK >= 1.0.1); absent on the result
42
+ * of `wk.verify` itself, which spends the token.
43
+ */
44
+ token?: string;
36
45
  /** Present only when requestedFields were granted; unfilled fields omitted. */
37
46
  profile?: WordKeyProfile;
38
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordkey/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Add the Word? button to your site in one afternoon. One word. Every door.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "main": "./dist/index.js",