@authon/vue 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,469 +2,214 @@
2
2
 
3
3
  # @authon/vue
4
4
 
5
- Vue 3 Composition API integration for [Authon](https://authon.dev)plugin setup, composables, and pre-built components.
5
+ > Drop-in Vue 3 authentication with composables and componentsself-hosted Clerk alternative, Auth0 alternative
6
6
 
7
- ## Install
7
+ [![npm version](https://img.shields.io/npm/v/@authon/vue?color=6d28d9)](https://www.npmjs.com/package/@authon/vue)
8
+ [![License](https://img.shields.io/badge/license-MIT-blue)](../../LICENSE)
8
9
 
9
- ```bash
10
- npm install @authon/vue @authon/js
11
- ```
10
+ ## Prerequisites
12
11
 
13
- Requires `vue >= 3.3.0`.
12
+ Before installing the SDK, create an Authon project and get your API keys:
14
13
 
15
- ## Setup
14
+ 1. **Create a project** at [Authon Dashboard](https://authon.dev/dashboard/overview)
15
+ - Click "Create Project" and enter your app name
16
+ - Select the authentication methods you want (Email/Password, OAuth providers, etc.)
16
17
 
17
- Register the plugin in `main.ts`:
18
-
19
- ```ts
20
- import { createApp } from 'vue'
21
- import { createAuthon } from '@authon/vue'
22
- import App from './App.vue'
23
-
24
- const app = createApp(App)
25
-
26
- app.use(createAuthon({
27
- publishableKey: 'pk_live_...',
28
- config: {
29
- theme: 'auto',
30
- locale: 'en',
31
- },
32
- }))
33
-
34
- app.mount('#app')
35
- ```
18
+ 2. **Get your API keys** from Project Settings → API Keys
19
+ - **Publishable Key** (`pk_live_...` or `pk_test_...`) — safe to use in client-side code
20
+ - **Secret Key** (`sk_live_...` or `sk_test_...`) — server-side only, never expose to clients
36
21
 
37
- ## Composables
22
+ 3. **Configure OAuth providers** (optional) in Project Settings → OAuth
23
+ - Add Google, Apple, GitHub, etc. with their respective Client ID and Secret
24
+ - Set the redirect URL to `https://api.authon.dev/v1/auth/oauth/redirect`
38
25
 
39
- ### `useAuthon()`
26
+ > **Test vs Live keys:** Use `pk_test_...` during development. Switch to `pk_live_...` before deploying to production. Test keys use a sandbox environment with no rate limits.
40
27
 
41
- Returns the full auth state and helper methods. The `client` property exposes the underlying `@authon/js` `Authon` instance for all advanced operations.
28
+ ## Install
42
29
 
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
+ ```bash
31
+ npm install @authon/vue
56
32
  ```
57
33
 
58
- ### `useUser()`
59
-
60
- Returns only the user and loading state as computed refs.
34
+ ## Quick Start
61
35
 
62
36
  ```ts
63
- import { useUser } from '@authon/vue'
37
+ // src/main.ts
38
+ import { createApp } from 'vue';
39
+ import { createAuthon } from '@authon/vue';
40
+ import App from './App.vue';
64
41
 
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'
42
+ const app = createApp(App);
43
+ app.use(createAuthon({
44
+ publishableKey: 'pk_live_YOUR_PUBLISHABLE_KEY',
45
+ config: { apiUrl: 'https://your-authon-server.com' },
46
+ }));
47
+ app.mount('#app');
82
48
  ```
83
49
 
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
97
-
98
50
  ```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
-
51
+ <!-- src/App.vue -->
111
52
  <script setup lang="ts">
112
- import { useAuthon, AuthonSignedIn, AuthonSignedOut, AuthonUserButton } from '@authon/vue'
113
-
114
- const { user, signOut } = useAuthon()
53
+ import { useAuthon, useUser, AuthonSignedIn, AuthonSignedOut, AuthonUserButton } from '@authon/vue';
54
+ const { openSignIn, signOut } = useAuthon();
55
+ const { user } = useUser();
115
56
  </script>
116
- ```
117
57
 
118
- ### Email + password sign-in
119
-
120
- ```vue
121
58
  <template>
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>
59
+ <AuthonSignedOut>
60
+ <button @click="openSignIn()">Sign In</button>
61
+ </AuthonSignedOut>
62
+ <AuthonSignedIn>
63
+ <p>Welcome, {{ user?.email }}</p>
64
+ <AuthonUserButton />
65
+ <button @click="signOut()">Sign Out</button>
66
+ </AuthonSignedIn>
128
67
  </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
68
  ```
156
69
 
157
- ### OAuth sign-in
70
+ ## Common Tasks
71
+
72
+ ### Add Google OAuth Login
158
73
 
159
74
  ```vue
160
75
  <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
- }
76
+ import { useAuthon } from '@authon/vue';
77
+ const { client } = useAuthon();
169
78
  </script>
170
79
 
171
80
  <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
- />
179
- </template>
180
- ```
181
-
182
- ### MFA setup
183
-
184
- ```vue
185
- <template>
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>
81
+ <button @click="client?.signInWithOAuth('google')">Sign in with Google</button>
196
82
  </template>
197
-
198
- <script setup lang="ts">
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
- }
214
-
215
- async function confirmSetup() {
216
- await client!.verifyMfaSetup(verifyCode.value)
217
- alert('MFA enabled successfully')
218
- }
219
- </script>
220
83
  ```
221
84
 
222
- ### MFA verification on sign-in
85
+ ### Protect a Route
223
86
 
224
- ```vue
225
- <script setup lang="ts">
226
- import { ref } from 'vue'
227
- import { useAuthon } from '@authon/vue'
228
- import { AuthonMfaRequiredError } from '@authon/js'
229
-
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
- }
87
+ ```ts
88
+ // src/router/index.ts
89
+ import { createRouter, createWebHistory } from 'vue-router';
90
+
91
+ const router = createRouter({
92
+ history: createWebHistory(),
93
+ routes: [
94
+ { path: '/', component: () => import('../views/Home.vue') },
95
+ { path: '/dashboard', component: () => import('../views/Dashboard.vue'), meta: { requiresAuth: true } },
96
+ { path: '/sign-in', component: () => import('../views/SignIn.vue') },
97
+ ],
98
+ });
99
+
100
+ router.beforeEach((to, _from, next) => {
101
+ if (to.meta.requiresAuth) {
102
+ const authon = router.app?.config.globalProperties.$authon;
103
+ if (!authon?.isSignedIn) return next({ path: '/sign-in', query: { redirect: to.fullPath } });
241
104
  }
242
- }
105
+ next();
106
+ });
243
107
 
244
- async function verifyMfa() {
245
- await client!.verifyMfa(mfaToken.value, totpCode.value)
246
- }
247
- </script>
108
+ export default router;
248
109
  ```
249
110
 
250
- ### Passwordless magic link
111
+ ### Get Current User
251
112
 
252
113
  ```vue
253
114
  <script setup lang="ts">
254
- import { ref } from 'vue'
255
- import { useAuthon } from '@authon/vue'
256
-
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
- }
115
+ import { useUser } from '@authon/vue';
116
+ const { user, isLoading } = useUser();
265
117
  </script>
266
- ```
267
-
268
- ### Passwordless — email OTP
269
-
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
118
 
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>
119
+ <template>
120
+ <p v-if="isLoading">Loading...</p>
121
+ <div v-else-if="user">
122
+ <p>Email: {{ user.email }}</p>
123
+ <p>Name: {{ user.displayName }}</p>
124
+ </div>
125
+ <p v-else>Not signed in</p>
126
+ </template>
290
127
  ```
291
128
 
292
- ### Passkeys
129
+ ### Add Email/Password Auth
293
130
 
294
131
  ```vue
295
132
  <script setup lang="ts">
296
- import { useAuthon } from '@authon/vue'
297
-
298
- const { client } = useAuthon()
133
+ import { ref } from 'vue';
134
+ import { useAuthon } from '@authon/vue';
135
+ const { client } = useAuthon();
136
+ const email = ref('');
137
+ const password = ref('');
299
138
 
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)
139
+ async function handleSignIn() {
140
+ await client?.signInWithEmail(email.value, password.value);
316
141
  }
317
142
  </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
143
 
345
- async function listLinkedWallets() {
346
- const wallets = await client!.listWallets()
347
- console.log(wallets)
348
- }
349
- </script>
144
+ <template>
145
+ <form @submit.prevent="handleSignIn">
146
+ <input v-model="email" type="email" placeholder="Email" />
147
+ <input v-model="password" type="password" placeholder="Password" />
148
+ <button type="submit">Sign In</button>
149
+ </form>
150
+ </template>
350
151
  ```
351
152
 
352
- ### Profile update
153
+ ### Handle Sign Out
353
154
 
354
155
  ```vue
355
156
  <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
- }
157
+ import { useAuthon } from '@authon/vue';
158
+ const { signOut } = useAuthon();
369
159
  </script>
370
- ```
371
160
 
372
- ### Session management
373
-
374
- ```vue
375
161
  <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>
162
+ <button @click="signOut()">Sign Out</button>
382
163
  </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
164
  ```
402
165
 
403
- ## Social Buttons
404
-
405
- `<AuthonSocialButtons>` fetches the enabled OAuth providers from your Authon dashboard and renders branded buttons automatically.
166
+ ## Environment Variables
406
167
 
407
- **Props:**
168
+ | Variable | Required | Description |
169
+ |----------|----------|-------------|
170
+ | `VITE_AUTHON_API_URL` | Yes | Your Authon server URL |
171
+ | `VITE_AUTHON_PUBLISHABLE_KEY` | Yes | Project publishable key |
408
172
 
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 |
173
+ ## API Reference
416
174
 
417
- For a single provider button:
175
+ ### Plugin
418
176
 
419
- ```vue
420
- <template>
421
- <AuthonSocialButton
422
- provider="github"
423
- :onClick="handleClick"
424
- :loading="isLoading"
425
- :compact="false"
426
- />
427
- </template>
177
+ ```ts
178
+ createAuthon({ publishableKey: string, config?: AuthonConfig })
428
179
  ```
429
180
 
430
- **`AuthonSocialButton` props:**
181
+ ### Composables
431
182
 
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) |
183
+ | Composable | Returns |
184
+ |------------|---------|
185
+ | `useAuthon()` | `{ isSignedIn, isLoading, user, client, signOut, openSignIn, openSignUp, getToken }` |
186
+ | `useUser()` | `{ user: ComputedRef, isLoading: ComputedRef }` |
187
+ | `useAuthonWeb3()` | Web3 wallet auth methods |
188
+ | `useAuthonPasswordless()` | Magic link and OTP methods |
189
+ | `useAuthonPasskeys()` | Passkey registration and auth |
443
190
 
444
- ## Plugin options
191
+ ### Components
445
192
 
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
- ```
463
-
464
- ## Documentation
465
-
466
- [authon.dev/docs](https://authon.dev/docs)
193
+ | Component | Description |
194
+ |-----------|-------------|
195
+ | `<AuthonSignIn>` | Sign-in form (`mode="popup"` or `"embedded"`) |
196
+ | `<AuthonSignUp>` | Sign-up form |
197
+ | `<AuthonUserButton>` | Avatar dropdown with sign-out |
198
+ | `<AuthonSignedIn>` | Slot rendered only when signed in |
199
+ | `<AuthonSignedOut>` | Slot rendered only when signed out |
200
+ | `<AuthonSocialButtons>` | All enabled OAuth provider buttons |
201
+
202
+ ## Comparison
203
+
204
+ | Feature | Authon | Clerk | Auth.js |
205
+ |---------|--------|-------|---------|
206
+ | Self-hosted | Yes | No | Partial |
207
+ | Pricing | Free | $25/mo+ | Free |
208
+ | OAuth providers | 10+ | 20+ | 80+ |
209
+ | ShadowDOM modal | Yes | No | No |
210
+ | MFA/Passkeys | Yes | Yes | Plugin |
211
+ | Web3 auth | Yes | No | No |
467
212
 
468
213
  ## License
469
214
 
470
- [MIT](../../LICENSE)
215
+ MIT