@drmhse/authos-vue 0.1.3 → 0.1.5

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
@@ -5,22 +5,13 @@
5
5
 
6
6
  Vue 3 adapter for [AuthOS](https://authos.dev) - the multi-tenant authentication platform. Provides Vue composables, components, and Nuxt module.
7
7
 
8
- ## Installation
8
+ ## Quick Start (5 minutes)
9
9
 
10
10
  ```bash
11
11
  npm install @drmhse/authos-vue
12
12
  ```
13
13
 
14
- Peer dependencies:
15
- ```bash
16
- npm install vue
17
- ```
18
-
19
- ## Quick Start
20
-
21
- ### Vue App
22
-
23
- Install the plugin and use the composables/components:
14
+ Install the plugin and you're ready to go:
24
15
 
25
16
  ```ts
26
17
  // main.ts
@@ -31,43 +22,122 @@ import App from './App.vue';
31
22
  const app = createApp(App);
32
23
 
33
24
  app.use(createAuthOS({
34
- // Required: AuthOS API URL (e.g., https://sso.example.com)
35
- baseUrl: 'https://sso.example.com'
25
+ baseURL: 'https://sso.example.com',
36
26
  }));
37
27
 
38
28
  app.mount('#app');
39
29
  ```
40
30
 
41
- ### Nuxt App
31
+ ```vue
32
+ <!-- App.vue -->
33
+ <script setup>
34
+ import { SignIn, SignedIn, SignedOut, UserButton } from '@drmhse/authos-vue';
35
+ </script>
36
+
37
+ <template>
38
+ <SignedOut>
39
+ <SignIn @success="console.log('Welcome!')" />
40
+ </SignedOut>
41
+ <SignedIn>
42
+ <UserButton />
43
+ <p>You're signed in!</p>
44
+ </SignedIn>
45
+ </template>
46
+ ```
47
+
48
+ That's it. You now have:
49
+ - Email/password authentication with MFA support
50
+ - Automatic session management
51
+ - User dropdown with logout
52
+ - Conditional rendering based on auth state
53
+
54
+ ## Usage Modes
55
+
56
+ AuthOS supports two usage modes:
57
+
58
+ ### Platform-Level Access
59
+
60
+ For platform owners and administrators, use without `org`/`service`:
61
+
62
+ ```ts
63
+ app.use(createAuthOS({
64
+ baseURL: 'https://sso.example.com',
65
+ }));
66
+ ```
67
+
68
+ ### Multi-Tenant Access (Organizations)
69
+
70
+ For tenant applications with OAuth support, add `org` and `service`:
42
71
 
43
72
  ```ts
44
- // nuxt.config.ts
45
- export default defineNuxtConfig({
46
- modules: ['@drmhse/authos-vue/nuxt']
73
+ app.use(createAuthOS({
74
+ baseURL: 'https://sso.example.com',
75
+ org: 'acme-corp', // Organization slug
76
+ service: 'main-app', // Service slug
77
+ }));
78
+ ```
79
+
80
+ ### Using an Existing Client
81
+
82
+ For advanced use cases, you can pass a pre-configured `SsoClient`:
83
+
84
+ ```ts
85
+ import { SsoClient } from '@drmhse/sso-sdk';
86
+ import { createAuthOS } from '@drmhse/authos-vue';
87
+
88
+ // Create client with custom configuration
89
+ const client = new SsoClient({
90
+ baseURL: 'https://sso.example.com',
91
+ storage: customStorage,
47
92
  });
93
+
94
+ app.use(createAuthOS({ client }));
95
+ ```
96
+
97
+ ## OAuth / Social Login
98
+
99
+ To use OAuth providers (GitHub, Google, Microsoft), configure your organization and service (see Multi-Tenant Access above):
100
+
101
+ ```ts
102
+ app.use(createAuthOS({
103
+ baseURL: 'https://sso.example.com',
104
+ org: 'my-org', // Your organization slug
105
+ service: 'my-app', // Your service slug
106
+ redirectUri: 'https://app.example.com/callback', // Optional
107
+ }));
108
+ ```
109
+
110
+ Then use individual OAuth buttons:
111
+
112
+ ```vue
113
+ <script setup>
114
+ import { OAuthButton } from '@drmhse/authos-vue';
115
+ </script>
116
+
117
+ <template>
118
+ <OAuthButton provider="github" />
119
+ <OAuthButton provider="google">Sign in with Google</OAuthButton>
120
+ <OAuthButton provider="microsoft" />
121
+ </template>
48
122
  ```
49
123
 
50
124
  ## Components
51
125
 
52
126
  ### SignIn
53
127
 
54
- Pre-built sign-in form with email/password and OAuth provider buttons.
128
+ Complete sign-in form with email/password authentication. Supports scoped slots for custom UI.
55
129
 
56
130
  ```vue
57
- <script setup lang="ts">
131
+ <script setup>
58
132
  import { SignIn } from '@drmhse/authos-vue';
59
133
 
60
134
  function handleSuccess() {
61
- console.log('Logged in!');
62
- }
63
-
64
- function handleError(err: Error) {
65
- console.error(err);
135
+ router.push('/dashboard');
66
136
  }
67
137
  </script>
68
138
 
69
139
  <template>
70
- <SignIn @success="handleSuccess" @error="handleError" />
140
+ <SignIn @success="handleSuccess" @error="console.error" />
71
141
  </template>
72
142
  ```
73
143
 
@@ -77,52 +147,74 @@ function handleError(err: Error) {
77
147
  | `@success` | - | Fired after successful login |
78
148
  | `@error` | `Error` | Fired on login error |
79
149
 
150
+ **Scoped Slot:**
151
+ ```vue
152
+ <SignIn v-slot="{ email, password, step, error, isSubmitting, updateEmail, updatePassword, submit }">
153
+ <form @submit.prevent="submit">
154
+ <input :value="email" @input="e => updateEmail(e.target.value)" />
155
+ <input :value="password" type="password" @input="e => updatePassword(e.target.value)" />
156
+ <button :disabled="isSubmitting">Sign In</button>
157
+ <p v-if="error">{{ error }}</p>
158
+ </form>
159
+ </SignIn>
160
+ ```
161
+
80
162
  ### SignUp
81
163
 
82
164
  Registration form for new users.
83
165
 
84
166
  ```vue
85
- <script setup lang="ts">
86
- import { SignUp } from '@drmhse/authos-vue';
87
- </script>
167
+ <SignUp @success="console.log('Check your email!')" @error="console.error" />
168
+ ```
88
169
 
89
- <template>
90
- <SignUp />
91
- </template>
170
+ ### MagicLinkSignIn
171
+
172
+ Sign-in component for Magic Links (passwordless).
173
+
174
+ ```vue
175
+ <MagicLinkSignIn @success="console.log('Magic link sent!')" />
92
176
  ```
93
177
 
94
- **Events:** Same as `SignIn`
178
+ ### PasskeySignIn
95
179
 
96
- ### UserButton
180
+ Sign-in component for Passkeys (WebAuthn).
97
181
 
98
- User menu button that shows avatar/name and dropdown with profile/signout.
182
+ ```vue
183
+ <PasskeySignIn @success="console.log('Authenticated!')" />
184
+ ```
185
+
186
+ ### SignedIn / SignedOut
187
+
188
+ Conditional rendering based on authentication state. Inspired by Clerk's API.
99
189
 
100
190
  ```vue
101
- <script setup lang="ts">
102
- import { UserButton } from '@drmhse/authos-vue';
103
- </script>
191
+ <SignedIn>
192
+ <!-- Only shown when user is logged in -->
193
+ <UserButton />
194
+ </SignedIn>
195
+
196
+ <SignedOut>
197
+ <!-- Only shown when user is logged out -->
198
+ <SignIn />
199
+ </SignedOut>
200
+ ```
104
201
 
105
- <template>
106
- <header>
107
- <UserButton />
108
- </header>
109
- </template>
202
+ ### UserButton
203
+
204
+ User menu button with avatar and logout.
205
+
206
+ ```vue
207
+ <UserButton @logout="router.push('/')" />
110
208
  ```
111
209
 
112
210
  ### OrganizationSwitcher
113
211
 
114
- Dropdown to switch between organizations (for multi-tenant users).
212
+ Dropdown to switch between organizations.
115
213
 
116
- ```vue
117
- <script setup lang="ts">
118
- import { OrganizationSwitcher } from '@drmhse/authos-vue';
119
- </script>
214
+ When switching organizations, the SDK automatically issues new JWT tokens with the new organization context, enabling seamless multi-tenant switching without re-authentication.
120
215
 
121
- <template>
122
- <aside>
123
- <OrganizationSwitcher />
124
- </aside>
125
- </template>
216
+ ```vue
217
+ <OrganizationSwitcher @switch="org => console.log('Switched to:', org.name)" />
126
218
  ```
127
219
 
128
220
  ### Protect
@@ -130,68 +222,58 @@ import { OrganizationSwitcher } from '@drmhse/authos-vue';
130
222
  Conditional rendering based on user permissions.
131
223
 
132
224
  ```vue
133
- <script setup lang="ts">
134
- import { Protect } from '@drmhse/authos-vue';
135
- </script>
225
+ <Protect permission="admin:access">
226
+ <template #default>
227
+ <AdminDashboard />
228
+ </template>
229
+ <template #fallback>
230
+ <p>Access denied</p>
231
+ </template>
232
+ </Protect>
233
+ ```
136
234
 
137
- <template>
138
- <Protect permission="admin:access">
139
- <template #default>
140
- <AdminDashboard />
141
- </template>
142
- <template #fallback>
143
- <p>Access denied. Admins only.</p>
144
- </template>
145
- </Protect>
146
- </template>
235
+ ### OAuthButton
236
+
237
+ Individual OAuth provider button. Requires `org` and `service` in plugin options.
238
+
239
+ ```vue
240
+ <OAuthButton provider="github" />
241
+ <OAuthButton provider="google">Continue with Google</OAuthButton>
147
242
  ```
148
243
 
149
- **Props:**
150
- | Prop | Type | Description |
151
- |------|------|-------------|
152
- | `permission` | `string` | Required permission to access content |
153
- | `fallback` | `slot` | Shown when user lacks permission |
154
- | `default` | `slot` | Protected content |
244
+ **Scoped Slot:**
245
+ ```vue
246
+ <OAuthButton provider="github" v-slot="{ providerName, isConfigured, handleClick }">
247
+ <button @click="handleClick" :disabled="!isConfigured">
248
+ Continue with {{ providerName }}
249
+ </button>
250
+ </OAuthButton>
251
+ ```
155
252
 
156
253
  ## Composables
157
254
 
158
255
  ### useAuthOS
159
256
 
160
- Access the AuthOS client directly.
257
+ Access the AuthOS client, state, and configuration.
161
258
 
162
259
  ```vue
163
- <script setup lang="ts">
260
+ <script setup>
164
261
  import { useAuthOS } from '@drmhse/authos-vue';
165
262
 
166
- const { client, isAuthenticated, isLoading } = useAuthOS();
263
+ const { client, options, isAuthenticated, isLoading } = useAuthOS();
167
264
 
168
265
  async function handleLogout() {
169
266
  await client.auth.logout();
170
267
  }
171
268
  </script>
172
-
173
- <template>
174
- <div v-if="isLoading">Loading...</div>
175
- <div v-else-if="!isAuthenticated">Please log in</div>
176
- <div v-else>
177
- <button @click="handleLogout">Logout</button>
178
- </div>
179
- </template>
180
269
  ```
181
270
 
182
- **Returns:**
183
- | Property | Type | Description |
184
- |----------|------|-------------|
185
- | `client` | `SsoClient` | The AuthOS SDK client |
186
- | `isLoading` | `Ref<boolean>` | True while checking auth state |
187
- | `isAuthenticated` | `Ref<boolean>` | True if user is logged in |
188
-
189
271
  ### useUser
190
272
 
191
273
  Get the current user's profile.
192
274
 
193
275
  ```vue
194
- <script setup lang="ts">
276
+ <script setup>
195
277
  import { useUser } from '@drmhse/authos-vue';
196
278
 
197
279
  const { user, isLoading } = useUser();
@@ -203,73 +285,64 @@ const { user, isLoading } = useUser();
203
285
  </template>
204
286
  ```
205
287
 
206
- **Returns:**
207
- | Property | Type | Description |
208
- |----------|------|-------------|
209
- | `user` | `Ref<UserProfile | null>` | Current user profile |
210
- | `isLoading` | `Ref<boolean>` | True while checking auth state |
211
-
212
288
  ### useOrganization
213
289
 
214
- Get the current organization and list of organizations.
290
+ Get and switch between organizations.
215
291
 
216
292
  ```vue
217
- <script setup lang="ts">
293
+ <script setup>
218
294
  import { useOrganization } from '@drmhse/authos-vue';
219
295
 
220
- const { currentOrganization, organizations, switchOrganization } = useOrganization();
296
+ const { currentOrganization, organizations, switchOrganization, isSwitching } = useOrganization();
221
297
  </script>
222
298
 
223
299
  <template>
224
- <div>
225
- <h3>{{ currentOrganization?.name }}</h3>
226
- <ul>
227
- <li v-for="org in organizations" :key="org.id">
228
- {{ org.name }}
229
- </li>
230
- </ul>
231
- </div>
300
+ <h3>{{ currentOrganization?.name }}</h3>
301
+ <ul>
302
+ <li v-for="org in organizations" :key="org.id">
303
+ <button @click="switchOrganization(org.slug)">{{ org.name }}</button>
304
+ </li>
305
+ </ul>
232
306
  </template>
233
307
  ```
234
308
 
235
- **Returns:**
236
- | Property | Type | Description |
237
- |----------|------|-------------|
238
- | `currentOrganization` | `Ref<Organization | null>` | Current organization |
239
- | `organizations` | `Ref<Organization[]>` | All user's organizations |
240
- | `switchOrganization` | `(slug: string) => Promise<void>` | Switch to a different org |
241
- | `isSwitching` | `Ref<boolean>` | True while switching |
242
-
243
- ## Nuxt Module
309
+ ### usePermission / useAnyPermission / useAllPermissions
244
310
 
245
- When using the Nuxt module, the plugin is auto-installed and provides additional server utilities:
311
+ Check user permissions.
246
312
 
247
313
  ```vue
248
- <!-- app.vue -->
249
- <script setup lang="ts">
250
- // Plugin is auto-installed, composables work automatically
251
- import { useUser } from '@drmhse/authos-vue';
314
+ <script setup>
315
+ import { usePermission, useAnyPermission, useAllPermissions } from '@drmhse/authos-vue';
252
316
 
253
- const { user } = useUser();
317
+ const canAccessAdmin = usePermission('admin:access');
318
+ const canReadOrWrite = useAnyPermission(['read:data', 'write:data']);
319
+ const hasFullAccess = useAllPermissions(['read:data', 'write:data', 'delete:data']);
254
320
  </script>
255
321
 
256
322
  <template>
257
- <div v-if="user">
258
- Welcome, {{ user.email }}
259
- </div>
323
+ <button v-if="canAccessAdmin">Admin Panel</button>
260
324
  </template>
261
325
  ```
262
326
 
263
- ### Nuxt Auth Middleware
327
+ ## Nuxt Integration
328
+
329
+ ### Plugin Installation
330
+
331
+ ```ts
332
+ // nuxt.config.ts
333
+ export default defineNuxtConfig({
334
+ modules: ['@drmhse/authos-vue/nuxt'],
335
+ });
336
+ ```
264
337
 
265
- Protect pages with the auth middleware:
338
+ ### Auth Middleware
266
339
 
267
340
  ```ts
268
341
  // middleware/auth.ts
269
342
  import { authMiddleware } from '@drmhse/authos-vue/nuxt';
270
343
 
271
344
  export default authMiddleware({
272
- redirectTo: '/login'
345
+ redirectTo: '/login',
273
346
  });
274
347
  ```
275
348
 
@@ -278,7 +351,7 @@ Then use in your pages:
278
351
  ```vue
279
352
  <script setup>
280
353
  definePageMeta({
281
- middleware: ['auth']
354
+ middleware: ['auth'],
282
355
  });
283
356
  </script>
284
357
 
@@ -299,7 +372,7 @@ export default defineEventHandler(async (event) => {
299
372
  if (!user) {
300
373
  throw createError({
301
374
  statusCode: 401,
302
- message: 'Unauthorized'
375
+ message: 'Unauthorized',
303
376
  });
304
377
  }
305
378
 
@@ -307,30 +380,50 @@ export default defineEventHandler(async (event) => {
307
380
  });
308
381
  ```
309
382
 
310
- ## SsoClient API
311
-
312
- The underlying client from `@drmhse/sso-sdk`. See [SDK docs](https://www.npmjs.com/package/@drmhse/sso-sdk) for full API.
313
-
314
- ```ts
315
- const { client } = useAuthOS();
316
-
317
- // Authentication
318
- await client.auth.login({ email, password });
319
- await client.auth.logout();
320
- await client.auth.register({ email, password, org_slug });
383
+ ## Configuration Reference
384
+
385
+ ### createAuthOS Options
386
+
387
+ | Option | Type | Required | Description |
388
+ |--------|------|----------|-------------|
389
+ | `baseURL` | `string` | ✅ | AuthOS API URL |
390
+ | `org` | `string` | For OAuth | Organization slug for OAuth flows |
391
+ | `service` | `string` | For OAuth | Service slug for OAuth flows |
392
+ | `redirectUri` | `string` | - | OAuth redirect URI (defaults to origin + '/callback') |
393
+ | `afterSignInUrl` | `string` | - | Redirect URL after sign-in |
394
+ | `afterSignUpUrl` | `string` | - | Redirect URL after sign-up |
395
+ | `storage` | `TokenStorage` | - | Custom token storage |
396
+ | `autoRefresh` | `boolean` | - | Auto-refresh expired tokens (default: true) |
397
+ | `initialToken` | `string` | - | SSR token for hydration |
398
+
399
+ ## Styling
400
+
401
+ All components use data attributes for styling hooks:
402
+
403
+ ```css
404
+ [data-authos-signin] { /* Container */ }
405
+ [data-authos-field="email"] { /* Email field wrapper */ }
406
+ [data-authos-field="password"] { /* Password field wrapper */ }
407
+ [data-authos-submit] { /* Submit button */ }
408
+ [data-authos-error] { /* Error message */ }
409
+ [data-authos-oauth] { /* OAuth button */ }
410
+ [data-authos-oauth][data-provider="github"] { /* GitHub button */ }
411
+ [data-authos-divider] { /* "or" divider */ }
412
+ ```
321
413
 
322
- // User
323
- await client.user.getProfile();
324
- await client.user.updateProfile({ name });
325
- await client.user.changePassword({ old, new });
414
+ ## Migration from v1
326
415
 
327
- // Organizations
328
- await client.organizations.list();
329
- await client.organizations.get(slug);
416
+ If you're upgrading from v1, note the following breaking change:
330
417
 
331
- // And more...
418
+ ```diff
419
+ app.use(createAuthOS({
420
+ - baseUrl: 'https://sso.example.com',
421
+ + baseURL: 'https://sso.example.com',
422
+ }));
332
423
  ```
333
424
 
425
+ The `baseUrl` property has been renamed to `baseURL` (uppercase URL) for consistency with the React package and underlying SDK.
426
+
334
427
  ## License
335
428
 
336
429
  MIT © DRM HSE
@@ -6,12 +6,16 @@ var AUTH_OS_INJECTION_KEY = /* @__PURE__ */ Symbol("authOS");
6
6
  function useAuthOS() {
7
7
  const context = inject(AUTH_OS_INJECTION_KEY);
8
8
  if (!context) {
9
+ const defaultOptions = {
10
+ baseURL: "http://localhost:3001"
11
+ };
9
12
  const defaultClient = new SsoClient({
10
- baseURL: "http://localhost:3001",
13
+ baseURL: defaultOptions.baseURL,
11
14
  storage: new MemoryStorage()
12
15
  });
13
16
  return {
14
17
  client: defaultClient,
18
+ options: defaultOptions,
15
19
  isLoading: computed(() => false),
16
20
  isAuthenticated: computed(() => false)
17
21
  };
@@ -20,6 +24,7 @@ function useAuthOS() {
20
24
  const isAuthenticated = computed(() => context.state.isAuthenticated);
21
25
  return {
22
26
  client: context.client,
27
+ options: context.options,
23
28
  isLoading,
24
29
  isAuthenticated
25
30
  };