@drmhse/authos-vue 0.2.7 → 0.8.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/dist/index.d.mts +565 -560
- package/dist/index.d.ts +565 -560
- package/dist/index.js +1285 -1222
- package/dist/index.mjs +1262 -1179
- package/dist/nuxt.d.mts +29 -29
- package/dist/nuxt.d.ts +29 -29
- package/dist/nuxt.js +57 -11354
- package/dist/nuxt.mjs +55 -10080
- package/dist/useAuthOS-DEOIm_-6.mjs +47 -0
- package/dist/useAuthOS-PBWjiXpo.js +58 -0
- package/package.json +6 -6
- package/dist/chokidar-B7NTEAA6.mjs +0 -1730
- package/dist/chunk-5OCDR7QV.mjs +0 -68
- package/dist/chunk-6DZX6EAA.mjs +0 -33
- package/dist/chunk-CD2MZG7D.mjs +0 -103
- package/dist/chunk-I3MWU7CX.mjs +0 -405
- package/dist/chunk-IUIUWQYL.mjs +0 -700
- package/dist/chunk-IYGVWWRW.mjs +0 -448
- package/dist/chunk-JN6U6SKP.mjs +0 -973
- package/dist/chunk-T7GBCGRZ.mjs +0 -734
- package/dist/chunk-VVJVQ4JO.mjs +0 -15
- package/dist/chunk-YED35B36.mjs +0 -33
- package/dist/chunk-ZHH4WLMR.mjs +0 -18
- package/dist/dist-N5W7B4HY.mjs +0 -3
- package/dist/dist-XLLRDJPJ.mjs +0 -505
- package/dist/json5-XBNSJCDA.mjs +0 -3
- package/dist/jsonc-H6LEPRFY.mjs +0 -3
- package/dist/nypm-5BYDGLH7.mjs +0 -3
- package/dist/prompt-WWTBWNTY.mjs +0 -751
- package/dist/tar-A5SPYDDD.mjs +0 -3289
- package/dist/toml-QZCHCWUM.mjs +0 -3
- package/dist/utils-YSWQHENC.mjs +0 -94
- package/dist/yaml-6OXYI3W7.mjs +0 -3
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { AUTH_OS_INJECTION_KEY, useAuthOS } from
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { n as AUTH_OS_INJECTION_KEY, t as useAuthOS } from "./useAuthOS-DEOIm_-6.mjs";
|
|
2
|
+
import { computed, defineComponent, h, inject, nextTick, onMounted, onUnmounted, provide, reactive, ref } from "vue";
|
|
3
|
+
import { AuthErrorCodes, BrowserStorage, BrowserStorage as BrowserStorage$1, MemoryStorage, MemoryStorage as MemoryStorage$1, SsoApiError, SsoApiError as SsoApiError$1, SsoClient } from "@drmhse/sso-sdk";
|
|
4
|
+
//#region src/styles.ts
|
|
5
|
+
/**
|
|
6
|
+
* Built-in styles for AuthOS Vue components.
|
|
7
|
+
* Provides polished default styling with CSS custom properties for theming.
|
|
8
|
+
*/
|
|
9
|
+
const AUTHOS_STYLES = `
|
|
10
10
|
/* ==========================================================================
|
|
11
11
|
AuthOS Component Styles
|
|
12
12
|
CSS Variables + Default Theme
|
|
@@ -435,1204 +435,1287 @@ var AUTHOS_STYLES = `
|
|
|
435
435
|
opacity: 0.6;
|
|
436
436
|
}
|
|
437
437
|
`;
|
|
438
|
-
|
|
438
|
+
/**
|
|
439
|
+
* Injects the AuthOS styles into the document head.
|
|
440
|
+
* Only injects once, even if called multiple times.
|
|
441
|
+
*/
|
|
442
|
+
let stylesInjected = false;
|
|
439
443
|
function injectStyles() {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
444
|
+
if (stylesInjected) return;
|
|
445
|
+
if (typeof document === "undefined") return;
|
|
446
|
+
const styleElement = document.createElement("style");
|
|
447
|
+
styleElement.setAttribute("data-authos-styles", "");
|
|
448
|
+
styleElement.textContent = AUTHOS_STYLES;
|
|
449
|
+
document.head.appendChild(styleElement);
|
|
450
|
+
stylesInjected = true;
|
|
447
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* Applies custom CSS variable overrides for theming.
|
|
454
|
+
*/
|
|
448
455
|
function applyVariables(variables) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
456
|
+
if (typeof document === "undefined") return;
|
|
457
|
+
const root = document.documentElement;
|
|
458
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
459
|
+
const cssVar = `--authos-${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
|
|
460
|
+
root.style.setProperty(cssVar, value);
|
|
461
|
+
}
|
|
455
462
|
}
|
|
456
|
-
|
|
457
|
-
|
|
463
|
+
//#endregion
|
|
464
|
+
//#region src/plugin.ts
|
|
458
465
|
function createAuthOS(options) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
}
|
|
524
|
-
try {
|
|
525
|
-
const orgs = await client.organizations.list();
|
|
526
|
-
state.organizations = orgs;
|
|
527
|
-
if (orgs.length > 0 && !state.currentOrganization) {
|
|
528
|
-
state.currentOrganization = orgs[0];
|
|
529
|
-
}
|
|
530
|
-
} catch {
|
|
531
|
-
state.organizations = [];
|
|
532
|
-
}
|
|
533
|
-
} else {
|
|
534
|
-
state.user = null;
|
|
535
|
-
state.currentOrganization = null;
|
|
536
|
-
state.organizations = [];
|
|
537
|
-
}
|
|
538
|
-
});
|
|
539
|
-
app.provide(AUTH_OS_INJECTION_KEY, context);
|
|
540
|
-
app.config.globalProperties.$authOS = context;
|
|
541
|
-
}
|
|
542
|
-
};
|
|
466
|
+
const getStorage = () => {
|
|
467
|
+
if (options.storage) return options.storage;
|
|
468
|
+
try {
|
|
469
|
+
if (typeof window !== "undefined" && window.localStorage) return new BrowserStorage$1();
|
|
470
|
+
} catch {}
|
|
471
|
+
return new MemoryStorage$1();
|
|
472
|
+
};
|
|
473
|
+
const client = new SsoClient({
|
|
474
|
+
baseURL: options.baseURL,
|
|
475
|
+
storage: getStorage(),
|
|
476
|
+
token: options.initialToken
|
|
477
|
+
});
|
|
478
|
+
let hasSetInitialToken = false;
|
|
479
|
+
const setInitialToken = async () => {
|
|
480
|
+
if (options.initialToken && !hasSetInitialToken) {
|
|
481
|
+
await client.setSession({ access_token: options.initialToken });
|
|
482
|
+
hasSetInitialToken = true;
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
const state = reactive({
|
|
486
|
+
user: null,
|
|
487
|
+
isAuthenticated: false,
|
|
488
|
+
isLoading: !options.initialToken,
|
|
489
|
+
currentOrganization: null,
|
|
490
|
+
organizations: []
|
|
491
|
+
});
|
|
492
|
+
const context = {
|
|
493
|
+
client,
|
|
494
|
+
state,
|
|
495
|
+
options
|
|
496
|
+
};
|
|
497
|
+
return { install(app) {
|
|
498
|
+
injectStyles();
|
|
499
|
+
if (options.appearance?.variables) applyVariables(options.appearance.variables);
|
|
500
|
+
if (options.org && !options.service) console.warn("[AuthOS] You provided \"org\" but not \"service\". OAuth flows may not work correctly.");
|
|
501
|
+
if (!options.org && options.service) console.warn("[AuthOS] You provided \"service\" but not \"org\". OAuth flows may not work correctly.");
|
|
502
|
+
nextTick(() => {
|
|
503
|
+
setInitialToken();
|
|
504
|
+
});
|
|
505
|
+
client.onAuthStateChange(async (isAuthenticated) => {
|
|
506
|
+
state.isAuthenticated = isAuthenticated;
|
|
507
|
+
state.isLoading = false;
|
|
508
|
+
if (isAuthenticated) {
|
|
509
|
+
try {
|
|
510
|
+
state.user = await client.user.getProfile();
|
|
511
|
+
} catch {
|
|
512
|
+
state.user = null;
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
const orgs = await client.organizations.list();
|
|
516
|
+
state.organizations = orgs;
|
|
517
|
+
if (orgs.length > 0 && !state.currentOrganization) state.currentOrganization = orgs[0];
|
|
518
|
+
} catch {
|
|
519
|
+
state.organizations = [];
|
|
520
|
+
}
|
|
521
|
+
} else {
|
|
522
|
+
state.user = null;
|
|
523
|
+
state.currentOrganization = null;
|
|
524
|
+
state.organizations = [];
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
app.provide(AUTH_OS_INJECTION_KEY, context);
|
|
528
|
+
app.config.globalProperties.$authOS = context;
|
|
529
|
+
} };
|
|
543
530
|
}
|
|
531
|
+
//#endregion
|
|
532
|
+
//#region src/composables/useUser.ts
|
|
544
533
|
function useUser() {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
return {
|
|
555
|
-
user,
|
|
556
|
-
isLoading
|
|
557
|
-
};
|
|
534
|
+
const context = inject(AUTH_OS_INJECTION_KEY);
|
|
535
|
+
if (!context) return {
|
|
536
|
+
user: ref(null),
|
|
537
|
+
isLoading: computed(() => false)
|
|
538
|
+
};
|
|
539
|
+
return {
|
|
540
|
+
user: computed(() => context.state.user),
|
|
541
|
+
isLoading: computed(() => context.state.isLoading)
|
|
542
|
+
};
|
|
558
543
|
}
|
|
544
|
+
//#endregion
|
|
545
|
+
//#region src/composables/useOrganization.ts
|
|
546
|
+
/**
|
|
547
|
+
* Composable to access the current organization context and switch between organizations.
|
|
548
|
+
*
|
|
549
|
+
* When switching organizations, this calls the backend to issue new JWT tokens
|
|
550
|
+
* with the organization context, enabling seamless organization switching without
|
|
551
|
+
* requiring re-authentication.
|
|
552
|
+
*
|
|
553
|
+
* @returns The current organization and a function to switch organizations
|
|
554
|
+
*
|
|
555
|
+
* @example
|
|
556
|
+
* ```vue
|
|
557
|
+
* <script setup>
|
|
558
|
+
* import { useOrganization } from '@drmhse/authos-vue';
|
|
559
|
+
*
|
|
560
|
+
* const { currentOrganization, switchOrganization, isSwitching } = useOrganization();
|
|
561
|
+
* <\/script>
|
|
562
|
+
* ```
|
|
563
|
+
*/
|
|
559
564
|
function useOrganization() {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
isSwitching
|
|
593
|
-
};
|
|
565
|
+
const context = inject(AUTH_OS_INJECTION_KEY);
|
|
566
|
+
if (!context) return {
|
|
567
|
+
currentOrganization: ref(null),
|
|
568
|
+
organizations: ref([]),
|
|
569
|
+
switchOrganization: async () => null,
|
|
570
|
+
isSwitching: ref(false)
|
|
571
|
+
};
|
|
572
|
+
const currentOrganization = computed(() => context.state.currentOrganization);
|
|
573
|
+
const organizations = computed(() => context.state.organizations);
|
|
574
|
+
const isSwitching = ref(false);
|
|
575
|
+
async function switchOrganization(slug) {
|
|
576
|
+
if (!context) return;
|
|
577
|
+
isSwitching.value = true;
|
|
578
|
+
try {
|
|
579
|
+
const result = await context.client.organizations.select(slug);
|
|
580
|
+
await context.client.setSession({
|
|
581
|
+
access_token: result.access_token,
|
|
582
|
+
refresh_token: result.refresh_token
|
|
583
|
+
});
|
|
584
|
+
const orgResponse = await context.client.organizations.get(slug);
|
|
585
|
+
context.state.currentOrganization = orgResponse;
|
|
586
|
+
return orgResponse;
|
|
587
|
+
} finally {
|
|
588
|
+
isSwitching.value = false;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return {
|
|
592
|
+
currentOrganization,
|
|
593
|
+
organizations,
|
|
594
|
+
switchOrganization,
|
|
595
|
+
isSwitching
|
|
596
|
+
};
|
|
594
597
|
}
|
|
598
|
+
//#endregion
|
|
599
|
+
//#region src/composables/usePermission.ts
|
|
600
|
+
/**
|
|
601
|
+
* Check if the current user has a specific permission.
|
|
602
|
+
*
|
|
603
|
+
* @param permission The permission to check
|
|
604
|
+
* @returns A computed ref that is true if the user has the permission
|
|
605
|
+
*
|
|
606
|
+
* @example
|
|
607
|
+
* ```vue
|
|
608
|
+
* <script setup>
|
|
609
|
+
* import { usePermission } from '@drmhse/authos-vue';
|
|
610
|
+
*
|
|
611
|
+
* const canAccessAdmin = usePermission('admin:access');
|
|
612
|
+
* <\/script>
|
|
613
|
+
*
|
|
614
|
+
* <template>
|
|
615
|
+
* <button v-if="canAccessAdmin">Admin Panel</button>
|
|
616
|
+
* </template>
|
|
617
|
+
* ```
|
|
618
|
+
*/
|
|
595
619
|
function usePermission(permission) {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
620
|
+
const { user } = useUser();
|
|
621
|
+
return computed(() => {
|
|
622
|
+
if (!user.value || !permission) return false;
|
|
623
|
+
return user.value.permissions?.includes(permission) ?? false;
|
|
624
|
+
});
|
|
601
625
|
}
|
|
626
|
+
/**
|
|
627
|
+
* Check if the current user has any of the specified permissions.
|
|
628
|
+
*
|
|
629
|
+
* @param permissions The permissions to check
|
|
630
|
+
* @returns A computed ref that is true if the user has any of the permissions
|
|
631
|
+
*
|
|
632
|
+
* @example
|
|
633
|
+
* ```vue
|
|
634
|
+
* <script setup>
|
|
635
|
+
* import { useAnyPermission } from '@drmhse/authos-vue';
|
|
636
|
+
*
|
|
637
|
+
* const canAccessReports = useAnyPermission(['reports:read', 'admin:access']);
|
|
638
|
+
* <\/script>
|
|
639
|
+
* ```
|
|
640
|
+
*/
|
|
602
641
|
function useAnyPermission(permissions) {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
642
|
+
const { user } = useUser();
|
|
643
|
+
return computed(() => {
|
|
644
|
+
if (!user.value || permissions.length === 0) return false;
|
|
645
|
+
return permissions.some((p) => user.value?.permissions?.includes(p));
|
|
646
|
+
});
|
|
608
647
|
}
|
|
648
|
+
/**
|
|
649
|
+
* Check if the current user has all of the specified permissions.
|
|
650
|
+
*
|
|
651
|
+
* @param permissions The permissions to check
|
|
652
|
+
* @returns A computed ref that is true if the user has all of the permissions
|
|
653
|
+
*
|
|
654
|
+
* @example
|
|
655
|
+
* ```vue
|
|
656
|
+
* <script setup>
|
|
657
|
+
* import { useAllPermissions } from '@drmhse/authos-vue';
|
|
658
|
+
*
|
|
659
|
+
* const canManageBilling = useAllPermissions(['billing:read', 'billing:write']);
|
|
660
|
+
* <\/script>
|
|
661
|
+
* ```
|
|
662
|
+
*/
|
|
609
663
|
function useAllPermissions(permissions) {
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
664
|
+
const { user } = useUser();
|
|
665
|
+
return computed(() => {
|
|
666
|
+
if (!user.value || permissions.length === 0) return false;
|
|
667
|
+
return permissions.every((p) => user.value?.permissions?.includes(p));
|
|
668
|
+
});
|
|
615
669
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
670
|
+
//#endregion
|
|
671
|
+
//#region src/components/AuthOSProvider.ts
|
|
672
|
+
const AuthOSProvider = defineComponent({
|
|
673
|
+
name: "AuthOSProvider",
|
|
674
|
+
props: {
|
|
675
|
+
baseURL: {
|
|
676
|
+
type: String,
|
|
677
|
+
required: true
|
|
678
|
+
},
|
|
679
|
+
org: {
|
|
680
|
+
type: String,
|
|
681
|
+
default: void 0
|
|
682
|
+
},
|
|
683
|
+
service: {
|
|
684
|
+
type: String,
|
|
685
|
+
default: void 0
|
|
686
|
+
},
|
|
687
|
+
redirectUri: {
|
|
688
|
+
type: String,
|
|
689
|
+
default: void 0
|
|
690
|
+
},
|
|
691
|
+
storage: {
|
|
692
|
+
type: Object,
|
|
693
|
+
default: void 0
|
|
694
|
+
},
|
|
695
|
+
client: {
|
|
696
|
+
type: Object,
|
|
697
|
+
default: void 0
|
|
698
|
+
}
|
|
699
|
+
},
|
|
700
|
+
setup(props, { slots }) {
|
|
701
|
+
const getStorage = () => {
|
|
702
|
+
if (props.storage) return props.storage;
|
|
703
|
+
if (typeof window !== "undefined") return new BrowserStorage$1();
|
|
704
|
+
return new MemoryStorage$1();
|
|
705
|
+
};
|
|
706
|
+
const client = props.client ?? new SsoClient({
|
|
707
|
+
baseURL: props.baseURL,
|
|
708
|
+
storage: getStorage()
|
|
709
|
+
});
|
|
710
|
+
const state = reactive({
|
|
711
|
+
user: null,
|
|
712
|
+
isAuthenticated: false,
|
|
713
|
+
isLoading: true,
|
|
714
|
+
currentOrganization: null,
|
|
715
|
+
organizations: []
|
|
716
|
+
});
|
|
717
|
+
provide(AUTH_OS_INJECTION_KEY, {
|
|
718
|
+
client,
|
|
719
|
+
state,
|
|
720
|
+
options: {
|
|
721
|
+
baseURL: props.baseURL,
|
|
722
|
+
org: props.org,
|
|
723
|
+
service: props.service,
|
|
724
|
+
redirectUri: props.redirectUri
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
let unsubscribe;
|
|
728
|
+
onMounted(() => {
|
|
729
|
+
unsubscribe = client.onAuthStateChange(async (isAuthenticated) => {
|
|
730
|
+
state.isAuthenticated = isAuthenticated;
|
|
731
|
+
state.isLoading = false;
|
|
732
|
+
if (isAuthenticated) {
|
|
733
|
+
try {
|
|
734
|
+
state.user = await client.user.getProfile();
|
|
735
|
+
} catch {
|
|
736
|
+
state.user = null;
|
|
737
|
+
}
|
|
738
|
+
try {
|
|
739
|
+
const orgs = await client.organizations.list();
|
|
740
|
+
state.organizations = orgs;
|
|
741
|
+
if (orgs.length > 0 && !state.currentOrganization) state.currentOrganization = orgs[0];
|
|
742
|
+
} catch {
|
|
743
|
+
state.organizations = [];
|
|
744
|
+
}
|
|
745
|
+
} else {
|
|
746
|
+
state.user = null;
|
|
747
|
+
state.currentOrganization = null;
|
|
748
|
+
state.organizations = [];
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
});
|
|
752
|
+
onUnmounted(() => {
|
|
753
|
+
unsubscribe?.();
|
|
754
|
+
});
|
|
755
|
+
return () => slots.default ? slots.default() : h("div");
|
|
756
|
+
}
|
|
702
757
|
});
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
h("button", {
|
|
847
|
-
type: "submit",
|
|
848
|
-
disabled: isSubmitting.value,
|
|
849
|
-
"data-authos-submit": ""
|
|
850
|
-
}, isSubmitting.value ? "Signing in..." : "Sign In")
|
|
851
|
-
])
|
|
852
|
-
]);
|
|
853
|
-
};
|
|
854
|
-
}
|
|
758
|
+
//#endregion
|
|
759
|
+
//#region src/components/SignIn.ts
|
|
760
|
+
const MFA_PREAUTH_EXPIRY = 300;
|
|
761
|
+
const SignIn = defineComponent({
|
|
762
|
+
name: "SignIn",
|
|
763
|
+
props: {
|
|
764
|
+
onSuccess: {
|
|
765
|
+
type: Function,
|
|
766
|
+
default: void 0
|
|
767
|
+
},
|
|
768
|
+
onError: {
|
|
769
|
+
type: Function,
|
|
770
|
+
default: void 0
|
|
771
|
+
}
|
|
772
|
+
},
|
|
773
|
+
emits: ["success", "error"],
|
|
774
|
+
setup(props, { slots, emit }) {
|
|
775
|
+
const { client, options } = useAuthOS();
|
|
776
|
+
const email = ref("");
|
|
777
|
+
const password = ref("");
|
|
778
|
+
const mfaCode = ref("");
|
|
779
|
+
const preauthToken = ref("");
|
|
780
|
+
const step = ref("credentials");
|
|
781
|
+
const error = ref(null);
|
|
782
|
+
const isSubmitting = ref(false);
|
|
783
|
+
async function submit() {
|
|
784
|
+
error.value = null;
|
|
785
|
+
isSubmitting.value = true;
|
|
786
|
+
try {
|
|
787
|
+
if (step.value === "credentials") {
|
|
788
|
+
const result = await client.auth.login({
|
|
789
|
+
email: email.value,
|
|
790
|
+
password: password.value,
|
|
791
|
+
org_slug: options.org,
|
|
792
|
+
service_slug: options.service
|
|
793
|
+
});
|
|
794
|
+
if (result.expires_in === MFA_PREAUTH_EXPIRY) {
|
|
795
|
+
preauthToken.value = result.access_token;
|
|
796
|
+
step.value = "mfa";
|
|
797
|
+
} else {
|
|
798
|
+
emit("success");
|
|
799
|
+
props.onSuccess?.();
|
|
800
|
+
}
|
|
801
|
+
} else {
|
|
802
|
+
await client.auth.verifyMfa(preauthToken.value, mfaCode.value);
|
|
803
|
+
emit("success");
|
|
804
|
+
props.onSuccess?.();
|
|
805
|
+
}
|
|
806
|
+
} catch (err) {
|
|
807
|
+
const message = err instanceof SsoApiError$1 ? err.message : "Login failed";
|
|
808
|
+
error.value = message;
|
|
809
|
+
const e = err instanceof Error ? err : new Error(message);
|
|
810
|
+
emit("error", e);
|
|
811
|
+
props.onError?.(e);
|
|
812
|
+
} finally {
|
|
813
|
+
isSubmitting.value = false;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return () => {
|
|
817
|
+
const slotProps = {
|
|
818
|
+
email: email.value,
|
|
819
|
+
password: password.value,
|
|
820
|
+
mfaCode: mfaCode.value,
|
|
821
|
+
step: step.value,
|
|
822
|
+
error: error.value,
|
|
823
|
+
isSubmitting: isSubmitting.value,
|
|
824
|
+
updateEmail: (v) => email.value = v,
|
|
825
|
+
updatePassword: (v) => password.value = v,
|
|
826
|
+
updateMfaCode: (v) => mfaCode.value = v,
|
|
827
|
+
submit
|
|
828
|
+
};
|
|
829
|
+
if (slots.default) return slots.default(slotProps);
|
|
830
|
+
if (step.value === "mfa") return h("div", {
|
|
831
|
+
"data-authos-signin": "",
|
|
832
|
+
"data-state": "mfa"
|
|
833
|
+
}, [h("form", { onSubmit: (e) => {
|
|
834
|
+
e.preventDefault();
|
|
835
|
+
submit();
|
|
836
|
+
} }, [
|
|
837
|
+
h("div", { "data-authos-field": "mfa-code" }, [h("label", { for: "authos-mfa-code" }, "Verification Code"), h("input", {
|
|
838
|
+
id: "authos-mfa-code",
|
|
839
|
+
type: "text",
|
|
840
|
+
inputMode: "numeric",
|
|
841
|
+
autocomplete: "one-time-code",
|
|
842
|
+
value: mfaCode.value,
|
|
843
|
+
placeholder: "Enter 6-digit code",
|
|
844
|
+
required: true,
|
|
845
|
+
disabled: isSubmitting.value,
|
|
846
|
+
onInput: (e) => mfaCode.value = e.target.value
|
|
847
|
+
})]),
|
|
848
|
+
error.value && h("div", { "data-authos-error": "" }, error.value),
|
|
849
|
+
h("button", {
|
|
850
|
+
type: "submit",
|
|
851
|
+
disabled: isSubmitting.value,
|
|
852
|
+
"data-authos-submit": ""
|
|
853
|
+
}, isSubmitting.value ? "Verifying..." : "Verify"),
|
|
854
|
+
h("button", {
|
|
855
|
+
type: "button",
|
|
856
|
+
"data-authos-back": "",
|
|
857
|
+
onClick: () => {
|
|
858
|
+
step.value = "credentials";
|
|
859
|
+
mfaCode.value = "";
|
|
860
|
+
preauthToken.value = "";
|
|
861
|
+
error.value = null;
|
|
862
|
+
}
|
|
863
|
+
}, "Back to login")
|
|
864
|
+
])]);
|
|
865
|
+
return h("div", {
|
|
866
|
+
"data-authos-signin": "",
|
|
867
|
+
"data-state": "credentials"
|
|
868
|
+
}, [h("form", { onSubmit: (e) => {
|
|
869
|
+
e.preventDefault();
|
|
870
|
+
submit();
|
|
871
|
+
} }, [
|
|
872
|
+
h("div", { "data-authos-field": "email" }, [h("label", { for: "authos-email" }, "Email"), h("input", {
|
|
873
|
+
id: "authos-email",
|
|
874
|
+
type: "email",
|
|
875
|
+
autocomplete: "email",
|
|
876
|
+
value: email.value,
|
|
877
|
+
placeholder: "Enter your email",
|
|
878
|
+
required: true,
|
|
879
|
+
disabled: isSubmitting.value,
|
|
880
|
+
onInput: (e) => email.value = e.target.value
|
|
881
|
+
})]),
|
|
882
|
+
h("div", { "data-authos-field": "password" }, [h("label", { for: "authos-password" }, "Password"), h("input", {
|
|
883
|
+
id: "authos-password",
|
|
884
|
+
type: "password",
|
|
885
|
+
autocomplete: "current-password",
|
|
886
|
+
value: password.value,
|
|
887
|
+
placeholder: "Enter your password",
|
|
888
|
+
required: true,
|
|
889
|
+
disabled: isSubmitting.value,
|
|
890
|
+
onInput: (e) => password.value = e.target.value
|
|
891
|
+
})]),
|
|
892
|
+
error.value && h("div", { "data-authos-error": "" }, error.value),
|
|
893
|
+
h("button", {
|
|
894
|
+
type: "submit",
|
|
895
|
+
disabled: isSubmitting.value,
|
|
896
|
+
"data-authos-submit": ""
|
|
897
|
+
}, isSubmitting.value ? "Signing in..." : "Sign In")
|
|
898
|
+
])]);
|
|
899
|
+
};
|
|
900
|
+
}
|
|
855
901
|
});
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
902
|
+
//#endregion
|
|
903
|
+
//#region src/components/OAuthButton.ts
|
|
904
|
+
/**
|
|
905
|
+
* Human-readable provider names for button labels
|
|
906
|
+
*/
|
|
907
|
+
const PROVIDER_NAMES = {
|
|
908
|
+
github: "GitHub",
|
|
909
|
+
google: "Google",
|
|
910
|
+
microsoft: "Microsoft"
|
|
860
911
|
};
|
|
912
|
+
/**
|
|
913
|
+
* SVG icons for each OAuth provider
|
|
914
|
+
*/
|
|
861
915
|
function getProviderIcon(provider) {
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
916
|
+
switch (provider) {
|
|
917
|
+
case "github": return h("svg", {
|
|
918
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
919
|
+
viewBox: "0 0 24 24",
|
|
920
|
+
fill: "currentColor",
|
|
921
|
+
"aria-hidden": "true"
|
|
922
|
+
}, [h("path", { d: "M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z" })]);
|
|
923
|
+
case "google": return h("svg", {
|
|
924
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
925
|
+
viewBox: "0 0 24 24",
|
|
926
|
+
"aria-hidden": "true"
|
|
927
|
+
}, [
|
|
928
|
+
h("path", {
|
|
929
|
+
fill: "#4285F4",
|
|
930
|
+
d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
931
|
+
}),
|
|
932
|
+
h("path", {
|
|
933
|
+
fill: "#34A853",
|
|
934
|
+
d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
935
|
+
}),
|
|
936
|
+
h("path", {
|
|
937
|
+
fill: "#FBBC05",
|
|
938
|
+
d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
939
|
+
}),
|
|
940
|
+
h("path", {
|
|
941
|
+
fill: "#EA4335",
|
|
942
|
+
d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
943
|
+
})
|
|
944
|
+
]);
|
|
945
|
+
case "microsoft": return h("svg", {
|
|
946
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
947
|
+
viewBox: "0 0 23 23",
|
|
948
|
+
"aria-hidden": "true"
|
|
949
|
+
}, [
|
|
950
|
+
h("path", {
|
|
951
|
+
fill: "#f35325",
|
|
952
|
+
d: "M1 1h10v10H1z"
|
|
953
|
+
}),
|
|
954
|
+
h("path", {
|
|
955
|
+
fill: "#81bc06",
|
|
956
|
+
d: "M12 1h10v10H12z"
|
|
957
|
+
}),
|
|
958
|
+
h("path", {
|
|
959
|
+
fill: "#05a6f0",
|
|
960
|
+
d: "M1 12h10v10H1z"
|
|
961
|
+
}),
|
|
962
|
+
h("path", {
|
|
963
|
+
fill: "#ffba08",
|
|
964
|
+
d: "M12 12h10v10H12z"
|
|
965
|
+
})
|
|
966
|
+
]);
|
|
967
|
+
}
|
|
909
968
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
);
|
|
978
|
-
};
|
|
979
|
-
}
|
|
969
|
+
/**
|
|
970
|
+
* OAuth login button for a specific provider.
|
|
971
|
+
* Redirects the user to the OAuth provider's login page.
|
|
972
|
+
*
|
|
973
|
+
* Requires `org` and `service` to be configured in createAuthOS options.
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* ```vue
|
|
977
|
+
* <script setup>
|
|
978
|
+
* import { OAuthButton } from '@drmhse/authos-vue';
|
|
979
|
+
* <\/script>
|
|
980
|
+
*
|
|
981
|
+
* <template>
|
|
982
|
+
* <OAuthButton provider="github" />
|
|
983
|
+
* <OAuthButton provider="google">Sign in with Google</OAuthButton>
|
|
984
|
+
* </template>
|
|
985
|
+
* ```
|
|
986
|
+
*/
|
|
987
|
+
const OAuthButton = defineComponent({
|
|
988
|
+
name: "OAuthButton",
|
|
989
|
+
props: {
|
|
990
|
+
provider: {
|
|
991
|
+
type: String,
|
|
992
|
+
required: true
|
|
993
|
+
},
|
|
994
|
+
disabled: {
|
|
995
|
+
type: Boolean,
|
|
996
|
+
default: false
|
|
997
|
+
}
|
|
998
|
+
},
|
|
999
|
+
emits: ["redirect"],
|
|
1000
|
+
setup(props, { slots, emit }) {
|
|
1001
|
+
const { client, options } = useAuthOS();
|
|
1002
|
+
const isConfigured = computed(() => !!(options.org && options.service));
|
|
1003
|
+
const providerName = computed(() => PROVIDER_NAMES[props.provider]);
|
|
1004
|
+
function handleClick() {
|
|
1005
|
+
if (!options.org || !options.service) {
|
|
1006
|
+
console.error(`[AuthOS] OAuth login requires "org" and "service" in createAuthOS options.\nCurrent options: { org: ${options.org ? `"${options.org}"` : "undefined"}, service: ${options.service ? `"${options.service}"` : "undefined"} }\n\nExample:\n app.use(createAuthOS({\n baseURL: "${options.baseURL}",\n org: "your-org-slug",\n service: "your-service-slug",\n }));\n\nSee: https://docs.authos.dev/vue/oauth-setup`);
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
const redirectUri = options.redirectUri ?? (typeof window !== "undefined" ? window.location.origin + "/callback" : void 0);
|
|
1010
|
+
const url = client.auth.getLoginUrl(props.provider, {
|
|
1011
|
+
org: options.org,
|
|
1012
|
+
service: options.service,
|
|
1013
|
+
redirect_uri: redirectUri
|
|
1014
|
+
});
|
|
1015
|
+
emit("redirect");
|
|
1016
|
+
window.location.href = url;
|
|
1017
|
+
}
|
|
1018
|
+
return () => {
|
|
1019
|
+
const slotProps = {
|
|
1020
|
+
provider: props.provider,
|
|
1021
|
+
providerName: providerName.value,
|
|
1022
|
+
isConfigured: isConfigured.value,
|
|
1023
|
+
disabled: props.disabled,
|
|
1024
|
+
handleClick
|
|
1025
|
+
};
|
|
1026
|
+
if (slots.default) return slots.default(slotProps);
|
|
1027
|
+
return h("button", {
|
|
1028
|
+
type: "button",
|
|
1029
|
+
onClick: handleClick,
|
|
1030
|
+
disabled: props.disabled || !isConfigured.value,
|
|
1031
|
+
"data-authos-oauth": "",
|
|
1032
|
+
"data-provider": props.provider
|
|
1033
|
+
}, [getProviderIcon(props.provider), h("span", `Continue with ${providerName.value}`)]);
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
980
1036
|
});
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
required: true,
|
|
1142
|
-
disabled: isSubmitting.value,
|
|
1143
|
-
onInput: (e) => confirmPassword.value = e.target.value
|
|
1144
|
-
})
|
|
1145
|
-
]),
|
|
1146
|
-
error.value && h("div", { "data-authos-error": "" }, error.value),
|
|
1147
|
-
h("button", {
|
|
1148
|
-
type: "submit",
|
|
1149
|
-
disabled: isSubmitting.value,
|
|
1150
|
-
"data-authos-submit": ""
|
|
1151
|
-
}, isSubmitting.value ? "Creating account..." : "Sign Up"),
|
|
1152
|
-
// Sign In Link
|
|
1153
|
-
props.showSignIn && h("div", { "data-authos-signin-prompt": "" }, [
|
|
1154
|
-
"Already have an account? ",
|
|
1155
|
-
h("a", { href: "/signin", "data-authos-link": "signin" }, "Sign in")
|
|
1156
|
-
])
|
|
1157
|
-
])
|
|
1158
|
-
]);
|
|
1159
|
-
};
|
|
1160
|
-
}
|
|
1037
|
+
//#endregion
|
|
1038
|
+
//#region src/components/SignUp.ts
|
|
1039
|
+
const SignUp = defineComponent({
|
|
1040
|
+
name: "SignUp",
|
|
1041
|
+
props: {
|
|
1042
|
+
onSuccess: {
|
|
1043
|
+
type: Function,
|
|
1044
|
+
default: void 0
|
|
1045
|
+
},
|
|
1046
|
+
onError: {
|
|
1047
|
+
type: Function,
|
|
1048
|
+
default: void 0
|
|
1049
|
+
},
|
|
1050
|
+
/** Organization slug for tenant context */
|
|
1051
|
+
orgSlug: {
|
|
1052
|
+
type: String,
|
|
1053
|
+
default: void 0
|
|
1054
|
+
},
|
|
1055
|
+
/** Service slug for tenant attribution (used with orgSlug) */
|
|
1056
|
+
serviceSlug: {
|
|
1057
|
+
type: String,
|
|
1058
|
+
default: void 0
|
|
1059
|
+
},
|
|
1060
|
+
/** List of OAuth providers to display buttons for */
|
|
1061
|
+
providers: {
|
|
1062
|
+
type: [Array, Boolean],
|
|
1063
|
+
default: false
|
|
1064
|
+
},
|
|
1065
|
+
/** Show divider between OAuth and email form */
|
|
1066
|
+
showDivider: {
|
|
1067
|
+
type: Boolean,
|
|
1068
|
+
default: true
|
|
1069
|
+
},
|
|
1070
|
+
/** Show sign in link */
|
|
1071
|
+
showSignIn: {
|
|
1072
|
+
type: Boolean,
|
|
1073
|
+
default: true
|
|
1074
|
+
}
|
|
1075
|
+
},
|
|
1076
|
+
emits: ["success", "error"],
|
|
1077
|
+
setup(props, { slots, emit }) {
|
|
1078
|
+
const { client, options } = useAuthOS();
|
|
1079
|
+
const email = ref("");
|
|
1080
|
+
const password = ref("");
|
|
1081
|
+
const confirmPassword = ref("");
|
|
1082
|
+
const error = ref(null);
|
|
1083
|
+
const isSubmitting = ref(false);
|
|
1084
|
+
const isSuccess = ref(false);
|
|
1085
|
+
const hasOAuthConfig = !!(options.org && options.service);
|
|
1086
|
+
const oauthProviders = Array.isArray(props.providers) ? props.providers : [];
|
|
1087
|
+
async function submit() {
|
|
1088
|
+
error.value = null;
|
|
1089
|
+
if (password.value !== confirmPassword.value) {
|
|
1090
|
+
error.value = "Passwords do not match";
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
if (password.value.length < 8) {
|
|
1094
|
+
error.value = "Password must be at least 8 characters";
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
isSubmitting.value = true;
|
|
1098
|
+
try {
|
|
1099
|
+
await client.auth.register({
|
|
1100
|
+
email: email.value,
|
|
1101
|
+
password: password.value,
|
|
1102
|
+
org_slug: props.orgSlug ?? options.org,
|
|
1103
|
+
service_slug: props.serviceSlug ?? options.service
|
|
1104
|
+
});
|
|
1105
|
+
isSuccess.value = true;
|
|
1106
|
+
emit("success");
|
|
1107
|
+
props.onSuccess?.();
|
|
1108
|
+
} catch (err) {
|
|
1109
|
+
const message = err instanceof SsoApiError$1 ? err.message : "Registration failed";
|
|
1110
|
+
error.value = message;
|
|
1111
|
+
const e = err instanceof Error ? err : new Error(message);
|
|
1112
|
+
emit("error", e);
|
|
1113
|
+
props.onError?.(e);
|
|
1114
|
+
} finally {
|
|
1115
|
+
isSubmitting.value = false;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
return () => {
|
|
1119
|
+
const slotProps = {
|
|
1120
|
+
email: email.value,
|
|
1121
|
+
password: password.value,
|
|
1122
|
+
error: error.value,
|
|
1123
|
+
isSubmitting: isSubmitting.value,
|
|
1124
|
+
updateEmail: (v) => email.value = v,
|
|
1125
|
+
updatePassword: (v) => password.value = v,
|
|
1126
|
+
submit
|
|
1127
|
+
};
|
|
1128
|
+
if (slots.default) return slots.default(slotProps);
|
|
1129
|
+
if (isSuccess.value) return h("div", {
|
|
1130
|
+
"data-authos-signup": "",
|
|
1131
|
+
"data-state": "success"
|
|
1132
|
+
}, [h("div", { "data-authos-success": "" }, [h("h2", "Check your email"), h("p", `We've sent a verification link to ${email.value}. Please click the link to verify your account.`)])]);
|
|
1133
|
+
return h("div", {
|
|
1134
|
+
"data-authos-signup": "",
|
|
1135
|
+
"data-state": "credentials"
|
|
1136
|
+
}, [
|
|
1137
|
+
oauthProviders.length > 0 && h("div", { "data-authos-oauth-section": "" }, [oauthProviders.map((provider) => h(OAuthButton, {
|
|
1138
|
+
key: provider,
|
|
1139
|
+
provider,
|
|
1140
|
+
disabled: isSubmitting.value || !hasOAuthConfig
|
|
1141
|
+
})), !hasOAuthConfig && h("p", {
|
|
1142
|
+
"data-authos-oauth-warning": "",
|
|
1143
|
+
style: {
|
|
1144
|
+
color: "orange",
|
|
1145
|
+
fontSize: "0.875rem"
|
|
1146
|
+
}
|
|
1147
|
+
}, "OAuth requires org and service in plugin options")]),
|
|
1148
|
+
oauthProviders.length > 0 && props.showDivider && h("div", { "data-authos-divider": "" }, [h("span", "or")]),
|
|
1149
|
+
h("form", { onSubmit: (e) => {
|
|
1150
|
+
e.preventDefault();
|
|
1151
|
+
submit();
|
|
1152
|
+
} }, [
|
|
1153
|
+
h("div", { "data-authos-field": "email" }, [h("label", { for: "authos-signup-email" }, "Email"), h("input", {
|
|
1154
|
+
id: "authos-signup-email",
|
|
1155
|
+
type: "email",
|
|
1156
|
+
autocomplete: "email",
|
|
1157
|
+
value: email.value,
|
|
1158
|
+
placeholder: "Enter your email",
|
|
1159
|
+
required: true,
|
|
1160
|
+
disabled: isSubmitting.value,
|
|
1161
|
+
onInput: (e) => email.value = e.target.value
|
|
1162
|
+
})]),
|
|
1163
|
+
h("div", { "data-authos-field": "password" }, [h("label", { for: "authos-signup-password" }, "Password"), h("input", {
|
|
1164
|
+
id: "authos-signup-password",
|
|
1165
|
+
type: "password",
|
|
1166
|
+
autocomplete: "new-password",
|
|
1167
|
+
value: password.value,
|
|
1168
|
+
placeholder: "Create a password",
|
|
1169
|
+
required: true,
|
|
1170
|
+
disabled: isSubmitting.value,
|
|
1171
|
+
onInput: (e) => password.value = e.target.value
|
|
1172
|
+
})]),
|
|
1173
|
+
h("div", { "data-authos-field": "confirm-password" }, [h("label", { for: "authos-signup-confirm" }, "Confirm Password"), h("input", {
|
|
1174
|
+
id: "authos-signup-confirm",
|
|
1175
|
+
type: "password",
|
|
1176
|
+
autocomplete: "new-password",
|
|
1177
|
+
value: confirmPassword.value,
|
|
1178
|
+
placeholder: "Confirm your password",
|
|
1179
|
+
required: true,
|
|
1180
|
+
disabled: isSubmitting.value,
|
|
1181
|
+
onInput: (e) => confirmPassword.value = e.target.value
|
|
1182
|
+
})]),
|
|
1183
|
+
error.value && h("div", { "data-authos-error": "" }, error.value),
|
|
1184
|
+
h("button", {
|
|
1185
|
+
type: "submit",
|
|
1186
|
+
disabled: isSubmitting.value,
|
|
1187
|
+
"data-authos-submit": ""
|
|
1188
|
+
}, isSubmitting.value ? "Creating account..." : "Sign Up"),
|
|
1189
|
+
props.showSignIn && h("div", { "data-authos-signin-prompt": "" }, ["Already have an account? ", h("a", {
|
|
1190
|
+
href: "/signin",
|
|
1191
|
+
"data-authos-link": "signin"
|
|
1192
|
+
}, "Sign in")])
|
|
1193
|
+
])
|
|
1194
|
+
]);
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1161
1197
|
});
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
(org) => h("option", { key: org.organization.id, value: org.organization.slug }, org.organization.name)
|
|
1203
|
-
)
|
|
1204
|
-
) : "No organizations"
|
|
1205
|
-
);
|
|
1206
|
-
};
|
|
1207
|
-
}
|
|
1198
|
+
//#endregion
|
|
1199
|
+
//#region src/components/OrganizationSwitcher.ts
|
|
1200
|
+
const OrganizationSwitcher = defineComponent({
|
|
1201
|
+
name: "OrganizationSwitcher",
|
|
1202
|
+
props: { onSwitch: {
|
|
1203
|
+
type: Function,
|
|
1204
|
+
default: void 0
|
|
1205
|
+
} },
|
|
1206
|
+
emits: ["switch"],
|
|
1207
|
+
setup(props, { slots, emit }) {
|
|
1208
|
+
const { currentOrganization, organizations, switchOrganization, isSwitching } = useOrganization();
|
|
1209
|
+
async function switchTo(slug) {
|
|
1210
|
+
await switchOrganization(slug);
|
|
1211
|
+
const org = organizations.value.find((o) => o.organization.slug === slug);
|
|
1212
|
+
if (org) {
|
|
1213
|
+
emit("switch", org);
|
|
1214
|
+
props.onSwitch?.(org);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
return () => {
|
|
1218
|
+
const slotProps = {
|
|
1219
|
+
currentOrganization: currentOrganization.value,
|
|
1220
|
+
organizations: organizations.value,
|
|
1221
|
+
isSwitching: isSwitching.value,
|
|
1222
|
+
switchTo
|
|
1223
|
+
};
|
|
1224
|
+
if (slots.default) return slots.default(slotProps);
|
|
1225
|
+
return h("div", {
|
|
1226
|
+
"data-authos-orgswitcher": "",
|
|
1227
|
+
"data-state": organizations.value.length > 0 ? "ready" : "empty"
|
|
1228
|
+
}, organizations.value.length > 0 ? h("select", {
|
|
1229
|
+
value: currentOrganization.value?.organization.slug ?? "",
|
|
1230
|
+
disabled: isSwitching.value,
|
|
1231
|
+
onChange: (e) => switchTo(e.target.value)
|
|
1232
|
+
}, organizations.value.map((org) => h("option", {
|
|
1233
|
+
key: org.organization.id,
|
|
1234
|
+
value: org.organization.slug
|
|
1235
|
+
}, org.organization.name))) : "No organizations");
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1208
1238
|
});
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
};
|
|
1266
|
-
}
|
|
1239
|
+
//#endregion
|
|
1240
|
+
//#region src/components/UserButton.ts
|
|
1241
|
+
const UserButton = defineComponent({
|
|
1242
|
+
name: "UserButton",
|
|
1243
|
+
props: { onLogout: {
|
|
1244
|
+
type: Function,
|
|
1245
|
+
default: void 0
|
|
1246
|
+
} },
|
|
1247
|
+
emits: ["logout"],
|
|
1248
|
+
setup(props, { slots, emit }) {
|
|
1249
|
+
const { user, isLoading } = useUser();
|
|
1250
|
+
const { client } = useAuthOS();
|
|
1251
|
+
const isLoggingOut = ref(false);
|
|
1252
|
+
async function logout() {
|
|
1253
|
+
isLoggingOut.value = true;
|
|
1254
|
+
try {
|
|
1255
|
+
await client.auth.logout();
|
|
1256
|
+
emit("logout");
|
|
1257
|
+
props.onLogout?.();
|
|
1258
|
+
} finally {
|
|
1259
|
+
isLoggingOut.value = false;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
return () => {
|
|
1263
|
+
const slotProps = {
|
|
1264
|
+
user: user.value,
|
|
1265
|
+
isLoading: isLoading.value,
|
|
1266
|
+
isLoggingOut: isLoggingOut.value,
|
|
1267
|
+
logout
|
|
1268
|
+
};
|
|
1269
|
+
if (slots.default) return slots.default(slotProps);
|
|
1270
|
+
if (isLoading.value) return h("div", {
|
|
1271
|
+
"data-authos-userbutton": "",
|
|
1272
|
+
"data-state": "loading"
|
|
1273
|
+
}, "Loading...");
|
|
1274
|
+
if (!user.value) return h("div", {
|
|
1275
|
+
"data-authos-userbutton": "",
|
|
1276
|
+
"data-state": "signed-out"
|
|
1277
|
+
}, "Not signed in");
|
|
1278
|
+
const getInitials = (email) => {
|
|
1279
|
+
return email.split("@")[0].substring(0, 2).toUpperCase();
|
|
1280
|
+
};
|
|
1281
|
+
return h("div", {
|
|
1282
|
+
"data-authos-userbutton": "",
|
|
1283
|
+
"data-state": "signed-in"
|
|
1284
|
+
}, [
|
|
1285
|
+
h("div", { "data-authos-avatar": "" }, getInitials(user.value.email)),
|
|
1286
|
+
h("span", { "data-authos-email": "" }, user.value.email),
|
|
1287
|
+
h("button", {
|
|
1288
|
+
"data-authos-logout": "",
|
|
1289
|
+
onClick: logout,
|
|
1290
|
+
disabled: isLoggingOut.value
|
|
1291
|
+
}, isLoggingOut.value ? "Logging out..." : "Logout")
|
|
1292
|
+
]);
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1267
1295
|
});
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1296
|
+
//#endregion
|
|
1297
|
+
//#region src/components/Protect.ts
|
|
1298
|
+
const Protect = defineComponent({
|
|
1299
|
+
name: "Protect",
|
|
1300
|
+
props: {
|
|
1301
|
+
permission: {
|
|
1302
|
+
type: String,
|
|
1303
|
+
default: void 0
|
|
1304
|
+
},
|
|
1305
|
+
permissions: {
|
|
1306
|
+
type: Array,
|
|
1307
|
+
default: void 0
|
|
1308
|
+
},
|
|
1309
|
+
requireAll: {
|
|
1310
|
+
type: Boolean,
|
|
1311
|
+
default: false
|
|
1312
|
+
},
|
|
1313
|
+
fallback: {
|
|
1314
|
+
type: [Object, Function],
|
|
1315
|
+
default: void 0
|
|
1316
|
+
}
|
|
1317
|
+
},
|
|
1318
|
+
setup(props, { slots }) {
|
|
1319
|
+
const { user, isLoading } = useUser();
|
|
1320
|
+
const hasAccess = computed(() => {
|
|
1321
|
+
if (isLoading.value || !user.value) return false;
|
|
1322
|
+
const userPermissions = user.value.permissions ?? [];
|
|
1323
|
+
if (props.permission) return userPermissions.includes(props.permission);
|
|
1324
|
+
if (props.permissions && props.permissions.length > 0) {
|
|
1325
|
+
if (props.requireAll) return props.permissions.every((p) => userPermissions.includes(p));
|
|
1326
|
+
return props.permissions.some((p) => userPermissions.includes(p));
|
|
1327
|
+
}
|
|
1328
|
+
return true;
|
|
1329
|
+
});
|
|
1330
|
+
return () => {
|
|
1331
|
+
if (isLoading.value) return h("div", {
|
|
1332
|
+
"data-authos-protect": "",
|
|
1333
|
+
"data-state": "loading"
|
|
1334
|
+
});
|
|
1335
|
+
if (hasAccess.value) return h("div", {
|
|
1336
|
+
"data-authos-protect": "",
|
|
1337
|
+
"data-state": "allowed"
|
|
1338
|
+
}, slots.default?.());
|
|
1339
|
+
if (props.fallback) return h("div", {
|
|
1340
|
+
"data-authos-protect": "",
|
|
1341
|
+
"data-state": "denied"
|
|
1342
|
+
}, [typeof props.fallback === "function" ? props.fallback() : props.fallback]);
|
|
1343
|
+
if (slots.fallback) return h("div", {
|
|
1344
|
+
"data-authos-protect": "",
|
|
1345
|
+
"data-state": "denied"
|
|
1346
|
+
}, slots.fallback());
|
|
1347
|
+
return h("div", {
|
|
1348
|
+
"data-authos-protect": "",
|
|
1349
|
+
"data-state": "denied"
|
|
1350
|
+
});
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1323
1353
|
});
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1354
|
+
//#endregion
|
|
1355
|
+
//#region src/components/SignedIn.ts
|
|
1356
|
+
/**
|
|
1357
|
+
* Renders children only when the user is authenticated.
|
|
1358
|
+
* Use with SignedOut to create conditional UI based on auth state.
|
|
1359
|
+
*
|
|
1360
|
+
* @example
|
|
1361
|
+
* ```vue
|
|
1362
|
+
* <script setup>
|
|
1363
|
+
* import { SignedIn, SignedOut, UserButton, SignIn } from '@drmhse/authos-vue';
|
|
1364
|
+
* <\/script>
|
|
1365
|
+
*
|
|
1366
|
+
* <template>
|
|
1367
|
+
* <header>
|
|
1368
|
+
* <SignedIn>
|
|
1369
|
+
* <UserButton />
|
|
1370
|
+
* </SignedIn>
|
|
1371
|
+
* <SignedOut>
|
|
1372
|
+
* <SignIn />
|
|
1373
|
+
* </SignedOut>
|
|
1374
|
+
* </header>
|
|
1375
|
+
* </template>
|
|
1376
|
+
* ```
|
|
1377
|
+
*/
|
|
1378
|
+
const SignedIn = defineComponent({
|
|
1379
|
+
name: "SignedIn",
|
|
1380
|
+
setup(_, { slots }) {
|
|
1381
|
+
const { isAuthenticated, isLoading } = useAuthOS();
|
|
1382
|
+
return () => {
|
|
1383
|
+
if (isLoading.value) return null;
|
|
1384
|
+
if (!isAuthenticated.value) return null;
|
|
1385
|
+
return slots.default?.();
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1338
1388
|
});
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1389
|
+
//#endregion
|
|
1390
|
+
//#region src/components/SignedOut.ts
|
|
1391
|
+
/**
|
|
1392
|
+
* Renders children only when the user is NOT authenticated.
|
|
1393
|
+
* Use with SignedIn to create conditional UI based on auth state.
|
|
1394
|
+
*
|
|
1395
|
+
* @example
|
|
1396
|
+
* ```vue
|
|
1397
|
+
* <script setup>
|
|
1398
|
+
* import { SignedIn, SignedOut, UserButton, SignIn } from '@drmhse/authos-vue';
|
|
1399
|
+
* <\/script>
|
|
1400
|
+
*
|
|
1401
|
+
* <template>
|
|
1402
|
+
* <header>
|
|
1403
|
+
* <SignedIn>
|
|
1404
|
+
* <UserButton />
|
|
1405
|
+
* </SignedIn>
|
|
1406
|
+
* <SignedOut>
|
|
1407
|
+
* <SignIn />
|
|
1408
|
+
* </SignedOut>
|
|
1409
|
+
* </header>
|
|
1410
|
+
* </template>
|
|
1411
|
+
* ```
|
|
1412
|
+
*/
|
|
1413
|
+
const SignedOut = defineComponent({
|
|
1414
|
+
name: "SignedOut",
|
|
1415
|
+
setup(_, { slots }) {
|
|
1416
|
+
const { isAuthenticated, isLoading } = useAuthOS();
|
|
1417
|
+
return () => {
|
|
1418
|
+
if (isLoading.value) return null;
|
|
1419
|
+
if (isAuthenticated.value) return null;
|
|
1420
|
+
return slots.default?.();
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1353
1423
|
});
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1424
|
+
//#endregion
|
|
1425
|
+
//#region src/components/MagicLinkSignIn.ts
|
|
1426
|
+
/**
|
|
1427
|
+
* Passwordless sign-in via magic link (email).
|
|
1428
|
+
*
|
|
1429
|
+
* Sends a one-time login link to the user's email address.
|
|
1430
|
+
*
|
|
1431
|
+
* @example
|
|
1432
|
+
* ```vue
|
|
1433
|
+
* <script setup>
|
|
1434
|
+
* import { MagicLinkSignIn } from '@drmhse/authos-vue';
|
|
1435
|
+
* <\/script>
|
|
1436
|
+
*
|
|
1437
|
+
* <template>
|
|
1438
|
+
* <MagicLinkSignIn @success="() => alert('Check your email!')" />
|
|
1439
|
+
* </template>
|
|
1440
|
+
* ```
|
|
1441
|
+
*/
|
|
1442
|
+
const MagicLinkSignIn = defineComponent({
|
|
1443
|
+
name: "MagicLinkSignIn",
|
|
1444
|
+
props: { showPasswordSignIn: {
|
|
1445
|
+
type: Boolean,
|
|
1446
|
+
default: true
|
|
1447
|
+
} },
|
|
1448
|
+
emits: ["success", "error"],
|
|
1449
|
+
setup(props, { emit, slots }) {
|
|
1450
|
+
const { client } = useAuthOS();
|
|
1451
|
+
const email = ref("");
|
|
1452
|
+
const isLoading = ref(false);
|
|
1453
|
+
const error = ref(null);
|
|
1454
|
+
const isSent = ref(false);
|
|
1455
|
+
async function handleSubmit() {
|
|
1456
|
+
error.value = null;
|
|
1457
|
+
isLoading.value = true;
|
|
1458
|
+
try {
|
|
1459
|
+
await client.magicLinks.request({ email: email.value });
|
|
1460
|
+
isSent.value = true;
|
|
1461
|
+
emit("success");
|
|
1462
|
+
} catch (err) {
|
|
1463
|
+
error.value = err instanceof Error ? err.message : "Failed to send magic link";
|
|
1464
|
+
emit("error", err);
|
|
1465
|
+
} finally {
|
|
1466
|
+
isLoading.value = false;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
const slotProps = computed(() => ({
|
|
1470
|
+
email: email.value,
|
|
1471
|
+
isLoading: isLoading.value,
|
|
1472
|
+
error: error.value,
|
|
1473
|
+
isSent: isSent.value,
|
|
1474
|
+
updateEmail: (val) => {
|
|
1475
|
+
email.value = val;
|
|
1476
|
+
},
|
|
1477
|
+
submit: handleSubmit,
|
|
1478
|
+
reset: () => {
|
|
1479
|
+
isSent.value = false;
|
|
1480
|
+
}
|
|
1481
|
+
}));
|
|
1482
|
+
return () => {
|
|
1483
|
+
if (slots.default) return slots.default(slotProps.value);
|
|
1484
|
+
if (isSent.value) return h("div", {
|
|
1485
|
+
"data-authos-magic-link": "",
|
|
1486
|
+
"data-state": "sent"
|
|
1487
|
+
}, [h("div", { "data-authos-success": "" }, [h("p", "Check your email!"), h("p", ["We sent a login link to ", h("strong", email.value)])]), h("button", {
|
|
1488
|
+
type: "button",
|
|
1489
|
+
onClick: () => {
|
|
1490
|
+
isSent.value = false;
|
|
1491
|
+
},
|
|
1492
|
+
"data-authos-back": ""
|
|
1493
|
+
}, "Use a different email")]);
|
|
1494
|
+
return h("form", {
|
|
1495
|
+
onSubmit: (e) => {
|
|
1496
|
+
e.preventDefault();
|
|
1497
|
+
handleSubmit();
|
|
1498
|
+
},
|
|
1499
|
+
"data-authos-magic-link": "",
|
|
1500
|
+
"data-state": "form"
|
|
1501
|
+
}, [
|
|
1502
|
+
h("div", { "data-authos-field": "email" }, [h("label", { for: "authos-magic-email" }, "Email"), h("input", {
|
|
1503
|
+
id: "authos-magic-email",
|
|
1504
|
+
type: "email",
|
|
1505
|
+
autocomplete: "email",
|
|
1506
|
+
value: email.value,
|
|
1507
|
+
onInput: (e) => {
|
|
1508
|
+
email.value = e.target.value;
|
|
1509
|
+
},
|
|
1510
|
+
placeholder: "Enter your email",
|
|
1511
|
+
required: true,
|
|
1512
|
+
disabled: isLoading.value
|
|
1513
|
+
})]),
|
|
1514
|
+
error.value ? h("div", { "data-authos-error": "" }, error.value) : null,
|
|
1515
|
+
h("button", {
|
|
1516
|
+
type: "submit",
|
|
1517
|
+
disabled: isLoading.value,
|
|
1518
|
+
"data-authos-submit": ""
|
|
1519
|
+
}, isLoading.value ? "Sending..." : "Send Magic Link"),
|
|
1520
|
+
props.showPasswordSignIn ? h("div", { "data-authos-signin-prompt": "" }, [h("a", {
|
|
1521
|
+
href: "/signin",
|
|
1522
|
+
"data-authos-link": "signin"
|
|
1523
|
+
}, "Sign in with password")]) : null
|
|
1524
|
+
]);
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1451
1527
|
});
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1528
|
+
//#endregion
|
|
1529
|
+
//#region src/components/PasskeySignIn.ts
|
|
1530
|
+
/**
|
|
1531
|
+
* Passkey (WebAuthn) sign-in component.
|
|
1532
|
+
*
|
|
1533
|
+
* Uses the Web Authentication API for passwordless authentication
|
|
1534
|
+
* via biometrics, security keys, or platform authenticators.
|
|
1535
|
+
*
|
|
1536
|
+
* @example
|
|
1537
|
+
* ```vue
|
|
1538
|
+
* <script setup>
|
|
1539
|
+
* import { PasskeySignIn } from '@drmhse/authos-vue';
|
|
1540
|
+
* <\/script>
|
|
1541
|
+
*
|
|
1542
|
+
* <template>
|
|
1543
|
+
* <PasskeySignIn @success="() => router.push('/dashboard')" />
|
|
1544
|
+
* </template>
|
|
1545
|
+
* ```
|
|
1546
|
+
*/
|
|
1547
|
+
const PasskeySignIn = defineComponent({
|
|
1548
|
+
name: "PasskeySignIn",
|
|
1549
|
+
props: {
|
|
1550
|
+
showPasswordSignIn: {
|
|
1551
|
+
type: Boolean,
|
|
1552
|
+
default: true
|
|
1553
|
+
},
|
|
1554
|
+
orgSlug: {
|
|
1555
|
+
type: String,
|
|
1556
|
+
default: void 0
|
|
1557
|
+
},
|
|
1558
|
+
serviceSlug: {
|
|
1559
|
+
type: String,
|
|
1560
|
+
default: void 0
|
|
1561
|
+
},
|
|
1562
|
+
redirectUri: {
|
|
1563
|
+
type: String,
|
|
1564
|
+
default: void 0
|
|
1565
|
+
},
|
|
1566
|
+
state: {
|
|
1567
|
+
type: String,
|
|
1568
|
+
default: void 0
|
|
1569
|
+
}
|
|
1570
|
+
},
|
|
1571
|
+
emits: ["success", "error"],
|
|
1572
|
+
setup(props, { emit, slots }) {
|
|
1573
|
+
const { client, options } = useAuthOS();
|
|
1574
|
+
const email = ref("");
|
|
1575
|
+
const isLoading = ref(false);
|
|
1576
|
+
const error = ref(null);
|
|
1577
|
+
const isSupported = ref(true);
|
|
1578
|
+
onMounted(() => {
|
|
1579
|
+
isSupported.value = client.passkeys.isSupported();
|
|
1580
|
+
});
|
|
1581
|
+
async function handleSubmit() {
|
|
1582
|
+
error.value = null;
|
|
1583
|
+
isLoading.value = true;
|
|
1584
|
+
try {
|
|
1585
|
+
const org = props.orgSlug ?? options.org;
|
|
1586
|
+
const service = props.serviceSlug ?? options.service;
|
|
1587
|
+
const passkeyContext = org && service ? {
|
|
1588
|
+
org_slug: org,
|
|
1589
|
+
service_slug: service,
|
|
1590
|
+
redirect_uri: props.redirectUri ?? options.redirectUri,
|
|
1591
|
+
state: props.state
|
|
1592
|
+
} : void 0;
|
|
1593
|
+
await client.passkeys.login(email.value, passkeyContext);
|
|
1594
|
+
emit("success");
|
|
1595
|
+
} catch (err) {
|
|
1596
|
+
error.value = err instanceof Error ? err.message : "Passkey authentication failed";
|
|
1597
|
+
emit("error", err);
|
|
1598
|
+
} finally {
|
|
1599
|
+
isLoading.value = false;
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
const slotProps = computed(() => ({
|
|
1603
|
+
email: email.value,
|
|
1604
|
+
isLoading: isLoading.value,
|
|
1605
|
+
error: error.value,
|
|
1606
|
+
isSupported: isSupported.value,
|
|
1607
|
+
updateEmail: (val) => {
|
|
1608
|
+
email.value = val;
|
|
1609
|
+
},
|
|
1610
|
+
submit: handleSubmit
|
|
1611
|
+
}));
|
|
1612
|
+
return () => {
|
|
1613
|
+
if (slots.default) return slots.default(slotProps.value);
|
|
1614
|
+
if (!isSupported.value) return h("div", {
|
|
1615
|
+
"data-authos-passkey": "",
|
|
1616
|
+
"data-state": "unsupported"
|
|
1617
|
+
}, [h("div", { "data-authos-error": "" }, "Passkeys are not supported in this browser."), props.showPasswordSignIn ? h("a", {
|
|
1618
|
+
href: "/signin",
|
|
1619
|
+
"data-authos-link": "signin"
|
|
1620
|
+
}, "Sign in with password") : null]);
|
|
1621
|
+
return h("form", {
|
|
1622
|
+
onSubmit: (e) => {
|
|
1623
|
+
e.preventDefault();
|
|
1624
|
+
handleSubmit();
|
|
1625
|
+
},
|
|
1626
|
+
"data-authos-passkey": ""
|
|
1627
|
+
}, [
|
|
1628
|
+
h("div", { "data-authos-field": "email" }, [h("label", { for: "authos-passkey-email" }, "Email"), h("input", {
|
|
1629
|
+
id: "authos-passkey-email",
|
|
1630
|
+
type: "email",
|
|
1631
|
+
autocomplete: "email webauthn",
|
|
1632
|
+
value: email.value,
|
|
1633
|
+
onInput: (e) => {
|
|
1634
|
+
email.value = e.target.value;
|
|
1635
|
+
},
|
|
1636
|
+
placeholder: "Enter your email",
|
|
1637
|
+
required: true,
|
|
1638
|
+
disabled: isLoading.value
|
|
1639
|
+
})]),
|
|
1640
|
+
error.value ? h("div", { "data-authos-error": "" }, error.value) : null,
|
|
1641
|
+
h("button", {
|
|
1642
|
+
type: "submit",
|
|
1643
|
+
disabled: isLoading.value,
|
|
1644
|
+
"data-authos-submit": ""
|
|
1645
|
+
}, isLoading.value ? "Authenticating..." : "Sign in with Passkey"),
|
|
1646
|
+
props.showPasswordSignIn ? h("div", { "data-authos-signin-prompt": "" }, [h("a", {
|
|
1647
|
+
href: "/signin",
|
|
1648
|
+
"data-authos-link": "signin"
|
|
1649
|
+
}, "Sign in with password")]) : null
|
|
1650
|
+
]);
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1565
1653
|
});
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
return h("div", { "data-authos-callback": "" }, [
|
|
1632
|
-
error.value ? h("div", { "data-authos-error": "" }, error.value) : h("div", { "data-authos-loading": "" }, "Completing sign in...")
|
|
1633
|
-
]);
|
|
1634
|
-
};
|
|
1635
|
-
}
|
|
1654
|
+
//#endregion
|
|
1655
|
+
//#region src/components/Callback.ts
|
|
1656
|
+
const Callback = defineComponent({
|
|
1657
|
+
name: "Callback",
|
|
1658
|
+
props: {
|
|
1659
|
+
onSuccess: {
|
|
1660
|
+
type: Function,
|
|
1661
|
+
default: void 0
|
|
1662
|
+
},
|
|
1663
|
+
onError: {
|
|
1664
|
+
type: Function,
|
|
1665
|
+
default: void 0
|
|
1666
|
+
}
|
|
1667
|
+
},
|
|
1668
|
+
emits: ["success", "error"],
|
|
1669
|
+
setup(props, { slots, emit }) {
|
|
1670
|
+
const { client } = useAuthOS();
|
|
1671
|
+
const error = ref(null);
|
|
1672
|
+
onMounted(async () => {
|
|
1673
|
+
if (!client) {
|
|
1674
|
+
error.value = "AuthOS client not initialized";
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
const hashParams = new URLSearchParams(window.location.hash.substring(1));
|
|
1678
|
+
const queryParams = new URLSearchParams(window.location.search);
|
|
1679
|
+
const accessToken = hashParams.get("access_token") || queryParams.get("access_token");
|
|
1680
|
+
const refreshToken = hashParams.get("refresh_token") || queryParams.get("refresh_token");
|
|
1681
|
+
const errorParam = hashParams.get("error") || queryParams.get("error");
|
|
1682
|
+
const errorDescription = hashParams.get("error_description") || queryParams.get("error_description");
|
|
1683
|
+
if (errorParam) {
|
|
1684
|
+
const msg = errorDescription || errorParam;
|
|
1685
|
+
error.value = msg;
|
|
1686
|
+
const e = new Error(msg);
|
|
1687
|
+
emit("error", e);
|
|
1688
|
+
props.onError?.(e);
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
if (accessToken) try {
|
|
1692
|
+
await client.setSession({
|
|
1693
|
+
access_token: accessToken,
|
|
1694
|
+
refresh_token: refreshToken || void 0
|
|
1695
|
+
});
|
|
1696
|
+
emit("success");
|
|
1697
|
+
props.onSuccess?.();
|
|
1698
|
+
} catch (err) {
|
|
1699
|
+
const message = err.message || "Failed to set session";
|
|
1700
|
+
error.value = message;
|
|
1701
|
+
const e = err instanceof Error ? err : new Error(message);
|
|
1702
|
+
emit("error", e);
|
|
1703
|
+
props.onError?.(e);
|
|
1704
|
+
}
|
|
1705
|
+
else {
|
|
1706
|
+
const message = "No authentication tokens found in callback URL.";
|
|
1707
|
+
error.value = message;
|
|
1708
|
+
const e = /* @__PURE__ */ new Error(message);
|
|
1709
|
+
emit("error", e);
|
|
1710
|
+
props.onError?.(e);
|
|
1711
|
+
}
|
|
1712
|
+
});
|
|
1713
|
+
return () => {
|
|
1714
|
+
const slotProps = { error: error.value };
|
|
1715
|
+
if (slots.default) return slots.default(slotProps);
|
|
1716
|
+
return h("div", { "data-authos-callback": "" }, [error.value ? h("div", { "data-authos-error": "" }, error.value) : h("div", { "data-authos-loading": "" }, "Completing sign in...")]);
|
|
1717
|
+
};
|
|
1718
|
+
}
|
|
1636
1719
|
});
|
|
1637
|
-
|
|
1638
|
-
export { AuthOSProvider, Callback, MagicLinkSignIn, OAuthButton, OrganizationSwitcher, PasskeySignIn, Protect, SignIn, SignUp, SignedIn, SignedOut, UserButton, createAuthOS, useAllPermissions, useAnyPermission, useOrganization, usePermission, useUser };
|
|
1720
|
+
//#endregion
|
|
1721
|
+
export { AUTH_OS_INJECTION_KEY, AuthErrorCodes, AuthOSProvider, BrowserStorage, Callback, MagicLinkSignIn, MemoryStorage, OAuthButton, OrganizationSwitcher, PasskeySignIn, Protect, SignIn, SignUp, SignedIn, SignedOut, SsoApiError, UserButton, createAuthOS, useAllPermissions, useAnyPermission, useAuthOS, useOrganization, usePermission, useUser };
|