@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 +16 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +16 -5
- package/dist/index.mjs +16 -5
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/{types-Be6QFOjD.d.mts → types-B4z3Zton.d.mts} +9 -0
- package/dist/{types-Be6QFOjD.d.ts → types-B4z3Zton.d.ts} +9 -0
- package/package.json +1 -1
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) =>
|
|
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-
|
|
3
|
-
export { A as Address, b as AuthEvent, C as CustomField, P as ProfileField, c as WordKeyProfile } from './types-
|
|
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-
|
|
3
|
-
export { A as Address, b as AuthEvent, C as CustomField, P as ProfileField, c as WordKeyProfile } from './types-
|
|
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(
|
|
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)
|
|
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(
|
|
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)
|
|
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-
|
|
2
|
-
export { A as Address, a as WordKeyError, c as WordKeyProfile } from './types-
|
|
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-
|
|
2
|
-
export { A as Address, a as WordKeyError, c as WordKeyProfile } from './types-
|
|
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
|
}
|