@drmhse/authos-vue 0.1.2 → 0.1.4

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.js CHANGED
@@ -21,7 +21,7 @@ function createAuthOS(options) {
21
21
  return new ssoSdk.MemoryStorage();
22
22
  };
23
23
  const client = new ssoSdk.SsoClient({
24
- baseURL: options.baseUrl,
24
+ baseURL: options.baseURL,
25
25
  storage: getStorage(),
26
26
  token: options.initialToken
27
27
  // Pass initial token if provided
@@ -43,7 +43,8 @@ function createAuthOS(options) {
43
43
  });
44
44
  const context = {
45
45
  client,
46
- state
46
+ state,
47
+ options
47
48
  };
48
49
  return {
49
50
  install(app) {
@@ -83,12 +84,16 @@ function createAuthOS(options) {
83
84
  function useAuthOS() {
84
85
  const context = vue.inject(AUTH_OS_INJECTION_KEY);
85
86
  if (!context) {
87
+ const defaultOptions = {
88
+ baseURL: "http://localhost:3001"
89
+ };
86
90
  const defaultClient = new ssoSdk.SsoClient({
87
- baseURL: "http://localhost:3001",
91
+ baseURL: defaultOptions.baseURL,
88
92
  storage: new ssoSdk.MemoryStorage()
89
93
  });
90
94
  return {
91
95
  client: defaultClient,
96
+ options: defaultOptions,
92
97
  isLoading: vue.computed(() => false),
93
98
  isAuthenticated: vue.computed(() => false)
94
99
  };
@@ -97,6 +102,7 @@ function useAuthOS() {
97
102
  const isAuthenticated = vue.computed(() => context.state.isAuthenticated);
98
103
  return {
99
104
  client: context.client,
105
+ options: context.options,
100
106
  isLoading,
101
107
  isAuthenticated
102
108
  };
@@ -133,6 +139,11 @@ function useOrganization() {
133
139
  if (!context) return;
134
140
  isSwitching.value = true;
135
141
  try {
142
+ const result = await context.client.organizations.select(slug);
143
+ await context.client.setSession({
144
+ access_token: result.access_token,
145
+ refresh_token: result.refresh_token
146
+ });
136
147
  const orgResponse = await context.client.organizations.get(slug);
137
148
  context.state.currentOrganization = orgResponse;
138
149
  return orgResponse;
@@ -147,13 +158,46 @@ function useOrganization() {
147
158
  isSwitching
148
159
  };
149
160
  }
161
+ function usePermission(permission) {
162
+ const { user } = useUser();
163
+ return vue.computed(() => {
164
+ if (!user.value || !permission) return false;
165
+ return user.value.permissions?.includes(permission) ?? false;
166
+ });
167
+ }
168
+ function useAnyPermission(permissions) {
169
+ const { user } = useUser();
170
+ return vue.computed(() => {
171
+ if (!user.value || permissions.length === 0) return false;
172
+ return permissions.some((p) => user.value?.permissions?.includes(p));
173
+ });
174
+ }
175
+ function useAllPermissions(permissions) {
176
+ const { user } = useUser();
177
+ return vue.computed(() => {
178
+ if (!user.value || permissions.length === 0) return false;
179
+ return permissions.every((p) => user.value?.permissions?.includes(p));
180
+ });
181
+ }
150
182
  var AuthOSProvider = vue.defineComponent({
151
183
  name: "AuthOSProvider",
152
184
  props: {
153
- baseUrl: {
185
+ baseURL: {
154
186
  type: String,
155
187
  required: true
156
188
  },
189
+ org: {
190
+ type: String,
191
+ default: void 0
192
+ },
193
+ service: {
194
+ type: String,
195
+ default: void 0
196
+ },
197
+ redirectUri: {
198
+ type: String,
199
+ default: void 0
200
+ },
157
201
  storage: {
158
202
  type: Object,
159
203
  default: void 0
@@ -170,7 +214,7 @@ var AuthOSProvider = vue.defineComponent({
170
214
  return new ssoSdk.MemoryStorage();
171
215
  };
172
216
  const client = props.client ?? new ssoSdk.SsoClient({
173
- baseURL: props.baseUrl,
217
+ baseURL: props.baseURL,
174
218
  storage: getStorage()
175
219
  });
176
220
  const state = vue.reactive({
@@ -180,7 +224,13 @@ var AuthOSProvider = vue.defineComponent({
180
224
  currentOrganization: null,
181
225
  organizations: []
182
226
  });
183
- const context = { client, state };
227
+ const options = {
228
+ baseURL: props.baseURL,
229
+ org: props.org,
230
+ service: props.service,
231
+ redirectUri: props.redirectUri
232
+ };
233
+ const context = { client, state, options };
184
234
  vue.provide(AUTH_OS_INJECTION_KEY, context);
185
235
  let unsubscribe;
186
236
  vue.onMounted(() => {
@@ -545,6 +595,297 @@ var Protect = vue.defineComponent({
545
595
  };
546
596
  }
547
597
  });
598
+ var PROVIDER_NAMES = {
599
+ github: "GitHub",
600
+ google: "Google",
601
+ microsoft: "Microsoft"
602
+ };
603
+ var OAuthButton = vue.defineComponent({
604
+ name: "OAuthButton",
605
+ props: {
606
+ provider: {
607
+ type: String,
608
+ required: true
609
+ },
610
+ disabled: {
611
+ type: Boolean,
612
+ default: false
613
+ }
614
+ },
615
+ emits: ["redirect"],
616
+ setup(props, { slots, emit }) {
617
+ const { client, options } = useAuthOS();
618
+ const isConfigured = vue.computed(() => !!(options.org && options.service));
619
+ const providerName = vue.computed(() => PROVIDER_NAMES[props.provider]);
620
+ function handleClick() {
621
+ if (!options.org || !options.service) {
622
+ console.error(
623
+ `[AuthOS] OAuth login requires "org" and "service" in createAuthOS options.
624
+ Current options: { org: ${options.org ? `"${options.org}"` : "undefined"}, service: ${options.service ? `"${options.service}"` : "undefined"} }
625
+
626
+ Example:
627
+ app.use(createAuthOS({
628
+ baseURL: "${options.baseURL}",
629
+ org: "your-org-slug",
630
+ service: "your-service-slug",
631
+ }));
632
+
633
+ See: https://docs.authos.dev/vue/oauth-setup`
634
+ );
635
+ return;
636
+ }
637
+ const redirectUri = options.redirectUri ?? (typeof window !== "undefined" ? window.location.origin + "/callback" : void 0);
638
+ const url = client.auth.getLoginUrl(props.provider, {
639
+ org: options.org,
640
+ service: options.service,
641
+ redirect_uri: redirectUri
642
+ });
643
+ emit("redirect");
644
+ window.location.href = url;
645
+ }
646
+ return () => {
647
+ const slotProps = {
648
+ provider: props.provider,
649
+ providerName: providerName.value,
650
+ isConfigured: isConfigured.value,
651
+ disabled: props.disabled,
652
+ handleClick
653
+ };
654
+ if (slots.default) {
655
+ return slots.default(slotProps);
656
+ }
657
+ return vue.h(
658
+ "button",
659
+ {
660
+ type: "button",
661
+ onClick: handleClick,
662
+ disabled: props.disabled || !isConfigured.value,
663
+ "data-authos-oauth": "",
664
+ "data-provider": props.provider
665
+ },
666
+ `Continue with ${providerName.value}`
667
+ );
668
+ };
669
+ }
670
+ });
671
+ var SignedIn = vue.defineComponent({
672
+ name: "SignedIn",
673
+ setup(_, { slots }) {
674
+ const { isAuthenticated, isLoading } = useAuthOS();
675
+ return () => {
676
+ if (isLoading.value) {
677
+ return null;
678
+ }
679
+ if (!isAuthenticated.value) {
680
+ return null;
681
+ }
682
+ return slots.default?.();
683
+ };
684
+ }
685
+ });
686
+ var SignedOut = vue.defineComponent({
687
+ name: "SignedOut",
688
+ setup(_, { slots }) {
689
+ const { isAuthenticated, isLoading } = useAuthOS();
690
+ return () => {
691
+ if (isLoading.value) {
692
+ return null;
693
+ }
694
+ if (isAuthenticated.value) {
695
+ return null;
696
+ }
697
+ return slots.default?.();
698
+ };
699
+ }
700
+ });
701
+ var MagicLinkSignIn = vue.defineComponent({
702
+ name: "MagicLinkSignIn",
703
+ props: {
704
+ showPasswordSignIn: {
705
+ type: Boolean,
706
+ default: true
707
+ }
708
+ },
709
+ emits: ["success", "error"],
710
+ setup(props, { emit, slots }) {
711
+ const { client } = useAuthOS();
712
+ const email = vue.ref("");
713
+ const isLoading = vue.ref(false);
714
+ const error = vue.ref(null);
715
+ const isSent = vue.ref(false);
716
+ async function handleSubmit() {
717
+ error.value = null;
718
+ isLoading.value = true;
719
+ try {
720
+ await client.magicLinks.request({ email: email.value });
721
+ isSent.value = true;
722
+ emit("success");
723
+ } catch (err) {
724
+ const message = err instanceof Error ? err.message : "Failed to send magic link";
725
+ error.value = message;
726
+ emit("error", err);
727
+ } finally {
728
+ isLoading.value = false;
729
+ }
730
+ }
731
+ const slotProps = vue.computed(() => ({
732
+ email: email.value,
733
+ isLoading: isLoading.value,
734
+ error: error.value,
735
+ isSent: isSent.value,
736
+ updateEmail: (val) => {
737
+ email.value = val;
738
+ },
739
+ submit: handleSubmit,
740
+ reset: () => {
741
+ isSent.value = false;
742
+ }
743
+ }));
744
+ return () => {
745
+ if (slots.default) {
746
+ return slots.default(slotProps.value);
747
+ }
748
+ if (isSent.value) {
749
+ return vue.h("div", { "data-authos-magic-link": "", "data-state": "sent" }, [
750
+ vue.h("div", { "data-authos-success": "" }, [
751
+ vue.h("p", "Check your email!"),
752
+ vue.h("p", ["We sent a login link to ", vue.h("strong", email.value)])
753
+ ]),
754
+ vue.h("button", {
755
+ type: "button",
756
+ onClick: () => {
757
+ isSent.value = false;
758
+ },
759
+ "data-authos-back": ""
760
+ }, "Use a different email")
761
+ ]);
762
+ }
763
+ return vue.h("form", {
764
+ onSubmit: (e) => {
765
+ e.preventDefault();
766
+ handleSubmit();
767
+ },
768
+ "data-authos-magic-link": "",
769
+ "data-state": "form"
770
+ }, [
771
+ vue.h("div", { "data-authos-field": "email" }, [
772
+ vue.h("label", { for: "authos-magic-email" }, "Email"),
773
+ vue.h("input", {
774
+ id: "authos-magic-email",
775
+ type: "email",
776
+ autocomplete: "email",
777
+ value: email.value,
778
+ onInput: (e) => {
779
+ email.value = e.target.value;
780
+ },
781
+ placeholder: "Enter your email",
782
+ required: true,
783
+ disabled: isLoading.value
784
+ })
785
+ ]),
786
+ error.value ? vue.h("div", { "data-authos-error": "" }, error.value) : null,
787
+ vue.h("button", {
788
+ type: "submit",
789
+ disabled: isLoading.value,
790
+ "data-authos-submit": ""
791
+ }, isLoading.value ? "Sending..." : "Send Magic Link"),
792
+ props.showPasswordSignIn ? vue.h("div", { "data-authos-signin-prompt": "" }, [
793
+ vue.h("a", { href: "/signin", "data-authos-link": "signin" }, "Sign in with password")
794
+ ]) : null
795
+ ]);
796
+ };
797
+ }
798
+ });
799
+ var PasskeySignIn = vue.defineComponent({
800
+ name: "PasskeySignIn",
801
+ props: {
802
+ showPasswordSignIn: {
803
+ type: Boolean,
804
+ default: true
805
+ }
806
+ },
807
+ emits: ["success", "error"],
808
+ setup(props, { emit, slots }) {
809
+ const { client } = useAuthOS();
810
+ const email = vue.ref("");
811
+ const isLoading = vue.ref(false);
812
+ const error = vue.ref(null);
813
+ const isSupported = vue.ref(true);
814
+ vue.onMounted(() => {
815
+ isSupported.value = client.passkeys.isSupported();
816
+ });
817
+ async function handleSubmit() {
818
+ error.value = null;
819
+ isLoading.value = true;
820
+ try {
821
+ await client.passkeys.login(email.value);
822
+ emit("success");
823
+ } catch (err) {
824
+ const message = err instanceof Error ? err.message : "Passkey authentication failed";
825
+ error.value = message;
826
+ emit("error", err);
827
+ } finally {
828
+ isLoading.value = false;
829
+ }
830
+ }
831
+ const slotProps = vue.computed(() => ({
832
+ email: email.value,
833
+ isLoading: isLoading.value,
834
+ error: error.value,
835
+ isSupported: isSupported.value,
836
+ updateEmail: (val) => {
837
+ email.value = val;
838
+ },
839
+ submit: handleSubmit
840
+ }));
841
+ return () => {
842
+ if (slots.default) {
843
+ return slots.default(slotProps.value);
844
+ }
845
+ if (!isSupported.value) {
846
+ return vue.h("div", {
847
+ "data-authos-passkey": "",
848
+ "data-state": "unsupported"
849
+ }, [
850
+ vue.h("div", { "data-authos-error": "" }, "Passkeys are not supported in this browser."),
851
+ props.showPasswordSignIn ? vue.h("a", { href: "/signin", "data-authos-link": "signin" }, "Sign in with password") : null
852
+ ]);
853
+ }
854
+ return vue.h("form", {
855
+ onSubmit: (e) => {
856
+ e.preventDefault();
857
+ handleSubmit();
858
+ },
859
+ "data-authos-passkey": ""
860
+ }, [
861
+ vue.h("div", { "data-authos-field": "email" }, [
862
+ vue.h("label", { for: "authos-passkey-email" }, "Email"),
863
+ vue.h("input", {
864
+ id: "authos-passkey-email",
865
+ type: "email",
866
+ autocomplete: "email webauthn",
867
+ value: email.value,
868
+ onInput: (e) => {
869
+ email.value = e.target.value;
870
+ },
871
+ placeholder: "Enter your email",
872
+ required: true,
873
+ disabled: isLoading.value
874
+ })
875
+ ]),
876
+ error.value ? vue.h("div", { "data-authos-error": "" }, error.value) : null,
877
+ vue.h("button", {
878
+ type: "submit",
879
+ disabled: isLoading.value,
880
+ "data-authos-submit": ""
881
+ }, isLoading.value ? "Authenticating..." : "Sign in with Passkey"),
882
+ props.showPasswordSignIn ? vue.h("div", { "data-authos-signin-prompt": "" }, [
883
+ vue.h("a", { href: "/signin", "data-authos-link": "signin" }, "Sign in with password")
884
+ ]) : null
885
+ ]);
886
+ };
887
+ }
888
+ });
548
889
 
549
890
  Object.defineProperty(exports, "AuthErrorCodes", {
550
891
  enumerable: true,
@@ -564,12 +905,20 @@ Object.defineProperty(exports, "SsoApiError", {
564
905
  });
565
906
  exports.AUTH_OS_INJECTION_KEY = AUTH_OS_INJECTION_KEY;
566
907
  exports.AuthOSProvider = AuthOSProvider;
908
+ exports.MagicLinkSignIn = MagicLinkSignIn;
909
+ exports.OAuthButton = OAuthButton;
567
910
  exports.OrganizationSwitcher = OrganizationSwitcher;
911
+ exports.PasskeySignIn = PasskeySignIn;
568
912
  exports.Protect = Protect;
569
913
  exports.SignIn = SignIn;
570
914
  exports.SignUp = SignUp;
915
+ exports.SignedIn = SignedIn;
916
+ exports.SignedOut = SignedOut;
571
917
  exports.UserButton = UserButton;
572
918
  exports.createAuthOS = createAuthOS;
919
+ exports.useAllPermissions = useAllPermissions;
920
+ exports.useAnyPermission = useAnyPermission;
573
921
  exports.useAuthOS = useAuthOS;
574
922
  exports.useOrganization = useOrganization;
923
+ exports.usePermission = usePermission;
575
924
  exports.useUser = useUser;