@authon/vue 0.2.0 → 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 +170 -0
- package/README.md +413 -68
- package/dist/index.cjs +334 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +192 -3
- package/dist/index.d.ts +192 -3
- package/dist/index.js +310 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.ko.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
[English](./README.md) | **한국어**
|
|
2
|
+
|
|
3
|
+
# @authon/vue
|
|
4
|
+
|
|
5
|
+
[Authon](https://authon.dev)용 Vue 3 SDK — 플러그인, composable, 컴포넌트를 제공합니다.
|
|
6
|
+
|
|
7
|
+
## 설치
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @authon/vue
|
|
11
|
+
# 또는
|
|
12
|
+
pnpm add @authon/vue
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`vue >= 3.3.0`이 필요합니다.
|
|
16
|
+
|
|
17
|
+
## 빠른 시작
|
|
18
|
+
|
|
19
|
+
### 1. 플러그인 설치
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// main.ts
|
|
23
|
+
import { createApp } from 'vue';
|
|
24
|
+
import { AuthonPlugin } from '@authon/vue';
|
|
25
|
+
import App from './App.vue';
|
|
26
|
+
|
|
27
|
+
const app = createApp(App);
|
|
28
|
+
app.use(AuthonPlugin, {
|
|
29
|
+
publishableKey: 'pk_live_...',
|
|
30
|
+
});
|
|
31
|
+
app.mount('#app');
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. Composable 사용
|
|
35
|
+
|
|
36
|
+
```vue
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import { useAuthon, useUser } from '@authon/vue';
|
|
39
|
+
|
|
40
|
+
const { isSignedIn, openSignIn, signOut } = useAuthon();
|
|
41
|
+
const { user, isLoading } = useUser();
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<div v-if="isLoading">Loading...</div>
|
|
46
|
+
<div v-else-if="isSignedIn">
|
|
47
|
+
<p>Welcome, {{ user?.displayName }}</p>
|
|
48
|
+
<button @click="signOut()">Sign Out</button>
|
|
49
|
+
</div>
|
|
50
|
+
<div v-else>
|
|
51
|
+
<button @click="openSignIn()">Sign In</button>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 3. 컴포넌트 사용
|
|
57
|
+
|
|
58
|
+
```vue
|
|
59
|
+
<template>
|
|
60
|
+
<SignedIn>
|
|
61
|
+
<UserButton />
|
|
62
|
+
</SignedIn>
|
|
63
|
+
<SignedOut>
|
|
64
|
+
<button @click="openSignIn()">Sign In</button>
|
|
65
|
+
</SignedOut>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
<script setup lang="ts">
|
|
69
|
+
import { SignedIn, SignedOut, UserButton, useAuthon } from '@authon/vue';
|
|
70
|
+
|
|
71
|
+
const { openSignIn } = useAuthon();
|
|
72
|
+
</script>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## API 레퍼런스
|
|
76
|
+
|
|
77
|
+
### 플러그인
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
app.use(AuthonPlugin, {
|
|
81
|
+
publishableKey: string;
|
|
82
|
+
apiUrl?: string;
|
|
83
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
84
|
+
locale?: string;
|
|
85
|
+
appearance?: Partial<BrandingConfig>;
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Composable
|
|
90
|
+
|
|
91
|
+
#### `useAuthon()`
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
const {
|
|
95
|
+
isSignedIn, // Ref<boolean>
|
|
96
|
+
isLoading, // Ref<boolean>
|
|
97
|
+
user, // Ref<AuthonUser | null>
|
|
98
|
+
signOut, // () => Promise<void>
|
|
99
|
+
openSignIn, // () => Promise<void>
|
|
100
|
+
openSignUp, // () => Promise<void>
|
|
101
|
+
getToken, // () => string | null
|
|
102
|
+
client, // Authon 인스턴스
|
|
103
|
+
} = useAuthon();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `useUser()`
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const { user, isLoading } = useUser();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 컴포넌트
|
|
113
|
+
|
|
114
|
+
| 컴포넌트 | 설명 |
|
|
115
|
+
|----------|------|
|
|
116
|
+
| `<SignedIn>` | 로그인 상태일 때만 슬롯을 렌더링 |
|
|
117
|
+
| `<SignedOut>` | 로그아웃 상태일 때만 슬롯을 렌더링 |
|
|
118
|
+
| `<UserButton>` | 로그아웃 기능이 포함된 아바타 드롭다운 |
|
|
119
|
+
| `<Protect>` | fallback 슬롯을 지원하는 조건부 렌더링 |
|
|
120
|
+
|
|
121
|
+
## Multi-Factor Authentication (MFA)
|
|
122
|
+
|
|
123
|
+
`useAuthon()`의 `client` 인스턴스를 통해 MFA 메서드에 접근합니다.
|
|
124
|
+
|
|
125
|
+
```vue
|
|
126
|
+
<script setup lang="ts">
|
|
127
|
+
import { ref } from 'vue';
|
|
128
|
+
import { useAuthon } from '@authon/vue';
|
|
129
|
+
import { AuthonMfaRequiredError } from '@authon/js';
|
|
130
|
+
|
|
131
|
+
const { client } = useAuthon();
|
|
132
|
+
const qrSvg = ref('');
|
|
133
|
+
const mfaToken = ref('');
|
|
134
|
+
|
|
135
|
+
// MFA 설정
|
|
136
|
+
async function enableMfa() {
|
|
137
|
+
const setup = await client.value!.setupMfa();
|
|
138
|
+
qrSvg.value = setup.qrCodeSvg; // 인증 앱에 표시할 QR 코드
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function verifySetup(code: string) {
|
|
142
|
+
await client.value!.verifyMfaSetup(code);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// MFA를 사용한 로그인
|
|
146
|
+
async function signIn(email: string, password: string) {
|
|
147
|
+
try {
|
|
148
|
+
await client.value!.signInWithEmail(email, password);
|
|
149
|
+
} catch (err) {
|
|
150
|
+
if (err instanceof AuthonMfaRequiredError) {
|
|
151
|
+
mfaToken.value = err.mfaToken; // TOTP 입력 화면 표시
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function verifyMfa(code: string) {
|
|
157
|
+
await client.value!.verifyMfa(mfaToken.value, code);
|
|
158
|
+
}
|
|
159
|
+
</script>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
전체 API 레퍼런스는 [`@authon/js` MFA 문서](../js/README.md#multi-factor-authentication-mfa)를 참고하세요.
|
|
163
|
+
|
|
164
|
+
## 문서
|
|
165
|
+
|
|
166
|
+
[authon.dev/docs](https://authon.dev/docs)
|
|
167
|
+
|
|
168
|
+
## 라이선스
|
|
169
|
+
|
|
170
|
+
[MIT](../../LICENSE)
|
package/README.md
CHANGED
|
@@ -1,120 +1,465 @@
|
|
|
1
|
+
**English** | [한국어](./README.ko.md)
|
|
2
|
+
|
|
1
3
|
# @authon/vue
|
|
2
4
|
|
|
3
|
-
Vue 3
|
|
5
|
+
Vue 3 Composition API integration for [Authon](https://authon.dev) — plugin setup, composables, and pre-built components.
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
npm install @authon/vue
|
|
9
|
-
# or
|
|
10
|
-
pnpm add @authon/vue
|
|
10
|
+
npm install @authon/vue @authon/js
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Requires `vue >= 3.3.0`.
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Setup
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Register the plugin in `main.ts`:
|
|
18
18
|
|
|
19
19
|
```ts
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
import
|
|
23
|
-
|
|
20
|
+
import { createApp } from 'vue'
|
|
21
|
+
import { createAuthon } from '@authon/vue'
|
|
22
|
+
import App from './App.vue'
|
|
23
|
+
|
|
24
|
+
const app = createApp(App)
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
app.use(AuthonPlugin, {
|
|
26
|
+
app.use(createAuthon({
|
|
27
27
|
publishableKey: 'pk_live_...',
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
config: {
|
|
29
|
+
theme: 'auto',
|
|
30
|
+
locale: 'en',
|
|
31
|
+
},
|
|
32
|
+
}))
|
|
33
|
+
|
|
34
|
+
app.mount('#app')
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Composables
|
|
38
|
+
|
|
39
|
+
### `useAuthon()`
|
|
40
|
+
|
|
41
|
+
Returns the full auth state and helper methods. The `client` property exposes the underlying `@authon/js` `Authon` instance for all advanced operations.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { useAuthon } from '@authon/vue'
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
isSignedIn, // boolean — reactive
|
|
48
|
+
isLoading, // boolean — reactive
|
|
49
|
+
user, // AuthonUser | null — reactive
|
|
50
|
+
client, // Authon instance from @authon/js
|
|
51
|
+
signOut, // () => Promise<void>
|
|
52
|
+
openSignIn, // () => Promise<void>
|
|
53
|
+
openSignUp, // () => Promise<void>
|
|
54
|
+
getToken, // () => string | null
|
|
55
|
+
} = useAuthon()
|
|
30
56
|
```
|
|
31
57
|
|
|
32
|
-
###
|
|
58
|
+
### `useUser()`
|
|
59
|
+
|
|
60
|
+
Returns only the user and loading state as computed refs.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { useUser } from '@authon/vue'
|
|
64
|
+
|
|
65
|
+
const { user, isLoading } = useUser()
|
|
66
|
+
// user: ComputedRef<AuthonUser | null>
|
|
67
|
+
// isLoading: ComputedRef<boolean>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Components
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import {
|
|
74
|
+
AuthonSignIn,
|
|
75
|
+
AuthonSignUp,
|
|
76
|
+
AuthonUserButton,
|
|
77
|
+
AuthonSignedIn,
|
|
78
|
+
AuthonSignedOut,
|
|
79
|
+
AuthonSocialButton,
|
|
80
|
+
AuthonSocialButtons,
|
|
81
|
+
} from '@authon/vue'
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Component | Description |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `<AuthonSignIn mode="popup" />` | Opens sign-in UI. `mode`: `'popup'` (default) or `'embedded'` |
|
|
87
|
+
| `<AuthonSignUp mode="popup" />` | Opens sign-up UI. `mode`: `'popup'` (default) or `'embedded'` |
|
|
88
|
+
| `<AuthonUserButton />` | Avatar button with dropdown — shows user info and sign-out |
|
|
89
|
+
| `<AuthonSignedIn>` | Renders default slot only when the user is authenticated |
|
|
90
|
+
| `<AuthonSignedOut>` | Renders default slot only when the user is not authenticated |
|
|
91
|
+
| `<AuthonSocialButton provider="google" />` | Single branded OAuth provider button |
|
|
92
|
+
| `<AuthonSocialButtons />` | Renders all configured OAuth provider buttons automatically |
|
|
93
|
+
|
|
94
|
+
## Examples
|
|
95
|
+
|
|
96
|
+
### Basic auth state in a navbar
|
|
33
97
|
|
|
34
98
|
```vue
|
|
99
|
+
<template>
|
|
100
|
+
<nav>
|
|
101
|
+
<AuthonSignedIn>
|
|
102
|
+
<span>Hello, {{ user?.displayName }}</span>
|
|
103
|
+
<button @click="signOut">Sign out</button>
|
|
104
|
+
</AuthonSignedIn>
|
|
105
|
+
<AuthonSignedOut>
|
|
106
|
+
<AuthonUserButton />
|
|
107
|
+
</AuthonSignedOut>
|
|
108
|
+
</nav>
|
|
109
|
+
</template>
|
|
110
|
+
|
|
35
111
|
<script setup lang="ts">
|
|
36
|
-
import { useAuthon,
|
|
112
|
+
import { useAuthon, AuthonSignedIn, AuthonSignedOut, AuthonUserButton } from '@authon/vue'
|
|
37
113
|
|
|
38
|
-
const {
|
|
39
|
-
const { user, isLoading } = useUser();
|
|
114
|
+
const { user, signOut } = useAuthon()
|
|
40
115
|
</script>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Email + password sign-in
|
|
41
119
|
|
|
120
|
+
```vue
|
|
42
121
|
<template>
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
<
|
|
46
|
-
<button
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
122
|
+
<form @submit.prevent="handleSignIn">
|
|
123
|
+
<input v-model="email" type="email" placeholder="Email" />
|
|
124
|
+
<input v-model="password" type="password" placeholder="Password" />
|
|
125
|
+
<button type="submit" :disabled="loading">Sign in</button>
|
|
126
|
+
<p v-if="error">{{ error }}</p>
|
|
127
|
+
</form>
|
|
128
|
+
</template>
|
|
129
|
+
|
|
130
|
+
<script setup lang="ts">
|
|
131
|
+
import { ref } from 'vue'
|
|
132
|
+
import { useAuthon } from '@authon/vue'
|
|
133
|
+
import { useRouter } from 'vue-router'
|
|
134
|
+
|
|
135
|
+
const { client } = useAuthon()
|
|
136
|
+
const router = useRouter()
|
|
137
|
+
const email = ref('')
|
|
138
|
+
const password = ref('')
|
|
139
|
+
const loading = ref(false)
|
|
140
|
+
const error = ref('')
|
|
141
|
+
|
|
142
|
+
async function handleSignIn() {
|
|
143
|
+
loading.value = true
|
|
144
|
+
error.value = ''
|
|
145
|
+
try {
|
|
146
|
+
await client!.signInWithEmail(email.value, password.value)
|
|
147
|
+
router.push('/dashboard')
|
|
148
|
+
} catch (e: any) {
|
|
149
|
+
error.value = e.message
|
|
150
|
+
} finally {
|
|
151
|
+
loading.value = false
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
</script>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### OAuth sign-in
|
|
158
|
+
|
|
159
|
+
```vue
|
|
160
|
+
<script setup lang="ts">
|
|
161
|
+
import { useAuthon, AuthonSocialButtons } from '@authon/vue'
|
|
162
|
+
|
|
163
|
+
const { client } = useAuthon()
|
|
164
|
+
|
|
165
|
+
// Trigger a specific provider directly
|
|
166
|
+
async function signInWithGoogle() {
|
|
167
|
+
await client!.signInWithOAuth('google')
|
|
168
|
+
}
|
|
169
|
+
</script>
|
|
170
|
+
|
|
171
|
+
<template>
|
|
172
|
+
<!-- Or render all configured providers automatically -->
|
|
173
|
+
<AuthonSocialButtons
|
|
174
|
+
:compact="false"
|
|
175
|
+
:labels="{ google: 'Continue with Google' }"
|
|
176
|
+
@success="() => router.push('/dashboard')"
|
|
177
|
+
@error="(e) => console.error(e)"
|
|
178
|
+
/>
|
|
51
179
|
</template>
|
|
52
180
|
```
|
|
53
181
|
|
|
54
|
-
###
|
|
182
|
+
### MFA setup
|
|
55
183
|
|
|
56
184
|
```vue
|
|
57
185
|
<template>
|
|
58
|
-
<
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
|
|
186
|
+
<div>
|
|
187
|
+
<button @click="initMfaSetup">Enable MFA</button>
|
|
188
|
+
<div v-if="qrCodeSvg" v-html="qrCodeSvg" />
|
|
189
|
+
<p v-if="secret">Secret: {{ secret }}</p>
|
|
190
|
+
<input v-model="verifyCode" placeholder="6-digit code" />
|
|
191
|
+
<button @click="confirmSetup">Verify</button>
|
|
192
|
+
<ul v-if="backupCodes.length">
|
|
193
|
+
<li v-for="c in backupCodes" :key="c">{{ c }}</li>
|
|
194
|
+
</ul>
|
|
195
|
+
</div>
|
|
64
196
|
</template>
|
|
65
197
|
|
|
66
198
|
<script setup lang="ts">
|
|
67
|
-
import {
|
|
199
|
+
import { ref } from 'vue'
|
|
200
|
+
import { useAuthon } from '@authon/vue'
|
|
201
|
+
|
|
202
|
+
const { client } = useAuthon()
|
|
203
|
+
const qrCodeSvg = ref('')
|
|
204
|
+
const secret = ref('')
|
|
205
|
+
const backupCodes = ref<string[]>([])
|
|
206
|
+
const verifyCode = ref('')
|
|
207
|
+
|
|
208
|
+
async function initMfaSetup() {
|
|
209
|
+
const res = await client!.setupMfa()
|
|
210
|
+
qrCodeSvg.value = res.qrCodeSvg // inline SVG for authenticator app
|
|
211
|
+
secret.value = res.secret
|
|
212
|
+
backupCodes.value = res.backupCodes
|
|
213
|
+
}
|
|
68
214
|
|
|
69
|
-
|
|
215
|
+
async function confirmSetup() {
|
|
216
|
+
await client!.verifyMfaSetup(verifyCode.value)
|
|
217
|
+
alert('MFA enabled successfully')
|
|
218
|
+
}
|
|
70
219
|
</script>
|
|
71
220
|
```
|
|
72
221
|
|
|
73
|
-
|
|
222
|
+
### MFA verification on sign-in
|
|
74
223
|
|
|
75
|
-
|
|
224
|
+
```vue
|
|
225
|
+
<script setup lang="ts">
|
|
226
|
+
import { ref } from 'vue'
|
|
227
|
+
import { useAuthon } from '@authon/vue'
|
|
228
|
+
import { AuthonMfaRequiredError } from '@authon/js'
|
|
76
229
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
})
|
|
230
|
+
const { client } = useAuthon()
|
|
231
|
+
const mfaToken = ref('')
|
|
232
|
+
const totpCode = ref('')
|
|
233
|
+
|
|
234
|
+
async function signIn(email: string, password: string) {
|
|
235
|
+
try {
|
|
236
|
+
await client!.signInWithEmail(email, password)
|
|
237
|
+
} catch (e) {
|
|
238
|
+
if (e instanceof AuthonMfaRequiredError) {
|
|
239
|
+
mfaToken.value = e.mfaToken // show TOTP input
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function verifyMfa() {
|
|
245
|
+
await client!.verifyMfa(mfaToken.value, totpCode.value)
|
|
246
|
+
}
|
|
247
|
+
</script>
|
|
85
248
|
```
|
|
86
249
|
|
|
87
|
-
###
|
|
250
|
+
### Passwordless — magic link
|
|
88
251
|
|
|
89
|
-
|
|
252
|
+
```vue
|
|
253
|
+
<script setup lang="ts">
|
|
254
|
+
import { ref } from 'vue'
|
|
255
|
+
import { useAuthon } from '@authon/vue'
|
|
90
256
|
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
client, // Authon instance
|
|
101
|
-
} = useAuthon();
|
|
257
|
+
const { client } = useAuthon()
|
|
258
|
+
const email = ref('')
|
|
259
|
+
const sent = ref(false)
|
|
260
|
+
|
|
261
|
+
async function sendMagicLink() {
|
|
262
|
+
await client!.sendMagicLink(email.value)
|
|
263
|
+
sent.value = true
|
|
264
|
+
}
|
|
265
|
+
</script>
|
|
102
266
|
```
|
|
103
267
|
|
|
104
|
-
|
|
268
|
+
### Passwordless — email OTP
|
|
105
269
|
|
|
106
|
-
```
|
|
107
|
-
|
|
270
|
+
```vue
|
|
271
|
+
<script setup lang="ts">
|
|
272
|
+
import { ref } from 'vue'
|
|
273
|
+
import { useAuthon } from '@authon/vue'
|
|
274
|
+
|
|
275
|
+
const { client } = useAuthon()
|
|
276
|
+
const email = ref('')
|
|
277
|
+
const otp = ref('')
|
|
278
|
+
const step = ref<'email' | 'verify'>('email')
|
|
279
|
+
|
|
280
|
+
async function sendOtp() {
|
|
281
|
+
await client!.sendEmailOtp(email.value)
|
|
282
|
+
step.value = 'verify'
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function verifyOtp() {
|
|
286
|
+
const user = await client!.verifyPasswordless({ email: email.value, code: otp.value })
|
|
287
|
+
console.log('Signed in as:', user.email)
|
|
288
|
+
}
|
|
289
|
+
</script>
|
|
108
290
|
```
|
|
109
291
|
|
|
110
|
-
###
|
|
292
|
+
### Passkeys
|
|
111
293
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
294
|
+
```vue
|
|
295
|
+
<script setup lang="ts">
|
|
296
|
+
import { useAuthon } from '@authon/vue'
|
|
297
|
+
|
|
298
|
+
const { client } = useAuthon()
|
|
299
|
+
|
|
300
|
+
// Register a new passkey (user must be signed in)
|
|
301
|
+
async function registerPasskey() {
|
|
302
|
+
const credential = await client!.registerPasskey('My MacBook')
|
|
303
|
+
console.log('Registered passkey:', credential.id)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Authenticate with an existing passkey
|
|
307
|
+
async function loginWithPasskey() {
|
|
308
|
+
const user = await client!.authenticateWithPasskey()
|
|
309
|
+
console.log('Signed in as:', user.email)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// List all registered passkeys
|
|
313
|
+
async function listPasskeys() {
|
|
314
|
+
const keys = await client!.listPasskeys()
|
|
315
|
+
console.log(keys)
|
|
316
|
+
}
|
|
317
|
+
</script>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Web3 wallet authentication
|
|
321
|
+
|
|
322
|
+
```vue
|
|
323
|
+
<script setup lang="ts">
|
|
324
|
+
import { useAuthon } from '@authon/vue'
|
|
325
|
+
|
|
326
|
+
const { client } = useAuthon()
|
|
327
|
+
|
|
328
|
+
async function signInWithWallet() {
|
|
329
|
+
const address = '0xYourWalletAddress'
|
|
330
|
+
|
|
331
|
+
// 1. Get a nonce + signable message from Authon
|
|
332
|
+
const { nonce, message } = await client!.web3GetNonce(address, 'evm', 'metamask')
|
|
333
|
+
|
|
334
|
+
// 2. Sign the message with the wallet
|
|
335
|
+
const signature = await window.ethereum.request({
|
|
336
|
+
method: 'personal_sign',
|
|
337
|
+
params: [message, address],
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
// 3. Verify the signature and sign in
|
|
341
|
+
const user = await client!.web3Verify(message, signature, address, 'evm', 'metamask')
|
|
342
|
+
console.log('Signed in as:', user.email)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async function listLinkedWallets() {
|
|
346
|
+
const wallets = await client!.listWallets()
|
|
347
|
+
console.log(wallets)
|
|
348
|
+
}
|
|
349
|
+
</script>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Profile update
|
|
353
|
+
|
|
354
|
+
```vue
|
|
355
|
+
<script setup lang="ts">
|
|
356
|
+
import { useAuthon } from '@authon/vue'
|
|
357
|
+
|
|
358
|
+
const { client } = useAuthon()
|
|
359
|
+
|
|
360
|
+
async function saveProfile() {
|
|
361
|
+
const updated = await client!.updateProfile({
|
|
362
|
+
displayName: 'Jane Doe',
|
|
363
|
+
avatarUrl: 'https://example.com/avatar.png',
|
|
364
|
+
phone: '+1234567890',
|
|
365
|
+
publicMetadata: { role: 'admin' },
|
|
366
|
+
})
|
|
367
|
+
console.log('Updated user:', updated)
|
|
368
|
+
}
|
|
369
|
+
</script>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Session management
|
|
373
|
+
|
|
374
|
+
```vue
|
|
375
|
+
<template>
|
|
376
|
+
<ul>
|
|
377
|
+
<li v-for="session in sessions" :key="session.id">
|
|
378
|
+
{{ session.userAgent }} — {{ session.createdAt }}
|
|
379
|
+
<button @click="revoke(session.id)">Revoke</button>
|
|
380
|
+
</li>
|
|
381
|
+
</ul>
|
|
382
|
+
</template>
|
|
383
|
+
|
|
384
|
+
<script setup lang="ts">
|
|
385
|
+
import { ref, onMounted } from 'vue'
|
|
386
|
+
import { useAuthon } from '@authon/vue'
|
|
387
|
+
import type { SessionInfo } from '@authon/shared'
|
|
388
|
+
|
|
389
|
+
const { client } = useAuthon()
|
|
390
|
+
const sessions = ref<SessionInfo[]>([])
|
|
391
|
+
|
|
392
|
+
onMounted(async () => {
|
|
393
|
+
sessions.value = await client!.listSessions()
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
async function revoke(sessionId: string) {
|
|
397
|
+
await client!.revokeSession(sessionId)
|
|
398
|
+
sessions.value = sessions.value.filter(s => s.id !== sessionId)
|
|
399
|
+
}
|
|
400
|
+
</script>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## Social Buttons
|
|
404
|
+
|
|
405
|
+
`<AuthonSocialButtons>` fetches the enabled OAuth providers from your Authon dashboard and renders branded buttons automatically.
|
|
406
|
+
|
|
407
|
+
**Props:**
|
|
408
|
+
|
|
409
|
+
| Prop | Type | Default | Description |
|
|
410
|
+
|---|---|---|---|
|
|
411
|
+
| `compact` | `boolean` | `false` | Icon-only square buttons in a row |
|
|
412
|
+
| `gap` | `number` | `10` / `12` | Gap between buttons in px |
|
|
413
|
+
| `labels` | `Record<provider, string>` | — | Override button labels per provider |
|
|
414
|
+
| `onSuccess` | `() => void` | — | Called after successful OAuth sign-in |
|
|
415
|
+
| `onError` | `(error: Error) => void` | — | Called on OAuth error |
|
|
416
|
+
|
|
417
|
+
For a single provider button:
|
|
418
|
+
|
|
419
|
+
```vue
|
|
420
|
+
<template>
|
|
421
|
+
<AuthonSocialButton
|
|
422
|
+
provider="github"
|
|
423
|
+
:onClick="handleClick"
|
|
424
|
+
:loading="isLoading"
|
|
425
|
+
:compact="false"
|
|
426
|
+
/>
|
|
427
|
+
</template>
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**`AuthonSocialButton` props:**
|
|
431
|
+
|
|
432
|
+
| Prop | Type | Default |
|
|
433
|
+
|---|---|---|
|
|
434
|
+
| `provider` | `OAuthProviderType` | required |
|
|
435
|
+
| `onClick` | `(provider) => void` | required |
|
|
436
|
+
| `loading` | `boolean` | `false` |
|
|
437
|
+
| `disabled` | `boolean` | `false` |
|
|
438
|
+
| `label` | `string` | `'Continue with {Name}'` |
|
|
439
|
+
| `compact` | `boolean` | `false` |
|
|
440
|
+
| `borderRadius` | `number` | `10` |
|
|
441
|
+
| `height` | `number` | `48` |
|
|
442
|
+
| `size` | `number` | `48` (compact mode) |
|
|
443
|
+
|
|
444
|
+
## Plugin options
|
|
445
|
+
|
|
446
|
+
| Option | Type | Default | Description |
|
|
447
|
+
|---|---|---|---|
|
|
448
|
+
| `publishableKey` | `string` | required | Your Authon publishable key |
|
|
449
|
+
| `config.theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | UI theme |
|
|
450
|
+
| `config.locale` | `string` | `'en'` | Language code |
|
|
451
|
+
| `config.apiUrl` | `string` | `'https://api.authon.dev'` | Custom API base URL |
|
|
452
|
+
| `config.mode` | `'popup' \| 'embedded'` | `'popup'` | Modal display mode |
|
|
453
|
+
| `config.appearance` | `Partial<BrandingConfig>` | — | Override branding colors and logo |
|
|
454
|
+
|
|
455
|
+
## TypeScript
|
|
456
|
+
|
|
457
|
+
Types are re-exported from `@authon/shared`:
|
|
458
|
+
|
|
459
|
+
```ts
|
|
460
|
+
import type { AuthonUser, SessionInfo, PasskeyCredential, Web3Wallet } from '@authon/shared'
|
|
461
|
+
import type { AuthonState, AuthonPluginOptions } from '@authon/vue'
|
|
462
|
+
```
|
|
118
463
|
|
|
119
464
|
## Documentation
|
|
120
465
|
|