@authon/svelte 0.2.1 → 0.3.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.ko.md +141 -0
- package/README.md +417 -47
- package/dist/index.cjs +15 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.ko.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
[English](./README.md) | **한국어**
|
|
2
|
+
|
|
3
|
+
# @authon/svelte
|
|
4
|
+
|
|
5
|
+
[Authon](https://authon.dev)을 위한 Svelte SDK — store, 액션, 컴포넌트를 제공합니다.
|
|
6
|
+
|
|
7
|
+
## 설치
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @authon/svelte
|
|
11
|
+
# 또는
|
|
12
|
+
pnpm add @authon/svelte
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`svelte >= 4.0.0`이 필요합니다.
|
|
16
|
+
|
|
17
|
+
## 빠른 시작
|
|
18
|
+
|
|
19
|
+
### 1. 초기화
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// src/lib/authon.ts
|
|
23
|
+
import { initAuthon } from '@authon/svelte';
|
|
24
|
+
|
|
25
|
+
export const authon = initAuthon({
|
|
26
|
+
publishableKey: 'pk_live_...',
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. Store 사용
|
|
31
|
+
|
|
32
|
+
```svelte
|
|
33
|
+
<script>
|
|
34
|
+
import { user, isSignedIn, isLoading } from '@authon/svelte';
|
|
35
|
+
import { openSignIn, signOut } from '@authon/svelte';
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
{#if $isLoading}
|
|
39
|
+
<p>Loading...</p>
|
|
40
|
+
{:else if $isSignedIn}
|
|
41
|
+
<p>Welcome, {$user?.displayName}</p>
|
|
42
|
+
<button on:click={signOut}>Sign Out</button>
|
|
43
|
+
{:else}
|
|
44
|
+
<button on:click={openSignIn}>Sign In</button>
|
|
45
|
+
{/if}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 3. 컴포넌트 사용
|
|
49
|
+
|
|
50
|
+
```svelte
|
|
51
|
+
<script>
|
|
52
|
+
import { SignedIn, SignedOut, UserButton } from '@authon/svelte';
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<SignedIn>
|
|
56
|
+
<UserButton />
|
|
57
|
+
</SignedIn>
|
|
58
|
+
<SignedOut>
|
|
59
|
+
<button on:click={openSignIn}>Sign In</button>
|
|
60
|
+
</SignedOut>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## API 레퍼런스
|
|
64
|
+
|
|
65
|
+
### Store
|
|
66
|
+
|
|
67
|
+
| Store | 타입 | 설명 |
|
|
68
|
+
|-------|------|------|
|
|
69
|
+
| `user` | `Readable<AuthonUser \| null>` | 현재 사용자 |
|
|
70
|
+
| `isSignedIn` | `Readable<boolean>` | 로그인 여부 |
|
|
71
|
+
| `isLoading` | `Readable<boolean>` | 인증 상태 로딩 여부 |
|
|
72
|
+
|
|
73
|
+
### 액션
|
|
74
|
+
|
|
75
|
+
| 함수 | 반환값 | 설명 |
|
|
76
|
+
|------|--------|------|
|
|
77
|
+
| `openSignIn()` | `Promise<void>` | 로그인 모달 열기 |
|
|
78
|
+
| `openSignUp()` | `Promise<void>` | 회원가입 모달 열기 |
|
|
79
|
+
| `signOut()` | `Promise<void>` | 로그아웃 |
|
|
80
|
+
| `getToken()` | `string \| null` | 현재 액세스 토큰 반환 |
|
|
81
|
+
|
|
82
|
+
### 컴포넌트
|
|
83
|
+
|
|
84
|
+
| 컴포넌트 | 설명 |
|
|
85
|
+
|----------|------|
|
|
86
|
+
| `<SignedIn>` | 로그인 상태일 때만 슬롯 렌더링 |
|
|
87
|
+
| `<SignedOut>` | 로그아웃 상태일 때만 슬롯 렌더링 |
|
|
88
|
+
| `<UserButton>` | 로그아웃 기능이 포함된 아바타 드롭다운 |
|
|
89
|
+
|
|
90
|
+
## 다중 인증 (MFA)
|
|
91
|
+
|
|
92
|
+
Authon 클라이언트 인스턴스를 통해 MFA에 접근합니다.
|
|
93
|
+
|
|
94
|
+
```svelte
|
|
95
|
+
<script>
|
|
96
|
+
import { getAuthon } from '@authon/svelte';
|
|
97
|
+
import { AuthonMfaRequiredError } from '@authon/js';
|
|
98
|
+
|
|
99
|
+
const authon = getAuthon();
|
|
100
|
+
let qrSvg = '';
|
|
101
|
+
let mfaToken = '';
|
|
102
|
+
|
|
103
|
+
async function enableMfa() {
|
|
104
|
+
const setup = await authon.client.setupMfa();
|
|
105
|
+
qrSvg = setup.qrCodeSvg; // Display QR for authenticator app
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function verifySetup(code) {
|
|
109
|
+
await authon.client.verifyMfaSetup(code);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function signIn(email, password) {
|
|
113
|
+
try {
|
|
114
|
+
await authon.client.signInWithEmail(email, password);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
if (err instanceof AuthonMfaRequiredError) {
|
|
117
|
+
mfaToken = err.mfaToken; // Show TOTP input
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function verifyMfa(code) {
|
|
123
|
+
await authon.client.verifyMfa(mfaToken, code);
|
|
124
|
+
}
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
{#if qrSvg}
|
|
128
|
+
{@html qrSvg}
|
|
129
|
+
<p>Scan with your authenticator app</p>
|
|
130
|
+
{/if}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
전체 API 레퍼런스는 [`@authon/js` MFA 문서](../js/README.md#multi-factor-authentication-mfa)를 참고하세요.
|
|
134
|
+
|
|
135
|
+
## 문서
|
|
136
|
+
|
|
137
|
+
[authon.dev/docs](https://authon.dev/docs)
|
|
138
|
+
|
|
139
|
+
## 라이선스
|
|
140
|
+
|
|
141
|
+
[MIT](../../LICENSE)
|
package/README.md
CHANGED
|
@@ -1,89 +1,459 @@
|
|
|
1
|
+
**English** | [한국어](./README.ko.md)
|
|
2
|
+
|
|
1
3
|
# @authon/svelte
|
|
2
4
|
|
|
3
|
-
Svelte
|
|
5
|
+
Svelte integration for [Authon](https://authon.dev) — reactive stores, context-based setup, and social login buttons.
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
npm install @authon/svelte
|
|
9
|
-
# or
|
|
10
|
-
pnpm add @authon/svelte
|
|
10
|
+
npm install @authon/svelte @authon/js
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Requires `svelte >= 4.0.0`.
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Setup
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Initialize Authon in your root layout component and provide it to the component tree via Svelte context:
|
|
18
18
|
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
```svelte
|
|
20
|
+
<!-- src/routes/+layout.svelte -->
|
|
21
|
+
<script lang="ts">
|
|
22
|
+
import { initAuthon } from '@authon/svelte'
|
|
23
|
+
import { onDestroy } from 'svelte'
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
const authon = initAuthon('pk_live_...', {
|
|
26
|
+
theme: 'auto',
|
|
27
|
+
locale: 'en',
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
onDestroy(() => authon.destroy())
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<slot />
|
|
26
34
|
```
|
|
27
35
|
|
|
28
|
-
|
|
36
|
+
Then access the store in any child component:
|
|
29
37
|
|
|
30
38
|
```svelte
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
import {
|
|
39
|
+
<!-- src/routes/+page.svelte -->
|
|
40
|
+
<script lang="ts">
|
|
41
|
+
import { getAuthon } from '@authon/svelte'
|
|
42
|
+
|
|
43
|
+
const { user, isSignedIn, isLoading, openSignIn, signOut } = getAuthon()
|
|
34
44
|
</script>
|
|
35
45
|
|
|
36
46
|
{#if $isLoading}
|
|
37
47
|
<p>Loading...</p>
|
|
38
48
|
{:else if $isSignedIn}
|
|
39
49
|
<p>Welcome, {$user?.displayName}</p>
|
|
40
|
-
<button on:click={signOut}>Sign
|
|
50
|
+
<button on:click={signOut}>Sign out</button>
|
|
51
|
+
{:else}
|
|
52
|
+
<button on:click={openSignIn}>Sign in</button>
|
|
53
|
+
{/if}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## API Reference
|
|
57
|
+
|
|
58
|
+
### `initAuthon(publishableKey, config?)`
|
|
59
|
+
|
|
60
|
+
Creates an `AuthonStore` and registers it in Svelte context. Call this once in your root layout.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { initAuthon } from '@authon/svelte'
|
|
64
|
+
|
|
65
|
+
const authon = initAuthon('pk_live_...', {
|
|
66
|
+
theme: 'auto',
|
|
67
|
+
locale: 'en',
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `getAuthon()`
|
|
72
|
+
|
|
73
|
+
Retrieves the `AuthonStore` from Svelte context. Must be called within a component that is a descendant of the component where `initAuthon` was called.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { getAuthon } from '@authon/svelte'
|
|
77
|
+
|
|
78
|
+
const authon = getAuthon()
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `createAuthonStore(publishableKey, config?)`
|
|
82
|
+
|
|
83
|
+
Low-level store factory. Use `initAuthon` / `getAuthon` for most cases; use this directly if you need a store without Svelte context.
|
|
84
|
+
|
|
85
|
+
### `AuthonStore`
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
interface AuthonStore {
|
|
89
|
+
user: Readable<AuthonUser | null>
|
|
90
|
+
isSignedIn: Readable<boolean>
|
|
91
|
+
isLoading: Readable<boolean>
|
|
92
|
+
client: Authon // @authon/js Authon instance
|
|
93
|
+
signOut: () => Promise<void>
|
|
94
|
+
openSignIn: () => Promise<void>
|
|
95
|
+
openSignUp: () => Promise<void>
|
|
96
|
+
getToken: () => string | null
|
|
97
|
+
destroy: () => void
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
All store values are Svelte `Readable` stores — subscribe with the `$` prefix in templates.
|
|
102
|
+
|
|
103
|
+
### `renderSocialButtons(options)`
|
|
104
|
+
|
|
105
|
+
Renders branded OAuth provider buttons into a DOM element. Returns a cleanup function.
|
|
106
|
+
|
|
107
|
+
**Options:**
|
|
108
|
+
|
|
109
|
+
| Option | Type | Default | Description |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| `client` | `Authon` | required | Authon client instance |
|
|
112
|
+
| `container` | `HTMLElement` | required | Target DOM element |
|
|
113
|
+
| `onSuccess` | `() => void` | — | Called after successful OAuth sign-in |
|
|
114
|
+
| `onError` | `(error: Error) => void` | — | Called on OAuth error |
|
|
115
|
+
| `compact` | `boolean` | `false` | Icon-only square buttons in a row |
|
|
116
|
+
| `gap` | `number` | `10` / `12` | Gap between buttons in px |
|
|
117
|
+
| `labels` | `Record<provider, string>` | — | Override button labels per provider |
|
|
118
|
+
| `borderRadius` | `number` | `10` | Button border radius in px |
|
|
119
|
+
| `height` | `number` | `48` | Button height in px |
|
|
120
|
+
| `size` | `number` | `48` | Icon button size in px (compact mode) |
|
|
121
|
+
|
|
122
|
+
## Examples
|
|
123
|
+
|
|
124
|
+
### Basic auth state
|
|
125
|
+
|
|
126
|
+
```svelte
|
|
127
|
+
<script lang="ts">
|
|
128
|
+
import { getAuthon } from '@authon/svelte'
|
|
129
|
+
|
|
130
|
+
const { user, isSignedIn, isLoading, openSignIn, signOut } = getAuthon()
|
|
131
|
+
</script>
|
|
132
|
+
|
|
133
|
+
{#if $isLoading}
|
|
134
|
+
<p>Loading...</p>
|
|
135
|
+
{:else if $isSignedIn}
|
|
136
|
+
<p>Hello, {$user?.displayName ?? $user?.email}</p>
|
|
137
|
+
<button on:click={signOut}>Sign out</button>
|
|
41
138
|
{:else}
|
|
42
|
-
<button on:click={openSignIn}>Sign
|
|
139
|
+
<button on:click={openSignIn}>Sign in</button>
|
|
43
140
|
{/if}
|
|
44
141
|
```
|
|
45
142
|
|
|
46
|
-
###
|
|
143
|
+
### Email + password sign-in
|
|
47
144
|
|
|
48
145
|
```svelte
|
|
49
|
-
<script>
|
|
50
|
-
import {
|
|
146
|
+
<script lang="ts">
|
|
147
|
+
import { getAuthon } from '@authon/svelte'
|
|
148
|
+
|
|
149
|
+
const { client } = getAuthon()
|
|
150
|
+
|
|
151
|
+
let email = ''
|
|
152
|
+
let password = ''
|
|
153
|
+
let loading = false
|
|
154
|
+
let error = ''
|
|
155
|
+
|
|
156
|
+
async function handleSignIn() {
|
|
157
|
+
loading = true
|
|
158
|
+
error = ''
|
|
159
|
+
try {
|
|
160
|
+
await client.signInWithEmail(email, password)
|
|
161
|
+
} catch (e: any) {
|
|
162
|
+
error = e.message
|
|
163
|
+
} finally {
|
|
164
|
+
loading = false
|
|
165
|
+
}
|
|
166
|
+
}
|
|
51
167
|
</script>
|
|
52
168
|
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
<
|
|
58
|
-
</
|
|
169
|
+
<form on:submit|preventDefault={handleSignIn}>
|
|
170
|
+
<input bind:value={email} type="email" placeholder="Email" />
|
|
171
|
+
<input bind:value={password} type="password" placeholder="Password" />
|
|
172
|
+
<button type="submit" disabled={loading}>Sign in</button>
|
|
173
|
+
{#if error}<p>{error}</p>{/if}
|
|
174
|
+
</form>
|
|
59
175
|
```
|
|
60
176
|
|
|
61
|
-
|
|
177
|
+
### OAuth sign-in
|
|
178
|
+
|
|
179
|
+
```svelte
|
|
180
|
+
<script lang="ts">
|
|
181
|
+
import { getAuthon, renderSocialButtons } from '@authon/svelte'
|
|
182
|
+
import { onMount, onDestroy } from 'svelte'
|
|
183
|
+
|
|
184
|
+
const { client } = getAuthon()
|
|
185
|
+
let container: HTMLElement
|
|
186
|
+
let cleanup: (() => void) | undefined
|
|
187
|
+
|
|
188
|
+
onMount(() => {
|
|
189
|
+
cleanup = renderSocialButtons({
|
|
190
|
+
client,
|
|
191
|
+
container,
|
|
192
|
+
onSuccess: () => window.location.href = '/dashboard',
|
|
193
|
+
onError: (e) => console.error(e),
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
onDestroy(() => cleanup?.())
|
|
198
|
+
|
|
199
|
+
// Or trigger a single provider directly
|
|
200
|
+
async function signInWithGoogle() {
|
|
201
|
+
await client.signInWithOAuth('google')
|
|
202
|
+
}
|
|
203
|
+
</script>
|
|
204
|
+
|
|
205
|
+
<div bind:this={container} />
|
|
206
|
+
<button on:click={signInWithGoogle}>Sign in with Google</button>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### MFA setup
|
|
210
|
+
|
|
211
|
+
```svelte
|
|
212
|
+
<script lang="ts">
|
|
213
|
+
import { getAuthon } from '@authon/svelte'
|
|
214
|
+
|
|
215
|
+
const { client } = getAuthon()
|
|
216
|
+
|
|
217
|
+
let qrCodeSvg = ''
|
|
218
|
+
let secret = ''
|
|
219
|
+
let backupCodes: string[] = []
|
|
220
|
+
let verifyCode = ''
|
|
221
|
+
|
|
222
|
+
async function initMfaSetup() {
|
|
223
|
+
const res = await client.setupMfa()
|
|
224
|
+
qrCodeSvg = res.qrCodeSvg // SVG string for display
|
|
225
|
+
secret = res.secret
|
|
226
|
+
backupCodes = res.backupCodes
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function confirmSetup() {
|
|
230
|
+
await client.verifyMfaSetup(verifyCode)
|
|
231
|
+
alert('MFA enabled')
|
|
232
|
+
}
|
|
233
|
+
</script>
|
|
234
|
+
|
|
235
|
+
<button on:click={initMfaSetup}>Enable MFA</button>
|
|
236
|
+
|
|
237
|
+
{#if qrCodeSvg}
|
|
238
|
+
{@html qrCodeSvg}
|
|
239
|
+
<p>Scan with your authenticator app</p>
|
|
240
|
+
<p>Secret: {secret}</p>
|
|
241
|
+
<ul>{#each backupCodes as code}<li>{code}</li>{/each}</ul>
|
|
242
|
+
<input bind:value={verifyCode} placeholder="6-digit code" />
|
|
243
|
+
<button on:click={confirmSetup}>Verify</button>
|
|
244
|
+
{/if}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### MFA verification on sign-in
|
|
248
|
+
|
|
249
|
+
```svelte
|
|
250
|
+
<script lang="ts">
|
|
251
|
+
import { getAuthon } from '@authon/svelte'
|
|
252
|
+
import { AuthonMfaRequiredError } from '@authon/js'
|
|
253
|
+
|
|
254
|
+
const { client } = getAuthon()
|
|
255
|
+
|
|
256
|
+
let mfaToken = ''
|
|
257
|
+
let totpCode = ''
|
|
258
|
+
|
|
259
|
+
async function signIn(email: string, password: string) {
|
|
260
|
+
try {
|
|
261
|
+
await client.signInWithEmail(email, password)
|
|
262
|
+
} catch (e) {
|
|
263
|
+
if (e instanceof AuthonMfaRequiredError) {
|
|
264
|
+
mfaToken = e.mfaToken // show TOTP input
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function verifyMfa() {
|
|
270
|
+
await client.verifyMfa(mfaToken, totpCode)
|
|
271
|
+
}
|
|
272
|
+
</script>
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Passwordless — magic link
|
|
276
|
+
|
|
277
|
+
```svelte
|
|
278
|
+
<script lang="ts">
|
|
279
|
+
import { getAuthon } from '@authon/svelte'
|
|
280
|
+
|
|
281
|
+
const { client } = getAuthon()
|
|
282
|
+
let email = ''
|
|
283
|
+
let sent = false
|
|
284
|
+
|
|
285
|
+
async function sendMagicLink() {
|
|
286
|
+
await client.sendMagicLink(email)
|
|
287
|
+
sent = true
|
|
288
|
+
}
|
|
289
|
+
</script>
|
|
290
|
+
|
|
291
|
+
{#if sent}
|
|
292
|
+
<p>Check your inbox for a sign-in link.</p>
|
|
293
|
+
{:else}
|
|
294
|
+
<input bind:value={email} type="email" placeholder="Email" />
|
|
295
|
+
<button on:click={sendMagicLink}>Send magic link</button>
|
|
296
|
+
{/if}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Passwordless — email OTP
|
|
300
|
+
|
|
301
|
+
```svelte
|
|
302
|
+
<script lang="ts">
|
|
303
|
+
import { getAuthon } from '@authon/svelte'
|
|
304
|
+
|
|
305
|
+
const { client } = getAuthon()
|
|
306
|
+
let email = ''
|
|
307
|
+
let otp = ''
|
|
308
|
+
let step: 'email' | 'verify' = 'email'
|
|
309
|
+
|
|
310
|
+
async function sendOtp() {
|
|
311
|
+
await client.sendEmailOtp(email)
|
|
312
|
+
step = 'verify'
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function verifyOtp() {
|
|
316
|
+
const user = await client.verifyPasswordless({ email, code: otp })
|
|
317
|
+
console.log('Signed in as:', user.email)
|
|
318
|
+
}
|
|
319
|
+
</script>
|
|
320
|
+
|
|
321
|
+
{#if step === 'email'}
|
|
322
|
+
<input bind:value={email} type="email" placeholder="Email" />
|
|
323
|
+
<button on:click={sendOtp}>Send code</button>
|
|
324
|
+
{:else}
|
|
325
|
+
<input bind:value={otp} placeholder="6-digit code" />
|
|
326
|
+
<button on:click={verifyOtp}>Verify</button>
|
|
327
|
+
{/if}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Passkeys
|
|
331
|
+
|
|
332
|
+
```svelte
|
|
333
|
+
<script lang="ts">
|
|
334
|
+
import { getAuthon } from '@authon/svelte'
|
|
335
|
+
|
|
336
|
+
const { client } = getAuthon()
|
|
337
|
+
|
|
338
|
+
// Register (user must be signed in)
|
|
339
|
+
async function registerPasskey() {
|
|
340
|
+
const credential = await client.registerPasskey('My Device')
|
|
341
|
+
console.log('Registered:', credential.id)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Authenticate
|
|
345
|
+
async function loginWithPasskey() {
|
|
346
|
+
const user = await client.authenticateWithPasskey()
|
|
347
|
+
console.log('Signed in as:', user.email)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// List registered passkeys
|
|
351
|
+
async function listPasskeys() {
|
|
352
|
+
const keys = await client.listPasskeys()
|
|
353
|
+
console.log(keys)
|
|
354
|
+
}
|
|
355
|
+
</script>
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Web3 wallet authentication
|
|
359
|
+
|
|
360
|
+
```svelte
|
|
361
|
+
<script lang="ts">
|
|
362
|
+
import { getAuthon } from '@authon/svelte'
|
|
363
|
+
|
|
364
|
+
const { client } = getAuthon()
|
|
365
|
+
|
|
366
|
+
async function signInWithWallet() {
|
|
367
|
+
const address = '0xYourWalletAddress'
|
|
368
|
+
|
|
369
|
+
// 1. Get a nonce + signable message from Authon
|
|
370
|
+
const { nonce, message } = await client.web3GetNonce(address, 'evm', 'metamask')
|
|
371
|
+
|
|
372
|
+
// 2. Sign the message with the wallet
|
|
373
|
+
const signature = await window.ethereum.request({
|
|
374
|
+
method: 'personal_sign',
|
|
375
|
+
params: [message, address],
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
// 3. Verify the signature and sign in
|
|
379
|
+
const user = await client.web3Verify(message, signature, address, 'evm', 'metamask')
|
|
380
|
+
console.log('Signed in as:', user.email)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function listLinkedWallets() {
|
|
384
|
+
const wallets = await client.listWallets()
|
|
385
|
+
console.log(wallets)
|
|
386
|
+
}
|
|
387
|
+
</script>
|
|
388
|
+
```
|
|
62
389
|
|
|
63
|
-
###
|
|
390
|
+
### Profile update
|
|
64
391
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
| `isSignedIn` | `Readable<boolean>` | Whether the user is signed in |
|
|
69
|
-
| `isLoading` | `Readable<boolean>` | Whether auth state is loading |
|
|
392
|
+
```svelte
|
|
393
|
+
<script lang="ts">
|
|
394
|
+
import { getAuthon } from '@authon/svelte'
|
|
70
395
|
|
|
71
|
-
|
|
396
|
+
const { client } = getAuthon()
|
|
72
397
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
398
|
+
async function saveProfile() {
|
|
399
|
+
const updated = await client.updateProfile({
|
|
400
|
+
displayName: 'Jane Doe',
|
|
401
|
+
avatarUrl: 'https://example.com/avatar.png',
|
|
402
|
+
phone: '+1234567890',
|
|
403
|
+
publicMetadata: { role: 'admin' },
|
|
404
|
+
})
|
|
405
|
+
console.log('Updated user:', updated)
|
|
406
|
+
}
|
|
407
|
+
</script>
|
|
408
|
+
```
|
|
79
409
|
|
|
80
|
-
###
|
|
410
|
+
### Session management
|
|
81
411
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
412
|
+
```svelte
|
|
413
|
+
<script lang="ts">
|
|
414
|
+
import { getAuthon } from '@authon/svelte'
|
|
415
|
+
import { onMount } from 'svelte'
|
|
416
|
+
import type { SessionInfo } from '@authon/shared'
|
|
417
|
+
|
|
418
|
+
const { client } = getAuthon()
|
|
419
|
+
let sessions: SessionInfo[] = []
|
|
420
|
+
|
|
421
|
+
onMount(async () => {
|
|
422
|
+
sessions = await client.listSessions()
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
async function revokeSession(sessionId: string) {
|
|
426
|
+
await client.revokeSession(sessionId)
|
|
427
|
+
sessions = sessions.filter(s => s.id !== sessionId)
|
|
428
|
+
}
|
|
429
|
+
</script>
|
|
430
|
+
|
|
431
|
+
<ul>
|
|
432
|
+
{#each sessions as session (session.id)}
|
|
433
|
+
<li>
|
|
434
|
+
{session.userAgent} — {session.createdAt}
|
|
435
|
+
<button on:click={() => revokeSession(session.id)}>Revoke</button>
|
|
436
|
+
</li>
|
|
437
|
+
{/each}
|
|
438
|
+
</ul>
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
## Store options
|
|
442
|
+
|
|
443
|
+
| Option | Type | Default | Description |
|
|
444
|
+
|---|---|---|---|
|
|
445
|
+
| `publishableKey` | `string` | required | Your Authon publishable key |
|
|
446
|
+
| `config.theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | UI theme |
|
|
447
|
+
| `config.locale` | `string` | `'en'` | Language code |
|
|
448
|
+
| `config.apiUrl` | `string` | `'https://api.authon.dev'` | Custom API base URL |
|
|
449
|
+
| `config.appearance` | `Partial<BrandingConfig>` | — | Override branding colors and logo |
|
|
450
|
+
|
|
451
|
+
## TypeScript
|
|
452
|
+
|
|
453
|
+
```ts
|
|
454
|
+
import type { AuthonStore, SocialButtonsOptions } from '@authon/svelte'
|
|
455
|
+
import type { AuthonUser, SessionInfo, PasskeyCredential, Web3Wallet } from '@authon/shared'
|
|
456
|
+
```
|
|
87
457
|
|
|
88
458
|
## Documentation
|
|
89
459
|
|
package/dist/index.cjs
CHANGED
|
@@ -62,7 +62,21 @@ function createAuthonStore(publishableKey, config) {
|
|
|
62
62
|
openSignUp: () => client.openSignUp(),
|
|
63
63
|
getToken: () => client.getToken(),
|
|
64
64
|
destroy: () => client.destroy(),
|
|
65
|
-
client
|
|
65
|
+
client,
|
|
66
|
+
// Web3
|
|
67
|
+
web3GetNonce: (address, chain, walletType, chainId) => client.web3GetNonce(address, chain, walletType, chainId),
|
|
68
|
+
web3Verify: (message, signature, address, chain, walletType) => client.web3Verify(message, signature, address, chain, walletType),
|
|
69
|
+
web3LinkWallet: (params) => client.linkWallet(params),
|
|
70
|
+
web3UnlinkWallet: (walletId) => client.unlinkWallet(walletId),
|
|
71
|
+
web3GetWallets: () => client.listWallets(),
|
|
72
|
+
// Passwordless
|
|
73
|
+
passwordlessSendCode: (email, type = "otp") => type === "magic-link" ? client.sendMagicLink(email) : client.sendEmailOtp(email),
|
|
74
|
+
passwordlessVerifyCode: (email, code) => client.verifyPasswordless({ email, code }),
|
|
75
|
+
// Passkeys
|
|
76
|
+
passkeyRegister: (name) => client.registerPasskey(name),
|
|
77
|
+
passkeyAuthenticate: (email) => client.authenticateWithPasskey(email),
|
|
78
|
+
passkeyList: () => client.listPasskeys(),
|
|
79
|
+
passkeyDelete: (credentialId) => client.revokePasskey(credentialId)
|
|
66
80
|
};
|
|
67
81
|
}
|
|
68
82
|
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/store.ts","../src/context.ts","../src/SocialButtons.ts"],"sourcesContent":["export { createAuthonStore } from './store';\nexport type { AuthonStore } from './store';\nexport { initAuthon, getAuthon } from './context';\nexport { renderSocialButtons } from './SocialButtons';\nexport type { SocialButtonsOptions } from './SocialButtons';\n","import { writable, derived, type Readable } from 'svelte/store';\nimport { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type { AuthonUser } from '@authon/shared';\n\nexport interface AuthonStore {\n user: Readable<AuthonUser | null>;\n isSignedIn: Readable<boolean>;\n isLoading: Readable<boolean>;\n signOut: () => Promise<void>;\n openSignIn: () => Promise<void>;\n openSignUp: () => Promise<void>;\n getToken: () => string | null;\n destroy: () => void;\n client: Authon;\n}\n\n/**\n * Creates an Authon store with reactive Svelte stores.\n *\n * Usage:\n * ```ts\n * import { createAuthonStore } from '@authon/svelte'\n *\n * const authon = createAuthonStore('pk_live_...')\n *\n * // In your component:\n * $: user = $authon.user\n * $: isSignedIn = $authon.isSignedIn\n * ```\n */\nexport function createAuthonStore(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const client = new Authon(publishableKey, config);\n const userStore = writable<AuthonUser | null>(null);\n const isLoadingStore = writable(true);\n\n const isSignedIn = derived(userStore, ($user) => $user !== null);\n\n client.on('signedIn', (user) => {\n userStore.set(user as AuthonUser);\n isLoadingStore.set(false);\n });\n\n client.on('signedOut', () => {\n userStore.set(null);\n });\n\n client.on('error', () => {\n isLoadingStore.set(false);\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n userStore.set(existingUser);\n }\n isLoadingStore.set(false);\n\n return {\n user: { subscribe: userStore.subscribe },\n isSignedIn,\n isLoading: { subscribe: isLoadingStore.subscribe },\n signOut: async () => {\n await client.signOut();\n userStore.set(null);\n },\n openSignIn: () => client.openSignIn(),\n openSignUp: () => client.openSignUp(),\n getToken: () => client.getToken(),\n destroy: () => client.destroy(),\n client,\n };\n}\n","import { setContext, getContext } from 'svelte';\nimport type { AuthonConfig } from '@authon/js';\nimport { createAuthonStore, type AuthonStore } from './store';\n\nconst AUTHON_CONTEXT_KEY = Symbol('authon');\n\n/**\n * Initialize Authon in a Svelte component tree.\n * Call this in your root layout or top-level component.\n *\n * Usage in +layout.svelte:\n * ```svelte\n * <script>\n * import { initAuthon } from '@authon/svelte'\n * const authon = initAuthon('pk_live_...')\n * </script>\n * ```\n */\nexport function initAuthon(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const store = createAuthonStore(publishableKey, config);\n setContext(AUTHON_CONTEXT_KEY, store);\n return store;\n}\n\n/**\n * Get the Authon store from Svelte context.\n * Must be called within a component tree where `initAuthon` was called.\n *\n * Usage:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * const { user, isSignedIn, signOut } = getAuthon()\n * </script>\n * ```\n */\nexport function getAuthon(): AuthonStore {\n const store = getContext<AuthonStore | undefined>(AUTHON_CONTEXT_KEY);\n if (!store) {\n throw new Error('getAuthon() must be called within a component tree where initAuthon() was called.');\n }\n return store;\n}\n","import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES, type OAuthProviderType } from '@authon/shared';\nimport { getProviderButtonConfig, type Authon } from '@authon/js';\n\nexport interface SocialButtonsOptions {\n /** Authon client instance */\n client: Authon;\n /** Target container element */\n container: HTMLElement;\n /** Called after successful OAuth sign-in */\n onSuccess?: () => void;\n /** Called on OAuth error */\n onError?: (error: Error) => void;\n /** Compact mode — icon-only square buttons in a row (default: false) */\n compact?: boolean;\n /** Gap between buttons in px (default: 10, compact default: 12) */\n gap?: number;\n /** Custom labels per provider */\n labels?: Partial<Record<OAuthProviderType, string>>;\n /** Icon size (default: 20, compact default: 24) */\n iconSize?: number;\n /** Border radius in px (default: 10) */\n borderRadius?: number;\n /** Button height in px (default: 48) */\n height?: number;\n /** Button size for compact mode in px (default: 48) */\n size?: number;\n}\n\n/**\n * Render social login buttons into a container element.\n *\n * Usage in +page.svelte:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * import { renderSocialButtons } from '@authon/svelte'\n * import { onMount } from 'svelte'\n *\n * const { client } = getAuthon()\n * let container: HTMLElement\n *\n * onMount(() => {\n * const cleanup = renderSocialButtons({\n * client,\n * container,\n * compact: true,\n * onError: (err) => console.error(err),\n * })\n * return cleanup\n * })\n * </script>\n *\n * <div bind:this={container}></div>\n * ```\n */\nexport function renderSocialButtons(options: SocialButtonsOptions): () => void {\n const {\n client,\n container,\n onSuccess,\n onError,\n compact = false,\n gap,\n labels,\n iconSize,\n borderRadius = 10,\n height = 48,\n size = 48,\n } = options;\n\n const resolvedGap = gap ?? (compact ? 12 : 10);\n const resolvedIconSize = iconSize ?? (compact ? 24 : 20);\n let loadingProvider: string | null = null;\n let buttons: HTMLButtonElement[] = [];\n\n const handleClick = async (provider: OAuthProviderType, btn: HTMLButtonElement) => {\n if (loadingProvider) return;\n loadingProvider = provider;\n btn.innerHTML = '<span style=\"display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite\"></span>';\n buttons.forEach((b) => (b.disabled = true));\n\n try {\n await client.signInWithOAuth(provider);\n onSuccess?.();\n } catch (e: any) {\n const error = e instanceof Error ? e : new Error(String(e));\n onError?.(error);\n } finally {\n loadingProvider = null;\n renderButtons(providers);\n }\n };\n\n let providers: OAuthProviderType[] = [];\n\n function renderButtons(providerList: OAuthProviderType[]) {\n container.innerHTML = '';\n buttons = [];\n\n // Inject keyframe if not present\n if (!document.getElementById('authon-spin-style')) {\n const style = document.createElement('style');\n style.id = 'authon-spin-style';\n style.textContent = '@keyframes authon-spin{to{transform:rotate(360deg)}}';\n document.head.appendChild(style);\n }\n\n const wrapper = document.createElement('div');\n wrapper.style.display = 'flex';\n wrapper.style.gap = `${resolvedGap}px`;\n\n if (compact) {\n wrapper.style.flexDirection = 'row';\n wrapper.style.flexWrap = 'wrap';\n wrapper.style.justifyContent = 'center';\n } else {\n wrapper.style.flexDirection = 'column';\n }\n\n for (const provider of providerList) {\n const colors = PROVIDER_COLORS[provider] || { bg: '#333', text: '#fff' };\n const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;\n const config = getProviderButtonConfig(provider);\n const iconSvg = config.iconSvg\n .replace(/width=\"\\d+\"/, `width=\"${resolvedIconSize}\"`)\n .replace(/height=\"\\d+\"/, `height=\"${resolvedIconSize}\"`);\n const needsBorder = colors.bg.toLowerCase() === '#ffffff';\n\n const btn = document.createElement('button');\n btn.setAttribute('aria-label', `Sign in with ${displayName}`);\n btn.style.display = 'flex';\n btn.style.alignItems = 'center';\n btn.style.justifyContent = 'center';\n btn.style.border = needsBorder ? '1px solid #dadce0' : 'none';\n btn.style.cursor = 'pointer';\n btn.style.backgroundColor = colors.bg;\n btn.style.color = colors.text;\n btn.style.borderRadius = `${borderRadius}px`;\n btn.style.transition = 'opacity 0.15s';\n btn.style.fontFamily = 'inherit';\n\n if (compact) {\n btn.style.width = `${size}px`;\n btn.style.height = `${size}px`;\n btn.style.padding = '0';\n btn.innerHTML = `<span style=\"display:flex;align-items:center\">${iconSvg}</span>`;\n } else {\n btn.style.width = '100%';\n btn.style.height = `${height}px`;\n btn.style.gap = '10px';\n btn.style.paddingLeft = '16px';\n btn.style.paddingRight = '16px';\n const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;\n btn.innerHTML = `<span style=\"display:flex;align-items:center;flex-shrink:0\">${iconSvg}</span><span style=\"font-size:15px;font-weight:600;white-space:nowrap\">${buttonLabel}</span>`;\n }\n\n btn.addEventListener('click', () => handleClick(provider, btn));\n btn.addEventListener('mouseenter', () => (btn.style.opacity = '0.85'));\n btn.addEventListener('mouseleave', () => (btn.style.opacity = '1'));\n\n buttons.push(btn);\n wrapper.appendChild(btn);\n }\n\n container.appendChild(wrapper);\n }\n\n // Init\n client.getProviders().then((p: OAuthProviderType[]) => {\n providers = p;\n if (providers.length > 0) {\n renderButtons(providers);\n }\n });\n\n // Cleanup\n return () => {\n container.innerHTML = '';\n buttons = [];\n };\n}\n"],"mappings":"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","names":["import_js"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/store.ts","../src/context.ts","../src/SocialButtons.ts"],"sourcesContent":["export { createAuthonStore } from './store';\nexport type { AuthonStore } from './store';\nexport { initAuthon, getAuthon } from './context';\nexport { renderSocialButtons } from './SocialButtons';\nexport type { SocialButtonsOptions } from './SocialButtons';\nexport type {\n PasskeyCredential,\n Web3Chain,\n Web3NonceResponse,\n Web3Wallet,\n Web3WalletType,\n} from '@authon/shared';\n","import { writable, derived, type Readable } from 'svelte/store';\nimport { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type {\n AuthonUser,\n PasskeyCredential,\n Web3Chain,\n Web3NonceResponse,\n Web3Wallet,\n Web3WalletType,\n} from '@authon/shared';\n\nexport interface AuthonStore {\n user: Readable<AuthonUser | null>;\n isSignedIn: Readable<boolean>;\n isLoading: Readable<boolean>;\n signOut: () => Promise<void>;\n openSignIn: () => Promise<void>;\n openSignUp: () => Promise<void>;\n getToken: () => string | null;\n destroy: () => void;\n client: Authon;\n // Web3\n web3GetNonce: (address: string, chain: Web3Chain, walletType: Web3WalletType, chainId?: number) => Promise<Web3NonceResponse>;\n web3Verify: (message: string, signature: string, address: string, chain: Web3Chain, walletType: Web3WalletType) => Promise<AuthonUser>;\n web3LinkWallet: (params: { address: string; chain: Web3Chain; walletType: Web3WalletType; chainId?: number; message: string; signature: string }) => Promise<Web3Wallet>;\n web3UnlinkWallet: (walletId: string) => Promise<void>;\n web3GetWallets: () => Promise<Web3Wallet[]>;\n // Passwordless\n passwordlessSendCode: (email: string, type?: 'magic-link' | 'otp') => Promise<void>;\n passwordlessVerifyCode: (email: string, code: string) => Promise<AuthonUser>;\n // Passkeys\n passkeyRegister: (name?: string) => Promise<PasskeyCredential>;\n passkeyAuthenticate: (email?: string) => Promise<AuthonUser>;\n passkeyList: () => Promise<PasskeyCredential[]>;\n passkeyDelete: (credentialId: string) => Promise<void>;\n}\n\n/**\n * Creates an Authon store with reactive Svelte stores.\n *\n * Usage:\n * ```ts\n * import { createAuthonStore } from '@authon/svelte'\n *\n * const authon = createAuthonStore('pk_live_...')\n *\n * // In your component:\n * $: user = $authon.user\n * $: isSignedIn = $authon.isSignedIn\n * ```\n */\nexport function createAuthonStore(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const client = new Authon(publishableKey, config);\n const userStore = writable<AuthonUser | null>(null);\n const isLoadingStore = writable(true);\n\n const isSignedIn = derived(userStore, ($user) => $user !== null);\n\n client.on('signedIn', (user) => {\n userStore.set(user as AuthonUser);\n isLoadingStore.set(false);\n });\n\n client.on('signedOut', () => {\n userStore.set(null);\n });\n\n client.on('error', () => {\n isLoadingStore.set(false);\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n userStore.set(existingUser);\n }\n isLoadingStore.set(false);\n\n return {\n user: { subscribe: userStore.subscribe },\n isSignedIn,\n isLoading: { subscribe: isLoadingStore.subscribe },\n signOut: async () => {\n await client.signOut();\n userStore.set(null);\n },\n openSignIn: () => client.openSignIn(),\n openSignUp: () => client.openSignUp(),\n getToken: () => client.getToken(),\n destroy: () => client.destroy(),\n client,\n // Web3\n web3GetNonce: (address, chain, walletType, chainId?) =>\n client.web3GetNonce(address, chain, walletType, chainId),\n web3Verify: (message, signature, address, chain, walletType) =>\n client.web3Verify(message, signature, address, chain, walletType),\n web3LinkWallet: (params) => client.linkWallet(params),\n web3UnlinkWallet: (walletId) => client.unlinkWallet(walletId),\n web3GetWallets: () => client.listWallets(),\n // Passwordless\n passwordlessSendCode: (email, type = 'otp') =>\n type === 'magic-link' ? client.sendMagicLink(email) : client.sendEmailOtp(email),\n passwordlessVerifyCode: (email, code) => client.verifyPasswordless({ email, code }),\n // Passkeys\n passkeyRegister: (name?) => client.registerPasskey(name),\n passkeyAuthenticate: (email?) => client.authenticateWithPasskey(email),\n passkeyList: () => client.listPasskeys(),\n passkeyDelete: (credentialId) => client.revokePasskey(credentialId),\n };\n}\n","import { setContext, getContext } from 'svelte';\nimport type { AuthonConfig } from '@authon/js';\nimport { createAuthonStore, type AuthonStore } from './store';\n\nconst AUTHON_CONTEXT_KEY = Symbol('authon');\n\n/**\n * Initialize Authon in a Svelte component tree.\n * Call this in your root layout or top-level component.\n *\n * Usage in +layout.svelte:\n * ```svelte\n * <script>\n * import { initAuthon } from '@authon/svelte'\n * const authon = initAuthon('pk_live_...')\n * </script>\n * ```\n */\nexport function initAuthon(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const store = createAuthonStore(publishableKey, config);\n setContext(AUTHON_CONTEXT_KEY, store);\n return store;\n}\n\n/**\n * Get the Authon store from Svelte context.\n * Must be called within a component tree where `initAuthon` was called.\n *\n * Usage:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * const { user, isSignedIn, signOut } = getAuthon()\n * </script>\n * ```\n */\nexport function getAuthon(): AuthonStore {\n const store = getContext<AuthonStore | undefined>(AUTHON_CONTEXT_KEY);\n if (!store) {\n throw new Error('getAuthon() must be called within a component tree where initAuthon() was called.');\n }\n return store;\n}\n","import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES, type OAuthProviderType } from '@authon/shared';\nimport { getProviderButtonConfig, type Authon } from '@authon/js';\n\nexport interface SocialButtonsOptions {\n /** Authon client instance */\n client: Authon;\n /** Target container element */\n container: HTMLElement;\n /** Called after successful OAuth sign-in */\n onSuccess?: () => void;\n /** Called on OAuth error */\n onError?: (error: Error) => void;\n /** Compact mode — icon-only square buttons in a row (default: false) */\n compact?: boolean;\n /** Gap between buttons in px (default: 10, compact default: 12) */\n gap?: number;\n /** Custom labels per provider */\n labels?: Partial<Record<OAuthProviderType, string>>;\n /** Icon size (default: 20, compact default: 24) */\n iconSize?: number;\n /** Border radius in px (default: 10) */\n borderRadius?: number;\n /** Button height in px (default: 48) */\n height?: number;\n /** Button size for compact mode in px (default: 48) */\n size?: number;\n}\n\n/**\n * Render social login buttons into a container element.\n *\n * Usage in +page.svelte:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * import { renderSocialButtons } from '@authon/svelte'\n * import { onMount } from 'svelte'\n *\n * const { client } = getAuthon()\n * let container: HTMLElement\n *\n * onMount(() => {\n * const cleanup = renderSocialButtons({\n * client,\n * container,\n * compact: true,\n * onError: (err) => console.error(err),\n * })\n * return cleanup\n * })\n * </script>\n *\n * <div bind:this={container}></div>\n * ```\n */\nexport function renderSocialButtons(options: SocialButtonsOptions): () => void {\n const {\n client,\n container,\n onSuccess,\n onError,\n compact = false,\n gap,\n labels,\n iconSize,\n borderRadius = 10,\n height = 48,\n size = 48,\n } = options;\n\n const resolvedGap = gap ?? (compact ? 12 : 10);\n const resolvedIconSize = iconSize ?? (compact ? 24 : 20);\n let loadingProvider: string | null = null;\n let buttons: HTMLButtonElement[] = [];\n\n const handleClick = async (provider: OAuthProviderType, btn: HTMLButtonElement) => {\n if (loadingProvider) return;\n loadingProvider = provider;\n btn.innerHTML = '<span style=\"display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite\"></span>';\n buttons.forEach((b) => (b.disabled = true));\n\n try {\n await client.signInWithOAuth(provider);\n onSuccess?.();\n } catch (e: any) {\n const error = e instanceof Error ? e : new Error(String(e));\n onError?.(error);\n } finally {\n loadingProvider = null;\n renderButtons(providers);\n }\n };\n\n let providers: OAuthProviderType[] = [];\n\n function renderButtons(providerList: OAuthProviderType[]) {\n container.innerHTML = '';\n buttons = [];\n\n // Inject keyframe if not present\n if (!document.getElementById('authon-spin-style')) {\n const style = document.createElement('style');\n style.id = 'authon-spin-style';\n style.textContent = '@keyframes authon-spin{to{transform:rotate(360deg)}}';\n document.head.appendChild(style);\n }\n\n const wrapper = document.createElement('div');\n wrapper.style.display = 'flex';\n wrapper.style.gap = `${resolvedGap}px`;\n\n if (compact) {\n wrapper.style.flexDirection = 'row';\n wrapper.style.flexWrap = 'wrap';\n wrapper.style.justifyContent = 'center';\n } else {\n wrapper.style.flexDirection = 'column';\n }\n\n for (const provider of providerList) {\n const colors = PROVIDER_COLORS[provider] || { bg: '#333', text: '#fff' };\n const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;\n const config = getProviderButtonConfig(provider);\n const iconSvg = config.iconSvg\n .replace(/width=\"\\d+\"/, `width=\"${resolvedIconSize}\"`)\n .replace(/height=\"\\d+\"/, `height=\"${resolvedIconSize}\"`);\n const needsBorder = colors.bg.toLowerCase() === '#ffffff';\n\n const btn = document.createElement('button');\n btn.setAttribute('aria-label', `Sign in with ${displayName}`);\n btn.style.display = 'flex';\n btn.style.alignItems = 'center';\n btn.style.justifyContent = 'center';\n btn.style.border = needsBorder ? '1px solid #dadce0' : 'none';\n btn.style.cursor = 'pointer';\n btn.style.backgroundColor = colors.bg;\n btn.style.color = colors.text;\n btn.style.borderRadius = `${borderRadius}px`;\n btn.style.transition = 'opacity 0.15s';\n btn.style.fontFamily = 'inherit';\n\n if (compact) {\n btn.style.width = `${size}px`;\n btn.style.height = `${size}px`;\n btn.style.padding = '0';\n btn.innerHTML = `<span style=\"display:flex;align-items:center\">${iconSvg}</span>`;\n } else {\n btn.style.width = '100%';\n btn.style.height = `${height}px`;\n btn.style.gap = '10px';\n btn.style.paddingLeft = '16px';\n btn.style.paddingRight = '16px';\n const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;\n btn.innerHTML = `<span style=\"display:flex;align-items:center;flex-shrink:0\">${iconSvg}</span><span style=\"font-size:15px;font-weight:600;white-space:nowrap\">${buttonLabel}</span>`;\n }\n\n btn.addEventListener('click', () => handleClick(provider, btn));\n btn.addEventListener('mouseenter', () => (btn.style.opacity = '0.85'));\n btn.addEventListener('mouseleave', () => (btn.style.opacity = '1'));\n\n buttons.push(btn);\n wrapper.appendChild(btn);\n }\n\n container.appendChild(wrapper);\n }\n\n // Init\n client.getProviders().then((p: OAuthProviderType[]) => {\n providers = p;\n if (providers.length > 0) {\n renderButtons(providers);\n }\n });\n\n // Cleanup\n return () => {\n container.innerHTML = '';\n buttons = [];\n };\n}\n"],"mappings":"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","names":["import_js"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Readable } from 'svelte/store';
|
|
2
2
|
import { Authon, AuthonConfig } from '@authon/js';
|
|
3
|
-
import { AuthonUser, OAuthProviderType } from '@authon/shared';
|
|
3
|
+
import { AuthonUser, Web3Chain, Web3WalletType, Web3NonceResponse, Web3Wallet, PasskeyCredential, OAuthProviderType } from '@authon/shared';
|
|
4
|
+
export { PasskeyCredential, Web3Chain, Web3NonceResponse, Web3Wallet, Web3WalletType } from '@authon/shared';
|
|
4
5
|
|
|
5
6
|
interface AuthonStore {
|
|
6
7
|
user: Readable<AuthonUser | null>;
|
|
@@ -12,6 +13,24 @@ interface AuthonStore {
|
|
|
12
13
|
getToken: () => string | null;
|
|
13
14
|
destroy: () => void;
|
|
14
15
|
client: Authon;
|
|
16
|
+
web3GetNonce: (address: string, chain: Web3Chain, walletType: Web3WalletType, chainId?: number) => Promise<Web3NonceResponse>;
|
|
17
|
+
web3Verify: (message: string, signature: string, address: string, chain: Web3Chain, walletType: Web3WalletType) => Promise<AuthonUser>;
|
|
18
|
+
web3LinkWallet: (params: {
|
|
19
|
+
address: string;
|
|
20
|
+
chain: Web3Chain;
|
|
21
|
+
walletType: Web3WalletType;
|
|
22
|
+
chainId?: number;
|
|
23
|
+
message: string;
|
|
24
|
+
signature: string;
|
|
25
|
+
}) => Promise<Web3Wallet>;
|
|
26
|
+
web3UnlinkWallet: (walletId: string) => Promise<void>;
|
|
27
|
+
web3GetWallets: () => Promise<Web3Wallet[]>;
|
|
28
|
+
passwordlessSendCode: (email: string, type?: 'magic-link' | 'otp') => Promise<void>;
|
|
29
|
+
passwordlessVerifyCode: (email: string, code: string) => Promise<AuthonUser>;
|
|
30
|
+
passkeyRegister: (name?: string) => Promise<PasskeyCredential>;
|
|
31
|
+
passkeyAuthenticate: (email?: string) => Promise<AuthonUser>;
|
|
32
|
+
passkeyList: () => Promise<PasskeyCredential[]>;
|
|
33
|
+
passkeyDelete: (credentialId: string) => Promise<void>;
|
|
15
34
|
}
|
|
16
35
|
/**
|
|
17
36
|
* Creates an Authon store with reactive Svelte stores.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Readable } from 'svelte/store';
|
|
2
2
|
import { Authon, AuthonConfig } from '@authon/js';
|
|
3
|
-
import { AuthonUser, OAuthProviderType } from '@authon/shared';
|
|
3
|
+
import { AuthonUser, Web3Chain, Web3WalletType, Web3NonceResponse, Web3Wallet, PasskeyCredential, OAuthProviderType } from '@authon/shared';
|
|
4
|
+
export { PasskeyCredential, Web3Chain, Web3NonceResponse, Web3Wallet, Web3WalletType } from '@authon/shared';
|
|
4
5
|
|
|
5
6
|
interface AuthonStore {
|
|
6
7
|
user: Readable<AuthonUser | null>;
|
|
@@ -12,6 +13,24 @@ interface AuthonStore {
|
|
|
12
13
|
getToken: () => string | null;
|
|
13
14
|
destroy: () => void;
|
|
14
15
|
client: Authon;
|
|
16
|
+
web3GetNonce: (address: string, chain: Web3Chain, walletType: Web3WalletType, chainId?: number) => Promise<Web3NonceResponse>;
|
|
17
|
+
web3Verify: (message: string, signature: string, address: string, chain: Web3Chain, walletType: Web3WalletType) => Promise<AuthonUser>;
|
|
18
|
+
web3LinkWallet: (params: {
|
|
19
|
+
address: string;
|
|
20
|
+
chain: Web3Chain;
|
|
21
|
+
walletType: Web3WalletType;
|
|
22
|
+
chainId?: number;
|
|
23
|
+
message: string;
|
|
24
|
+
signature: string;
|
|
25
|
+
}) => Promise<Web3Wallet>;
|
|
26
|
+
web3UnlinkWallet: (walletId: string) => Promise<void>;
|
|
27
|
+
web3GetWallets: () => Promise<Web3Wallet[]>;
|
|
28
|
+
passwordlessSendCode: (email: string, type?: 'magic-link' | 'otp') => Promise<void>;
|
|
29
|
+
passwordlessVerifyCode: (email: string, code: string) => Promise<AuthonUser>;
|
|
30
|
+
passkeyRegister: (name?: string) => Promise<PasskeyCredential>;
|
|
31
|
+
passkeyAuthenticate: (email?: string) => Promise<AuthonUser>;
|
|
32
|
+
passkeyList: () => Promise<PasskeyCredential[]>;
|
|
33
|
+
passkeyDelete: (credentialId: string) => Promise<void>;
|
|
15
34
|
}
|
|
16
35
|
/**
|
|
17
36
|
* Creates an Authon store with reactive Svelte stores.
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,21 @@ function createAuthonStore(publishableKey, config) {
|
|
|
33
33
|
openSignUp: () => client.openSignUp(),
|
|
34
34
|
getToken: () => client.getToken(),
|
|
35
35
|
destroy: () => client.destroy(),
|
|
36
|
-
client
|
|
36
|
+
client,
|
|
37
|
+
// Web3
|
|
38
|
+
web3GetNonce: (address, chain, walletType, chainId) => client.web3GetNonce(address, chain, walletType, chainId),
|
|
39
|
+
web3Verify: (message, signature, address, chain, walletType) => client.web3Verify(message, signature, address, chain, walletType),
|
|
40
|
+
web3LinkWallet: (params) => client.linkWallet(params),
|
|
41
|
+
web3UnlinkWallet: (walletId) => client.unlinkWallet(walletId),
|
|
42
|
+
web3GetWallets: () => client.listWallets(),
|
|
43
|
+
// Passwordless
|
|
44
|
+
passwordlessSendCode: (email, type = "otp") => type === "magic-link" ? client.sendMagicLink(email) : client.sendEmailOtp(email),
|
|
45
|
+
passwordlessVerifyCode: (email, code) => client.verifyPasswordless({ email, code }),
|
|
46
|
+
// Passkeys
|
|
47
|
+
passkeyRegister: (name) => client.registerPasskey(name),
|
|
48
|
+
passkeyAuthenticate: (email) => client.authenticateWithPasskey(email),
|
|
49
|
+
passkeyList: () => client.listPasskeys(),
|
|
50
|
+
passkeyDelete: (credentialId) => client.revokePasskey(credentialId)
|
|
37
51
|
};
|
|
38
52
|
}
|
|
39
53
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store.ts","../src/context.ts","../src/SocialButtons.ts"],"sourcesContent":["import { writable, derived, type Readable } from 'svelte/store';\nimport { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type { AuthonUser } from '@authon/shared';\n\nexport interface AuthonStore {\n user: Readable<AuthonUser | null>;\n isSignedIn: Readable<boolean>;\n isLoading: Readable<boolean>;\n signOut: () => Promise<void>;\n openSignIn: () => Promise<void>;\n openSignUp: () => Promise<void>;\n getToken: () => string | null;\n destroy: () => void;\n client: Authon;\n}\n\n/**\n * Creates an Authon store with reactive Svelte stores.\n *\n * Usage:\n * ```ts\n * import { createAuthonStore } from '@authon/svelte'\n *\n * const authon = createAuthonStore('pk_live_...')\n *\n * // In your component:\n * $: user = $authon.user\n * $: isSignedIn = $authon.isSignedIn\n * ```\n */\nexport function createAuthonStore(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const client = new Authon(publishableKey, config);\n const userStore = writable<AuthonUser | null>(null);\n const isLoadingStore = writable(true);\n\n const isSignedIn = derived(userStore, ($user) => $user !== null);\n\n client.on('signedIn', (user) => {\n userStore.set(user as AuthonUser);\n isLoadingStore.set(false);\n });\n\n client.on('signedOut', () => {\n userStore.set(null);\n });\n\n client.on('error', () => {\n isLoadingStore.set(false);\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n userStore.set(existingUser);\n }\n isLoadingStore.set(false);\n\n return {\n user: { subscribe: userStore.subscribe },\n isSignedIn,\n isLoading: { subscribe: isLoadingStore.subscribe },\n signOut: async () => {\n await client.signOut();\n userStore.set(null);\n },\n openSignIn: () => client.openSignIn(),\n openSignUp: () => client.openSignUp(),\n getToken: () => client.getToken(),\n destroy: () => client.destroy(),\n client,\n };\n}\n","import { setContext, getContext } from 'svelte';\nimport type { AuthonConfig } from '@authon/js';\nimport { createAuthonStore, type AuthonStore } from './store';\n\nconst AUTHON_CONTEXT_KEY = Symbol('authon');\n\n/**\n * Initialize Authon in a Svelte component tree.\n * Call this in your root layout or top-level component.\n *\n * Usage in +layout.svelte:\n * ```svelte\n * <script>\n * import { initAuthon } from '@authon/svelte'\n * const authon = initAuthon('pk_live_...')\n * </script>\n * ```\n */\nexport function initAuthon(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const store = createAuthonStore(publishableKey, config);\n setContext(AUTHON_CONTEXT_KEY, store);\n return store;\n}\n\n/**\n * Get the Authon store from Svelte context.\n * Must be called within a component tree where `initAuthon` was called.\n *\n * Usage:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * const { user, isSignedIn, signOut } = getAuthon()\n * </script>\n * ```\n */\nexport function getAuthon(): AuthonStore {\n const store = getContext<AuthonStore | undefined>(AUTHON_CONTEXT_KEY);\n if (!store) {\n throw new Error('getAuthon() must be called within a component tree where initAuthon() was called.');\n }\n return store;\n}\n","import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES, type OAuthProviderType } from '@authon/shared';\nimport { getProviderButtonConfig, type Authon } from '@authon/js';\n\nexport interface SocialButtonsOptions {\n /** Authon client instance */\n client: Authon;\n /** Target container element */\n container: HTMLElement;\n /** Called after successful OAuth sign-in */\n onSuccess?: () => void;\n /** Called on OAuth error */\n onError?: (error: Error) => void;\n /** Compact mode — icon-only square buttons in a row (default: false) */\n compact?: boolean;\n /** Gap between buttons in px (default: 10, compact default: 12) */\n gap?: number;\n /** Custom labels per provider */\n labels?: Partial<Record<OAuthProviderType, string>>;\n /** Icon size (default: 20, compact default: 24) */\n iconSize?: number;\n /** Border radius in px (default: 10) */\n borderRadius?: number;\n /** Button height in px (default: 48) */\n height?: number;\n /** Button size for compact mode in px (default: 48) */\n size?: number;\n}\n\n/**\n * Render social login buttons into a container element.\n *\n * Usage in +page.svelte:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * import { renderSocialButtons } from '@authon/svelte'\n * import { onMount } from 'svelte'\n *\n * const { client } = getAuthon()\n * let container: HTMLElement\n *\n * onMount(() => {\n * const cleanup = renderSocialButtons({\n * client,\n * container,\n * compact: true,\n * onError: (err) => console.error(err),\n * })\n * return cleanup\n * })\n * </script>\n *\n * <div bind:this={container}></div>\n * ```\n */\nexport function renderSocialButtons(options: SocialButtonsOptions): () => void {\n const {\n client,\n container,\n onSuccess,\n onError,\n compact = false,\n gap,\n labels,\n iconSize,\n borderRadius = 10,\n height = 48,\n size = 48,\n } = options;\n\n const resolvedGap = gap ?? (compact ? 12 : 10);\n const resolvedIconSize = iconSize ?? (compact ? 24 : 20);\n let loadingProvider: string | null = null;\n let buttons: HTMLButtonElement[] = [];\n\n const handleClick = async (provider: OAuthProviderType, btn: HTMLButtonElement) => {\n if (loadingProvider) return;\n loadingProvider = provider;\n btn.innerHTML = '<span style=\"display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite\"></span>';\n buttons.forEach((b) => (b.disabled = true));\n\n try {\n await client.signInWithOAuth(provider);\n onSuccess?.();\n } catch (e: any) {\n const error = e instanceof Error ? e : new Error(String(e));\n onError?.(error);\n } finally {\n loadingProvider = null;\n renderButtons(providers);\n }\n };\n\n let providers: OAuthProviderType[] = [];\n\n function renderButtons(providerList: OAuthProviderType[]) {\n container.innerHTML = '';\n buttons = [];\n\n // Inject keyframe if not present\n if (!document.getElementById('authon-spin-style')) {\n const style = document.createElement('style');\n style.id = 'authon-spin-style';\n style.textContent = '@keyframes authon-spin{to{transform:rotate(360deg)}}';\n document.head.appendChild(style);\n }\n\n const wrapper = document.createElement('div');\n wrapper.style.display = 'flex';\n wrapper.style.gap = `${resolvedGap}px`;\n\n if (compact) {\n wrapper.style.flexDirection = 'row';\n wrapper.style.flexWrap = 'wrap';\n wrapper.style.justifyContent = 'center';\n } else {\n wrapper.style.flexDirection = 'column';\n }\n\n for (const provider of providerList) {\n const colors = PROVIDER_COLORS[provider] || { bg: '#333', text: '#fff' };\n const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;\n const config = getProviderButtonConfig(provider);\n const iconSvg = config.iconSvg\n .replace(/width=\"\\d+\"/, `width=\"${resolvedIconSize}\"`)\n .replace(/height=\"\\d+\"/, `height=\"${resolvedIconSize}\"`);\n const needsBorder = colors.bg.toLowerCase() === '#ffffff';\n\n const btn = document.createElement('button');\n btn.setAttribute('aria-label', `Sign in with ${displayName}`);\n btn.style.display = 'flex';\n btn.style.alignItems = 'center';\n btn.style.justifyContent = 'center';\n btn.style.border = needsBorder ? '1px solid #dadce0' : 'none';\n btn.style.cursor = 'pointer';\n btn.style.backgroundColor = colors.bg;\n btn.style.color = colors.text;\n btn.style.borderRadius = `${borderRadius}px`;\n btn.style.transition = 'opacity 0.15s';\n btn.style.fontFamily = 'inherit';\n\n if (compact) {\n btn.style.width = `${size}px`;\n btn.style.height = `${size}px`;\n btn.style.padding = '0';\n btn.innerHTML = `<span style=\"display:flex;align-items:center\">${iconSvg}</span>`;\n } else {\n btn.style.width = '100%';\n btn.style.height = `${height}px`;\n btn.style.gap = '10px';\n btn.style.paddingLeft = '16px';\n btn.style.paddingRight = '16px';\n const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;\n btn.innerHTML = `<span style=\"display:flex;align-items:center;flex-shrink:0\">${iconSvg}</span><span style=\"font-size:15px;font-weight:600;white-space:nowrap\">${buttonLabel}</span>`;\n }\n\n btn.addEventListener('click', () => handleClick(provider, btn));\n btn.addEventListener('mouseenter', () => (btn.style.opacity = '0.85'));\n btn.addEventListener('mouseleave', () => (btn.style.opacity = '1'));\n\n buttons.push(btn);\n wrapper.appendChild(btn);\n }\n\n container.appendChild(wrapper);\n }\n\n // Init\n client.getProviders().then((p: OAuthProviderType[]) => {\n providers = p;\n if (providers.length > 0) {\n renderButtons(providers);\n }\n });\n\n // Cleanup\n return () => {\n container.innerHTML = '';\n buttons = [];\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/store.ts","../src/context.ts","../src/SocialButtons.ts"],"sourcesContent":["import { writable, derived, type Readable } from 'svelte/store';\nimport { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type {\n AuthonUser,\n PasskeyCredential,\n Web3Chain,\n Web3NonceResponse,\n Web3Wallet,\n Web3WalletType,\n} from '@authon/shared';\n\nexport interface AuthonStore {\n user: Readable<AuthonUser | null>;\n isSignedIn: Readable<boolean>;\n isLoading: Readable<boolean>;\n signOut: () => Promise<void>;\n openSignIn: () => Promise<void>;\n openSignUp: () => Promise<void>;\n getToken: () => string | null;\n destroy: () => void;\n client: Authon;\n // Web3\n web3GetNonce: (address: string, chain: Web3Chain, walletType: Web3WalletType, chainId?: number) => Promise<Web3NonceResponse>;\n web3Verify: (message: string, signature: string, address: string, chain: Web3Chain, walletType: Web3WalletType) => Promise<AuthonUser>;\n web3LinkWallet: (params: { address: string; chain: Web3Chain; walletType: Web3WalletType; chainId?: number; message: string; signature: string }) => Promise<Web3Wallet>;\n web3UnlinkWallet: (walletId: string) => Promise<void>;\n web3GetWallets: () => Promise<Web3Wallet[]>;\n // Passwordless\n passwordlessSendCode: (email: string, type?: 'magic-link' | 'otp') => Promise<void>;\n passwordlessVerifyCode: (email: string, code: string) => Promise<AuthonUser>;\n // Passkeys\n passkeyRegister: (name?: string) => Promise<PasskeyCredential>;\n passkeyAuthenticate: (email?: string) => Promise<AuthonUser>;\n passkeyList: () => Promise<PasskeyCredential[]>;\n passkeyDelete: (credentialId: string) => Promise<void>;\n}\n\n/**\n * Creates an Authon store with reactive Svelte stores.\n *\n * Usage:\n * ```ts\n * import { createAuthonStore } from '@authon/svelte'\n *\n * const authon = createAuthonStore('pk_live_...')\n *\n * // In your component:\n * $: user = $authon.user\n * $: isSignedIn = $authon.isSignedIn\n * ```\n */\nexport function createAuthonStore(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const client = new Authon(publishableKey, config);\n const userStore = writable<AuthonUser | null>(null);\n const isLoadingStore = writable(true);\n\n const isSignedIn = derived(userStore, ($user) => $user !== null);\n\n client.on('signedIn', (user) => {\n userStore.set(user as AuthonUser);\n isLoadingStore.set(false);\n });\n\n client.on('signedOut', () => {\n userStore.set(null);\n });\n\n client.on('error', () => {\n isLoadingStore.set(false);\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n userStore.set(existingUser);\n }\n isLoadingStore.set(false);\n\n return {\n user: { subscribe: userStore.subscribe },\n isSignedIn,\n isLoading: { subscribe: isLoadingStore.subscribe },\n signOut: async () => {\n await client.signOut();\n userStore.set(null);\n },\n openSignIn: () => client.openSignIn(),\n openSignUp: () => client.openSignUp(),\n getToken: () => client.getToken(),\n destroy: () => client.destroy(),\n client,\n // Web3\n web3GetNonce: (address, chain, walletType, chainId?) =>\n client.web3GetNonce(address, chain, walletType, chainId),\n web3Verify: (message, signature, address, chain, walletType) =>\n client.web3Verify(message, signature, address, chain, walletType),\n web3LinkWallet: (params) => client.linkWallet(params),\n web3UnlinkWallet: (walletId) => client.unlinkWallet(walletId),\n web3GetWallets: () => client.listWallets(),\n // Passwordless\n passwordlessSendCode: (email, type = 'otp') =>\n type === 'magic-link' ? client.sendMagicLink(email) : client.sendEmailOtp(email),\n passwordlessVerifyCode: (email, code) => client.verifyPasswordless({ email, code }),\n // Passkeys\n passkeyRegister: (name?) => client.registerPasskey(name),\n passkeyAuthenticate: (email?) => client.authenticateWithPasskey(email),\n passkeyList: () => client.listPasskeys(),\n passkeyDelete: (credentialId) => client.revokePasskey(credentialId),\n };\n}\n","import { setContext, getContext } from 'svelte';\nimport type { AuthonConfig } from '@authon/js';\nimport { createAuthonStore, type AuthonStore } from './store';\n\nconst AUTHON_CONTEXT_KEY = Symbol('authon');\n\n/**\n * Initialize Authon in a Svelte component tree.\n * Call this in your root layout or top-level component.\n *\n * Usage in +layout.svelte:\n * ```svelte\n * <script>\n * import { initAuthon } from '@authon/svelte'\n * const authon = initAuthon('pk_live_...')\n * </script>\n * ```\n */\nexport function initAuthon(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonStore {\n const store = createAuthonStore(publishableKey, config);\n setContext(AUTHON_CONTEXT_KEY, store);\n return store;\n}\n\n/**\n * Get the Authon store from Svelte context.\n * Must be called within a component tree where `initAuthon` was called.\n *\n * Usage:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * const { user, isSignedIn, signOut } = getAuthon()\n * </script>\n * ```\n */\nexport function getAuthon(): AuthonStore {\n const store = getContext<AuthonStore | undefined>(AUTHON_CONTEXT_KEY);\n if (!store) {\n throw new Error('getAuthon() must be called within a component tree where initAuthon() was called.');\n }\n return store;\n}\n","import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES, type OAuthProviderType } from '@authon/shared';\nimport { getProviderButtonConfig, type Authon } from '@authon/js';\n\nexport interface SocialButtonsOptions {\n /** Authon client instance */\n client: Authon;\n /** Target container element */\n container: HTMLElement;\n /** Called after successful OAuth sign-in */\n onSuccess?: () => void;\n /** Called on OAuth error */\n onError?: (error: Error) => void;\n /** Compact mode — icon-only square buttons in a row (default: false) */\n compact?: boolean;\n /** Gap between buttons in px (default: 10, compact default: 12) */\n gap?: number;\n /** Custom labels per provider */\n labels?: Partial<Record<OAuthProviderType, string>>;\n /** Icon size (default: 20, compact default: 24) */\n iconSize?: number;\n /** Border radius in px (default: 10) */\n borderRadius?: number;\n /** Button height in px (default: 48) */\n height?: number;\n /** Button size for compact mode in px (default: 48) */\n size?: number;\n}\n\n/**\n * Render social login buttons into a container element.\n *\n * Usage in +page.svelte:\n * ```svelte\n * <script>\n * import { getAuthon } from '@authon/svelte'\n * import { renderSocialButtons } from '@authon/svelte'\n * import { onMount } from 'svelte'\n *\n * const { client } = getAuthon()\n * let container: HTMLElement\n *\n * onMount(() => {\n * const cleanup = renderSocialButtons({\n * client,\n * container,\n * compact: true,\n * onError: (err) => console.error(err),\n * })\n * return cleanup\n * })\n * </script>\n *\n * <div bind:this={container}></div>\n * ```\n */\nexport function renderSocialButtons(options: SocialButtonsOptions): () => void {\n const {\n client,\n container,\n onSuccess,\n onError,\n compact = false,\n gap,\n labels,\n iconSize,\n borderRadius = 10,\n height = 48,\n size = 48,\n } = options;\n\n const resolvedGap = gap ?? (compact ? 12 : 10);\n const resolvedIconSize = iconSize ?? (compact ? 24 : 20);\n let loadingProvider: string | null = null;\n let buttons: HTMLButtonElement[] = [];\n\n const handleClick = async (provider: OAuthProviderType, btn: HTMLButtonElement) => {\n if (loadingProvider) return;\n loadingProvider = provider;\n btn.innerHTML = '<span style=\"display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite\"></span>';\n buttons.forEach((b) => (b.disabled = true));\n\n try {\n await client.signInWithOAuth(provider);\n onSuccess?.();\n } catch (e: any) {\n const error = e instanceof Error ? e : new Error(String(e));\n onError?.(error);\n } finally {\n loadingProvider = null;\n renderButtons(providers);\n }\n };\n\n let providers: OAuthProviderType[] = [];\n\n function renderButtons(providerList: OAuthProviderType[]) {\n container.innerHTML = '';\n buttons = [];\n\n // Inject keyframe if not present\n if (!document.getElementById('authon-spin-style')) {\n const style = document.createElement('style');\n style.id = 'authon-spin-style';\n style.textContent = '@keyframes authon-spin{to{transform:rotate(360deg)}}';\n document.head.appendChild(style);\n }\n\n const wrapper = document.createElement('div');\n wrapper.style.display = 'flex';\n wrapper.style.gap = `${resolvedGap}px`;\n\n if (compact) {\n wrapper.style.flexDirection = 'row';\n wrapper.style.flexWrap = 'wrap';\n wrapper.style.justifyContent = 'center';\n } else {\n wrapper.style.flexDirection = 'column';\n }\n\n for (const provider of providerList) {\n const colors = PROVIDER_COLORS[provider] || { bg: '#333', text: '#fff' };\n const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;\n const config = getProviderButtonConfig(provider);\n const iconSvg = config.iconSvg\n .replace(/width=\"\\d+\"/, `width=\"${resolvedIconSize}\"`)\n .replace(/height=\"\\d+\"/, `height=\"${resolvedIconSize}\"`);\n const needsBorder = colors.bg.toLowerCase() === '#ffffff';\n\n const btn = document.createElement('button');\n btn.setAttribute('aria-label', `Sign in with ${displayName}`);\n btn.style.display = 'flex';\n btn.style.alignItems = 'center';\n btn.style.justifyContent = 'center';\n btn.style.border = needsBorder ? '1px solid #dadce0' : 'none';\n btn.style.cursor = 'pointer';\n btn.style.backgroundColor = colors.bg;\n btn.style.color = colors.text;\n btn.style.borderRadius = `${borderRadius}px`;\n btn.style.transition = 'opacity 0.15s';\n btn.style.fontFamily = 'inherit';\n\n if (compact) {\n btn.style.width = `${size}px`;\n btn.style.height = `${size}px`;\n btn.style.padding = '0';\n btn.innerHTML = `<span style=\"display:flex;align-items:center\">${iconSvg}</span>`;\n } else {\n btn.style.width = '100%';\n btn.style.height = `${height}px`;\n btn.style.gap = '10px';\n btn.style.paddingLeft = '16px';\n btn.style.paddingRight = '16px';\n const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;\n btn.innerHTML = `<span style=\"display:flex;align-items:center;flex-shrink:0\">${iconSvg}</span><span style=\"font-size:15px;font-weight:600;white-space:nowrap\">${buttonLabel}</span>`;\n }\n\n btn.addEventListener('click', () => handleClick(provider, btn));\n btn.addEventListener('mouseenter', () => (btn.style.opacity = '0.85'));\n btn.addEventListener('mouseleave', () => (btn.style.opacity = '1'));\n\n buttons.push(btn);\n wrapper.appendChild(btn);\n }\n\n container.appendChild(wrapper);\n }\n\n // Init\n client.getProviders().then((p: OAuthProviderType[]) => {\n providers = p;\n if (providers.length > 0) {\n renderButtons(providers);\n }\n });\n\n // Cleanup\n return () => {\n container.innerHTML = '';\n buttons = [];\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@authon/svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Authon Svelte SDK — stores and components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"store"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@authon/js": "
|
|
41
|
-
"@authon/shared": "
|
|
40
|
+
"@authon/js": "workspace:^",
|
|
41
|
+
"@authon/shared": "workspace:^"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"svelte": "^4.0.0 || ^5.0.0"
|