@wordkey/sdk 1.0.0 → 1.1.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/README.md +72 -6
- package/dist/index.d.mts +41 -4
- package/dist/index.d.ts +41 -4
- package/dist/index.js +359 -66
- package/dist/index.mjs +356 -64
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/{types-Be6QFOjD.d.mts → types-WFjkfsy6.d.mts} +17 -1
- package/dist/{types-Be6QFOjD.d.ts → types-WFjkfsy6.d.ts} +17 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,16 +10,77 @@ npm install @wordkey/sdk
|
|
|
10
10
|
|
|
11
11
|
## 1. Add the button
|
|
12
12
|
|
|
13
|
+
Signup and login are different user moments, so the SDK gives you both:
|
|
14
|
+
|
|
15
|
+
- **`mode="signup"`** — *Get your word*: a brand-new user gets a word in
|
|
16
|
+
WordKey's onboarding window, this device is enrolled, and they come back
|
|
17
|
+
signed in to your site — one flow.
|
|
18
|
+
- **`mode="login"`** (default) — *Word?*: a returning user types the word
|
|
19
|
+
they already have.
|
|
20
|
+
|
|
21
|
+
Both deliver the identical `onSuccess` user, so your backend handles one
|
|
22
|
+
contract.
|
|
23
|
+
|
|
13
24
|
```jsx
|
|
14
25
|
import { WordKeyButton } from "@wordkey/sdk";
|
|
15
26
|
|
|
27
|
+
const handleUser = async (user) => {
|
|
28
|
+
// user.token is a single-use proof of authentication. Hand it to
|
|
29
|
+
// YOUR backend and verify it there (step 2) before creating a
|
|
30
|
+
// session — never trust user.id straight from the browser.
|
|
31
|
+
await fetch("/api/wordkey-login", {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "Content-Type": "application/json" },
|
|
34
|
+
body: JSON.stringify({ token: user.token }),
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
16
38
|
export function LoginPage() {
|
|
17
39
|
return (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
40
|
+
<>
|
|
41
|
+
<WordKeyButton
|
|
42
|
+
publishableKey="wk_pub_xxxxxxxxxxxx"
|
|
43
|
+
mode="signup"
|
|
44
|
+
onSuccess={handleUser}
|
|
45
|
+
/>
|
|
46
|
+
<WordKeyButton
|
|
47
|
+
publishableKey="wk_pub_xxxxxxxxxxxx"
|
|
48
|
+
onSuccess={handleUser}
|
|
49
|
+
onError={(err) => console.error(err)}
|
|
50
|
+
/>
|
|
51
|
+
</>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Use your own buttons (WordKeyModal)
|
|
57
|
+
|
|
58
|
+
`WordKeyButton` is sugar over the controlled `WordKeyModal`. Render the modal
|
|
59
|
+
once, unconditionally, and open either flow from any UI you like:
|
|
60
|
+
|
|
61
|
+
```jsx
|
|
62
|
+
import { WordKeyModal } from "@wordkey/sdk";
|
|
63
|
+
import { useState } from "react";
|
|
64
|
+
|
|
65
|
+
export function AuthButtons() {
|
|
66
|
+
const [flow, setFlow] = useState(null); // null | "signup" | "login"
|
|
67
|
+
return (
|
|
68
|
+
<>
|
|
69
|
+
<button className="cta" onClick={() => setFlow("signup")}>
|
|
70
|
+
Get Your Word
|
|
71
|
+
</button>
|
|
72
|
+
<a onClick={() => setFlow("login")}>Already have a word?</a>
|
|
73
|
+
|
|
74
|
+
{/* Always mounted — toggle `open`, don't conditionally render: the
|
|
75
|
+
modal also resumes the popup-blocked redirect fallback on mount. */}
|
|
76
|
+
<WordKeyModal
|
|
77
|
+
publishableKey="wk_pub_xxxxxxxxxxxx"
|
|
78
|
+
open={flow !== null}
|
|
79
|
+
mode={flow ?? "login"}
|
|
80
|
+
onClose={() => setFlow(null)}
|
|
81
|
+
onSuccess={handleUser}
|
|
82
|
+
/>
|
|
83
|
+
</>
|
|
23
84
|
);
|
|
24
85
|
}
|
|
25
86
|
```
|
|
@@ -64,6 +125,10 @@ SDK automatically falls back to a full-page redirect and resumes your
|
|
|
64
125
|
|
|
65
126
|
## 2. Verify server-side
|
|
66
127
|
|
|
128
|
+
The token your page received in `onSuccess` is single-use and business-bound:
|
|
129
|
+
your server exchanges it with WordKey exactly once, over an authenticated
|
|
130
|
+
server-to-server call. A replayed, expired, or foreign-business token throws.
|
|
131
|
+
|
|
67
132
|
```ts
|
|
68
133
|
import { WordKey } from "@wordkey/sdk/server";
|
|
69
134
|
|
|
@@ -71,9 +136,10 @@ const wk = new WordKey({ secretKey: process.env.WORDKEY_SECRET_KEY! });
|
|
|
71
136
|
|
|
72
137
|
export async function POST(req: Request) {
|
|
73
138
|
const { token } = await req.json();
|
|
74
|
-
const user = await wk.verify(token);
|
|
139
|
+
const user = await wk.verify(token); // throws if invalid, reused, or expired
|
|
75
140
|
// user.id — permanent unique id for this WordKey user
|
|
76
141
|
// user.isNewUser — true if this is their first authentication
|
|
142
|
+
// Safe to create your session for user.id now.
|
|
77
143
|
}
|
|
78
144
|
```
|
|
79
145
|
|
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,
|
|
2
|
+
import { W as WordKeyUser, a as WordKeyError, b as WordKeyMode, R as RequestedField } from './types-WFjkfsy6.mjs';
|
|
3
|
+
export { A as Address, c as AuthEvent, C as CustomField, P as ProfileField, d as WordKeyProfile } from './types-WFjkfsy6.mjs';
|
|
4
4
|
|
|
5
5
|
interface WordKeyButtonProps {
|
|
6
6
|
publishableKey: string;
|
|
@@ -9,11 +9,48 @@ interface WordKeyButtonProps {
|
|
|
9
9
|
theme?: "dark" | "light" | "auto";
|
|
10
10
|
size?: "sm" | "md" | "lg";
|
|
11
11
|
label?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Which flow this button opens. "login" (default, the 1.0.x behavior)
|
|
14
|
+
* renders the Word? mark for returning users; "signup" renders
|
|
15
|
+
* "Get your word" and opens WordKey onboarding for brand-new users.
|
|
16
|
+
* Render one button of each mode for the two user moments — or drive
|
|
17
|
+
* WordKeyModal yourself from any UI you like.
|
|
18
|
+
*/
|
|
19
|
+
mode?: WordKeyMode;
|
|
12
20
|
/** Profile fields to request. Standard names or custom field descriptors. */
|
|
13
21
|
requestedFields?: RequestedField[];
|
|
14
22
|
/** Override the protocol host (defaults to app.wordkey.app). */
|
|
15
23
|
apiUrl?: string;
|
|
16
24
|
}
|
|
17
|
-
declare function WordKeyButton({ publishableKey, onSuccess, onError, size, label, requestedFields, apiUrl, }: WordKeyButtonProps): react.JSX.Element;
|
|
25
|
+
declare function WordKeyButton({ publishableKey, onSuccess, onError, size, label, mode, requestedFields, apiUrl, }: WordKeyButtonProps): react.JSX.Element;
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The controlled WordKey flow. Render it ONCE, unconditionally (toggle `open`,
|
|
29
|
+
* don't conditionally mount it): the redirect-fallback return leg is consumed
|
|
30
|
+
* in this component's mount effect, and a conditionally rendered modal would
|
|
31
|
+
* miss it on the page load that carries the result fragment.
|
|
32
|
+
*
|
|
33
|
+
* mode "login" — the Word? flow for returning users (the 1.0.x behavior).
|
|
34
|
+
* mode "signup" — the Get-your-word flow: opens WordKey's first-party
|
|
35
|
+
* onboarding page in a popup, which assigns a word, enrolls this device, and
|
|
36
|
+
* hands back the same single-use token login yields — so `onSuccess` has ONE
|
|
37
|
+
* contract across both modes and every WordKeyButton stays a thin wrapper.
|
|
38
|
+
*/
|
|
39
|
+
interface WordKeyModalProps {
|
|
40
|
+
publishableKey: string;
|
|
41
|
+
/** Controlled visibility. The modal calls `onClose` when it dismisses itself
|
|
42
|
+
* (cancel, overlay click, or right before delivering `onSuccess`). */
|
|
43
|
+
open: boolean;
|
|
44
|
+
onClose: () => void;
|
|
45
|
+
onSuccess: (user: WordKeyUser) => void;
|
|
46
|
+
/** Which flow to open. Defaults to "login". */
|
|
47
|
+
mode?: WordKeyMode;
|
|
48
|
+
onError?: (error: WordKeyError) => void;
|
|
49
|
+
/** Profile fields to request. Standard names or custom field descriptors. */
|
|
50
|
+
requestedFields?: RequestedField[];
|
|
51
|
+
/** Override the protocol host (defaults to app.wordkey.app). */
|
|
52
|
+
apiUrl?: string;
|
|
53
|
+
}
|
|
54
|
+
declare function WordKeyModal({ publishableKey, open, onClose, onSuccess, mode, onError, requestedFields, apiUrl, }: WordKeyModalProps): react.JSX.Element | null;
|
|
55
|
+
|
|
56
|
+
export { RequestedField, WordKeyButton, type WordKeyButtonProps, WordKeyError, WordKeyModal, type WordKeyModalProps, WordKeyMode, WordKeyUser };
|
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,
|
|
2
|
+
import { W as WordKeyUser, a as WordKeyError, b as WordKeyMode, R as RequestedField } from './types-WFjkfsy6.js';
|
|
3
|
+
export { A as Address, c as AuthEvent, C as CustomField, P as ProfileField, d as WordKeyProfile } from './types-WFjkfsy6.js';
|
|
4
4
|
|
|
5
5
|
interface WordKeyButtonProps {
|
|
6
6
|
publishableKey: string;
|
|
@@ -9,11 +9,48 @@ interface WordKeyButtonProps {
|
|
|
9
9
|
theme?: "dark" | "light" | "auto";
|
|
10
10
|
size?: "sm" | "md" | "lg";
|
|
11
11
|
label?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Which flow this button opens. "login" (default, the 1.0.x behavior)
|
|
14
|
+
* renders the Word? mark for returning users; "signup" renders
|
|
15
|
+
* "Get your word" and opens WordKey onboarding for brand-new users.
|
|
16
|
+
* Render one button of each mode for the two user moments — or drive
|
|
17
|
+
* WordKeyModal yourself from any UI you like.
|
|
18
|
+
*/
|
|
19
|
+
mode?: WordKeyMode;
|
|
12
20
|
/** Profile fields to request. Standard names or custom field descriptors. */
|
|
13
21
|
requestedFields?: RequestedField[];
|
|
14
22
|
/** Override the protocol host (defaults to app.wordkey.app). */
|
|
15
23
|
apiUrl?: string;
|
|
16
24
|
}
|
|
17
|
-
declare function WordKeyButton({ publishableKey, onSuccess, onError, size, label, requestedFields, apiUrl, }: WordKeyButtonProps): react.JSX.Element;
|
|
25
|
+
declare function WordKeyButton({ publishableKey, onSuccess, onError, size, label, mode, requestedFields, apiUrl, }: WordKeyButtonProps): react.JSX.Element;
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The controlled WordKey flow. Render it ONCE, unconditionally (toggle `open`,
|
|
29
|
+
* don't conditionally mount it): the redirect-fallback return leg is consumed
|
|
30
|
+
* in this component's mount effect, and a conditionally rendered modal would
|
|
31
|
+
* miss it on the page load that carries the result fragment.
|
|
32
|
+
*
|
|
33
|
+
* mode "login" — the Word? flow for returning users (the 1.0.x behavior).
|
|
34
|
+
* mode "signup" — the Get-your-word flow: opens WordKey's first-party
|
|
35
|
+
* onboarding page in a popup, which assigns a word, enrolls this device, and
|
|
36
|
+
* hands back the same single-use token login yields — so `onSuccess` has ONE
|
|
37
|
+
* contract across both modes and every WordKeyButton stays a thin wrapper.
|
|
38
|
+
*/
|
|
39
|
+
interface WordKeyModalProps {
|
|
40
|
+
publishableKey: string;
|
|
41
|
+
/** Controlled visibility. The modal calls `onClose` when it dismisses itself
|
|
42
|
+
* (cancel, overlay click, or right before delivering `onSuccess`). */
|
|
43
|
+
open: boolean;
|
|
44
|
+
onClose: () => void;
|
|
45
|
+
onSuccess: (user: WordKeyUser) => void;
|
|
46
|
+
/** Which flow to open. Defaults to "login". */
|
|
47
|
+
mode?: WordKeyMode;
|
|
48
|
+
onError?: (error: WordKeyError) => void;
|
|
49
|
+
/** Profile fields to request. Standard names or custom field descriptors. */
|
|
50
|
+
requestedFields?: RequestedField[];
|
|
51
|
+
/** Override the protocol host (defaults to app.wordkey.app). */
|
|
52
|
+
apiUrl?: string;
|
|
53
|
+
}
|
|
54
|
+
declare function WordKeyModal({ publishableKey, open, onClose, onSuccess, mode, onError, requestedFields, apiUrl, }: WordKeyModalProps): react.JSX.Element | null;
|
|
55
|
+
|
|
56
|
+
export { RequestedField, WordKeyButton, type WordKeyButtonProps, WordKeyError, WordKeyModal, type WordKeyModalProps, WordKeyMode, WordKeyUser };
|