@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/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { AUTH_OS_INJECTION_KEY, useAuthOS } from './chunk-OGRUDANK.mjs';
2
- export { AUTH_OS_INJECTION_KEY, useAuthOS } from './chunk-OGRUDANK.mjs';
1
+ import { AUTH_OS_INJECTION_KEY, useAuthOS } from './chunk-YED35B36.mjs';
2
+ export { AUTH_OS_INJECTION_KEY, useAuthOS } from './chunk-YED35B36.mjs';
3
3
  import './chunk-6DZX6EAA.mjs';
4
4
  import { defineComponent, reactive, provide, onMounted, onUnmounted, h, ref, computed, inject, nextTick } from 'vue';
5
5
  import { SsoClient, BrowserStorage, MemoryStorage, SsoApiError } from '@drmhse/sso-sdk';
@@ -17,7 +17,7 @@ function createAuthOS(options) {
17
17
  return new MemoryStorage();
18
18
  };
19
19
  const client = new SsoClient({
20
- baseURL: options.baseUrl,
20
+ baseURL: options.baseURL,
21
21
  storage: getStorage(),
22
22
  token: options.initialToken
23
23
  // Pass initial token if provided
@@ -39,7 +39,8 @@ function createAuthOS(options) {
39
39
  });
40
40
  const context = {
41
41
  client,
42
- state
42
+ state,
43
+ options
43
44
  };
44
45
  return {
45
46
  install(app) {
@@ -108,6 +109,11 @@ function useOrganization() {
108
109
  if (!context) return;
109
110
  isSwitching.value = true;
110
111
  try {
112
+ const result = await context.client.organizations.select(slug);
113
+ await context.client.setSession({
114
+ access_token: result.access_token,
115
+ refresh_token: result.refresh_token
116
+ });
111
117
  const orgResponse = await context.client.organizations.get(slug);
112
118
  context.state.currentOrganization = orgResponse;
113
119
  return orgResponse;
@@ -122,13 +128,46 @@ function useOrganization() {
122
128
  isSwitching
123
129
  };
124
130
  }
131
+ function usePermission(permission) {
132
+ const { user } = useUser();
133
+ return computed(() => {
134
+ if (!user.value || !permission) return false;
135
+ return user.value.permissions?.includes(permission) ?? false;
136
+ });
137
+ }
138
+ function useAnyPermission(permissions) {
139
+ const { user } = useUser();
140
+ return computed(() => {
141
+ if (!user.value || permissions.length === 0) return false;
142
+ return permissions.some((p) => user.value?.permissions?.includes(p));
143
+ });
144
+ }
145
+ function useAllPermissions(permissions) {
146
+ const { user } = useUser();
147
+ return computed(() => {
148
+ if (!user.value || permissions.length === 0) return false;
149
+ return permissions.every((p) => user.value?.permissions?.includes(p));
150
+ });
151
+ }
125
152
  var AuthOSProvider = defineComponent({
126
153
  name: "AuthOSProvider",
127
154
  props: {
128
- baseUrl: {
155
+ baseURL: {
129
156
  type: String,
130
157
  required: true
131
158
  },
159
+ org: {
160
+ type: String,
161
+ default: void 0
162
+ },
163
+ service: {
164
+ type: String,
165
+ default: void 0
166
+ },
167
+ redirectUri: {
168
+ type: String,
169
+ default: void 0
170
+ },
132
171
  storage: {
133
172
  type: Object,
134
173
  default: void 0
@@ -145,7 +184,7 @@ var AuthOSProvider = defineComponent({
145
184
  return new MemoryStorage();
146
185
  };
147
186
  const client = props.client ?? new SsoClient({
148
- baseURL: props.baseUrl,
187
+ baseURL: props.baseURL,
149
188
  storage: getStorage()
150
189
  });
151
190
  const state = reactive({
@@ -155,7 +194,13 @@ var AuthOSProvider = defineComponent({
155
194
  currentOrganization: null,
156
195
  organizations: []
157
196
  });
158
- const context = { client, state };
197
+ const options = {
198
+ baseURL: props.baseURL,
199
+ org: props.org,
200
+ service: props.service,
201
+ redirectUri: props.redirectUri
202
+ };
203
+ const context = { client, state, options };
159
204
  provide(AUTH_OS_INJECTION_KEY, context);
160
205
  let unsubscribe;
161
206
  onMounted(() => {
@@ -206,7 +251,7 @@ var SignIn = defineComponent({
206
251
  },
207
252
  emits: ["success", "error"],
208
253
  setup(props, { slots, emit }) {
209
- const { client } = useAuthOS();
254
+ const { client, options } = useAuthOS();
210
255
  const email = ref("");
211
256
  const password = ref("");
212
257
  const mfaCode = ref("");
@@ -221,7 +266,9 @@ var SignIn = defineComponent({
221
266
  if (step.value === "credentials") {
222
267
  const result = await client.auth.login({
223
268
  email: email.value,
224
- password: password.value
269
+ password: password.value,
270
+ org_slug: options.org,
271
+ service_slug: options.service
225
272
  });
226
273
  if (result.expires_in === MFA_PREAUTH_EXPIRY) {
227
274
  preauthToken.value = result.access_token;
@@ -300,6 +347,16 @@ var SignUp = defineComponent({
300
347
  onError: {
301
348
  type: Function,
302
349
  default: void 0
350
+ },
351
+ /** Organization slug for tenant context */
352
+ orgSlug: {
353
+ type: String,
354
+ default: void 0
355
+ },
356
+ /** Service slug for tenant attribution (used with orgSlug) */
357
+ serviceSlug: {
358
+ type: String,
359
+ default: void 0
303
360
  }
304
361
  },
305
362
  emits: ["success", "error"],
@@ -315,7 +372,9 @@ var SignUp = defineComponent({
315
372
  try {
316
373
  await client.auth.register({
317
374
  email: email.value,
318
- password: password.value
375
+ password: password.value,
376
+ org_slug: props.orgSlug,
377
+ service_slug: props.serviceSlug
319
378
  });
320
379
  emit("success");
321
380
  props.onSuccess?.();
@@ -520,5 +579,296 @@ var Protect = defineComponent({
520
579
  };
521
580
  }
522
581
  });
582
+ var PROVIDER_NAMES = {
583
+ github: "GitHub",
584
+ google: "Google",
585
+ microsoft: "Microsoft"
586
+ };
587
+ var OAuthButton = defineComponent({
588
+ name: "OAuthButton",
589
+ props: {
590
+ provider: {
591
+ type: String,
592
+ required: true
593
+ },
594
+ disabled: {
595
+ type: Boolean,
596
+ default: false
597
+ }
598
+ },
599
+ emits: ["redirect"],
600
+ setup(props, { slots, emit }) {
601
+ const { client, options } = useAuthOS();
602
+ const isConfigured = computed(() => !!(options.org && options.service));
603
+ const providerName = computed(() => PROVIDER_NAMES[props.provider]);
604
+ function handleClick() {
605
+ if (!options.org || !options.service) {
606
+ console.error(
607
+ `[AuthOS] OAuth login requires "org" and "service" in createAuthOS options.
608
+ Current options: { org: ${options.org ? `"${options.org}"` : "undefined"}, service: ${options.service ? `"${options.service}"` : "undefined"} }
609
+
610
+ Example:
611
+ app.use(createAuthOS({
612
+ baseURL: "${options.baseURL}",
613
+ org: "your-org-slug",
614
+ service: "your-service-slug",
615
+ }));
616
+
617
+ See: https://docs.authos.dev/vue/oauth-setup`
618
+ );
619
+ return;
620
+ }
621
+ const redirectUri = options.redirectUri ?? (typeof window !== "undefined" ? window.location.origin + "/callback" : void 0);
622
+ const url = client.auth.getLoginUrl(props.provider, {
623
+ org: options.org,
624
+ service: options.service,
625
+ redirect_uri: redirectUri
626
+ });
627
+ emit("redirect");
628
+ window.location.href = url;
629
+ }
630
+ return () => {
631
+ const slotProps = {
632
+ provider: props.provider,
633
+ providerName: providerName.value,
634
+ isConfigured: isConfigured.value,
635
+ disabled: props.disabled,
636
+ handleClick
637
+ };
638
+ if (slots.default) {
639
+ return slots.default(slotProps);
640
+ }
641
+ return h(
642
+ "button",
643
+ {
644
+ type: "button",
645
+ onClick: handleClick,
646
+ disabled: props.disabled || !isConfigured.value,
647
+ "data-authos-oauth": "",
648
+ "data-provider": props.provider
649
+ },
650
+ `Continue with ${providerName.value}`
651
+ );
652
+ };
653
+ }
654
+ });
655
+ var SignedIn = defineComponent({
656
+ name: "SignedIn",
657
+ setup(_, { slots }) {
658
+ const { isAuthenticated, isLoading } = useAuthOS();
659
+ return () => {
660
+ if (isLoading.value) {
661
+ return null;
662
+ }
663
+ if (!isAuthenticated.value) {
664
+ return null;
665
+ }
666
+ return slots.default?.();
667
+ };
668
+ }
669
+ });
670
+ var SignedOut = defineComponent({
671
+ name: "SignedOut",
672
+ setup(_, { slots }) {
673
+ const { isAuthenticated, isLoading } = useAuthOS();
674
+ return () => {
675
+ if (isLoading.value) {
676
+ return null;
677
+ }
678
+ if (isAuthenticated.value) {
679
+ return null;
680
+ }
681
+ return slots.default?.();
682
+ };
683
+ }
684
+ });
685
+ var MagicLinkSignIn = defineComponent({
686
+ name: "MagicLinkSignIn",
687
+ props: {
688
+ showPasswordSignIn: {
689
+ type: Boolean,
690
+ default: true
691
+ }
692
+ },
693
+ emits: ["success", "error"],
694
+ setup(props, { emit, slots }) {
695
+ const { client } = useAuthOS();
696
+ const email = ref("");
697
+ const isLoading = ref(false);
698
+ const error = ref(null);
699
+ const isSent = ref(false);
700
+ async function handleSubmit() {
701
+ error.value = null;
702
+ isLoading.value = true;
703
+ try {
704
+ await client.magicLinks.request({ email: email.value });
705
+ isSent.value = true;
706
+ emit("success");
707
+ } catch (err) {
708
+ const message = err instanceof Error ? err.message : "Failed to send magic link";
709
+ error.value = message;
710
+ emit("error", err);
711
+ } finally {
712
+ isLoading.value = false;
713
+ }
714
+ }
715
+ const slotProps = computed(() => ({
716
+ email: email.value,
717
+ isLoading: isLoading.value,
718
+ error: error.value,
719
+ isSent: isSent.value,
720
+ updateEmail: (val) => {
721
+ email.value = val;
722
+ },
723
+ submit: handleSubmit,
724
+ reset: () => {
725
+ isSent.value = false;
726
+ }
727
+ }));
728
+ return () => {
729
+ if (slots.default) {
730
+ return slots.default(slotProps.value);
731
+ }
732
+ if (isSent.value) {
733
+ return h("div", { "data-authos-magic-link": "", "data-state": "sent" }, [
734
+ h("div", { "data-authos-success": "" }, [
735
+ h("p", "Check your email!"),
736
+ h("p", ["We sent a login link to ", h("strong", email.value)])
737
+ ]),
738
+ h("button", {
739
+ type: "button",
740
+ onClick: () => {
741
+ isSent.value = false;
742
+ },
743
+ "data-authos-back": ""
744
+ }, "Use a different email")
745
+ ]);
746
+ }
747
+ return h("form", {
748
+ onSubmit: (e) => {
749
+ e.preventDefault();
750
+ handleSubmit();
751
+ },
752
+ "data-authos-magic-link": "",
753
+ "data-state": "form"
754
+ }, [
755
+ h("div", { "data-authos-field": "email" }, [
756
+ h("label", { for: "authos-magic-email" }, "Email"),
757
+ h("input", {
758
+ id: "authos-magic-email",
759
+ type: "email",
760
+ autocomplete: "email",
761
+ value: email.value,
762
+ onInput: (e) => {
763
+ email.value = e.target.value;
764
+ },
765
+ placeholder: "Enter your email",
766
+ required: true,
767
+ disabled: isLoading.value
768
+ })
769
+ ]),
770
+ error.value ? h("div", { "data-authos-error": "" }, error.value) : null,
771
+ h("button", {
772
+ type: "submit",
773
+ disabled: isLoading.value,
774
+ "data-authos-submit": ""
775
+ }, isLoading.value ? "Sending..." : "Send Magic Link"),
776
+ props.showPasswordSignIn ? h("div", { "data-authos-signin-prompt": "" }, [
777
+ h("a", { href: "/signin", "data-authos-link": "signin" }, "Sign in with password")
778
+ ]) : null
779
+ ]);
780
+ };
781
+ }
782
+ });
783
+ var PasskeySignIn = defineComponent({
784
+ name: "PasskeySignIn",
785
+ props: {
786
+ showPasswordSignIn: {
787
+ type: Boolean,
788
+ default: true
789
+ }
790
+ },
791
+ emits: ["success", "error"],
792
+ setup(props, { emit, slots }) {
793
+ const { client } = useAuthOS();
794
+ const email = ref("");
795
+ const isLoading = ref(false);
796
+ const error = ref(null);
797
+ const isSupported = ref(true);
798
+ onMounted(() => {
799
+ isSupported.value = client.passkeys.isSupported();
800
+ });
801
+ async function handleSubmit() {
802
+ error.value = null;
803
+ isLoading.value = true;
804
+ try {
805
+ await client.passkeys.login(email.value);
806
+ emit("success");
807
+ } catch (err) {
808
+ const message = err instanceof Error ? err.message : "Passkey authentication failed";
809
+ error.value = message;
810
+ emit("error", err);
811
+ } finally {
812
+ isLoading.value = false;
813
+ }
814
+ }
815
+ const slotProps = computed(() => ({
816
+ email: email.value,
817
+ isLoading: isLoading.value,
818
+ error: error.value,
819
+ isSupported: isSupported.value,
820
+ updateEmail: (val) => {
821
+ email.value = val;
822
+ },
823
+ submit: handleSubmit
824
+ }));
825
+ return () => {
826
+ if (slots.default) {
827
+ return slots.default(slotProps.value);
828
+ }
829
+ if (!isSupported.value) {
830
+ return h("div", {
831
+ "data-authos-passkey": "",
832
+ "data-state": "unsupported"
833
+ }, [
834
+ h("div", { "data-authos-error": "" }, "Passkeys are not supported in this browser."),
835
+ props.showPasswordSignIn ? h("a", { href: "/signin", "data-authos-link": "signin" }, "Sign in with password") : null
836
+ ]);
837
+ }
838
+ return h("form", {
839
+ onSubmit: (e) => {
840
+ e.preventDefault();
841
+ handleSubmit();
842
+ },
843
+ "data-authos-passkey": ""
844
+ }, [
845
+ h("div", { "data-authos-field": "email" }, [
846
+ h("label", { for: "authos-passkey-email" }, "Email"),
847
+ h("input", {
848
+ id: "authos-passkey-email",
849
+ type: "email",
850
+ autocomplete: "email webauthn",
851
+ value: email.value,
852
+ onInput: (e) => {
853
+ email.value = e.target.value;
854
+ },
855
+ placeholder: "Enter your email",
856
+ required: true,
857
+ disabled: isLoading.value
858
+ })
859
+ ]),
860
+ error.value ? h("div", { "data-authos-error": "" }, error.value) : null,
861
+ h("button", {
862
+ type: "submit",
863
+ disabled: isLoading.value,
864
+ "data-authos-submit": ""
865
+ }, isLoading.value ? "Authenticating..." : "Sign in with Passkey"),
866
+ props.showPasswordSignIn ? h("div", { "data-authos-signin-prompt": "" }, [
867
+ h("a", { href: "/signin", "data-authos-link": "signin" }, "Sign in with password")
868
+ ]) : null
869
+ ]);
870
+ };
871
+ }
872
+ });
523
873
 
524
- export { AuthOSProvider, OrganizationSwitcher, Protect, SignIn, SignUp, UserButton, createAuthOS, useOrganization, useUser };
874
+ export { AuthOSProvider, MagicLinkSignIn, OAuthButton, OrganizationSwitcher, PasskeySignIn, Protect, SignIn, SignUp, SignedIn, SignedOut, UserButton, createAuthOS, useAllPermissions, useAnyPermission, useOrganization, usePermission, useUser };
package/dist/nuxt.js CHANGED
@@ -10492,12 +10492,16 @@ var AUTH_OS_INJECTION_KEY = /* @__PURE__ */ Symbol("authOS");
10492
10492
  function useAuthOS() {
10493
10493
  const context = vue.inject(AUTH_OS_INJECTION_KEY);
10494
10494
  if (!context) {
10495
+ const defaultOptions = {
10496
+ baseURL: "http://localhost:3001"
10497
+ };
10495
10498
  const defaultClient = new ssoSdk.SsoClient({
10496
- baseURL: "http://localhost:3001",
10499
+ baseURL: defaultOptions.baseURL,
10497
10500
  storage: new ssoSdk.MemoryStorage()
10498
10501
  });
10499
10502
  return {
10500
10503
  client: defaultClient,
10504
+ options: defaultOptions,
10501
10505
  isLoading: vue.computed(() => false),
10502
10506
  isAuthenticated: vue.computed(() => false)
10503
10507
  };
@@ -10506,6 +10510,7 @@ function useAuthOS() {
10506
10510
  const isAuthenticated = vue.computed(() => context.state.isAuthenticated);
10507
10511
  return {
10508
10512
  client: context.client,
10513
+ options: context.options,
10509
10514
  isLoading,
10510
10515
  isAuthenticated
10511
10516
  };
package/dist/nuxt.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { defu, readPackageJSON, dirname, resolve, normalize, resolveModulePath, isAbsolute as isAbsolute$1, join, normalizeWindowsPath } from './chunk-QGSFJDIC.mjs';
2
2
  import './chunk-T2K7EIWY.mjs';
3
3
  import './chunk-C2J6NSID.mjs';
4
- import { useAuthOS } from './chunk-OGRUDANK.mjs';
4
+ import { useAuthOS } from './chunk-YED35B36.mjs';
5
5
  import './chunk-VCTSSS2F.mjs';
6
6
  import './chunk-Z2UOP6GF.mjs';
7
7
  import './chunk-F6BPUSH3.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drmhse/authos-vue",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Vue and Nuxt adapter for AuthOS authentication",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -68,6 +68,6 @@
68
68
  "access": "public"
69
69
  },
70
70
  "dependencies": {
71
- "@drmhse/sso-sdk": "^0.3.4"
71
+ "@drmhse/sso-sdk": "^0.3.10"
72
72
  }
73
73
  }