or3-provider-basic-auth 0.0.1

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.
Files changed (73) hide show
  1. package/README.md +99 -0
  2. package/dist/module.d.mts +5 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +42 -0
  5. package/dist/runtime/components/BasicAuthChangePasswordModal.client.d.vue.ts +12 -0
  6. package/dist/runtime/components/BasicAuthChangePasswordModal.client.vue +133 -0
  7. package/dist/runtime/components/BasicAuthChangePasswordModal.client.vue.d.ts +12 -0
  8. package/dist/runtime/components/BasicAuthRegisterModal.client.d.vue.ts +11 -0
  9. package/dist/runtime/components/BasicAuthRegisterModal.client.vue +164 -0
  10. package/dist/runtime/components/BasicAuthRegisterModal.client.vue.d.ts +11 -0
  11. package/dist/runtime/components/BasicAuthSignInModal.client.d.vue.ts +13 -0
  12. package/dist/runtime/components/BasicAuthSignInModal.client.vue +136 -0
  13. package/dist/runtime/components/BasicAuthSignInModal.client.vue.d.ts +13 -0
  14. package/dist/runtime/components/BasicAuthUserMenu.client.d.vue.ts +12 -0
  15. package/dist/runtime/components/BasicAuthUserMenu.client.vue +106 -0
  16. package/dist/runtime/components/BasicAuthUserMenu.client.vue.d.ts +12 -0
  17. package/dist/runtime/components/SidebarAuthButtonBasic.client.d.vue.ts +2 -0
  18. package/dist/runtime/components/SidebarAuthButtonBasic.client.vue +154 -0
  19. package/dist/runtime/components/SidebarAuthButtonBasic.client.vue.d.ts +2 -0
  20. package/dist/runtime/lib/constants.d.ts +7 -0
  21. package/dist/runtime/lib/constants.js +7 -0
  22. package/dist/runtime/plugins/auth-status.client.d.ts +2 -0
  23. package/dist/runtime/plugins/auth-status.client.js +38 -0
  24. package/dist/runtime/plugins/basic-auth-ui.client.d.ts +2 -0
  25. package/dist/runtime/plugins/basic-auth-ui.client.js +46 -0
  26. package/dist/runtime/server/admin/adapters/auth-basic-auth.d.ts +2 -0
  27. package/dist/runtime/server/admin/adapters/auth-basic-auth.js +34 -0
  28. package/dist/runtime/server/api/basic-auth/_helpers.d.ts +6 -0
  29. package/dist/runtime/server/api/basic-auth/_helpers.js +26 -0
  30. package/dist/runtime/server/api/basic-auth/change-password.post.d.ts +8 -0
  31. package/dist/runtime/server/api/basic-auth/change-password.post.js +49 -0
  32. package/dist/runtime/server/api/basic-auth/refresh.post.d.ts +8 -0
  33. package/dist/runtime/server/api/basic-auth/refresh.post.js +78 -0
  34. package/dist/runtime/server/api/basic-auth/register.post.d.ts +8 -0
  35. package/dist/runtime/server/api/basic-auth/register.post.js +112 -0
  36. package/dist/runtime/server/api/basic-auth/sign-in.post.d.ts +8 -0
  37. package/dist/runtime/server/api/basic-auth/sign-in.post.js +75 -0
  38. package/dist/runtime/server/api/basic-auth/sign-out.post.d.ts +8 -0
  39. package/dist/runtime/server/api/basic-auth/sign-out.post.js +37 -0
  40. package/dist/runtime/server/auth/basic-auth-provider.d.ts +2 -0
  41. package/dist/runtime/server/auth/basic-auth-provider.js +41 -0
  42. package/dist/runtime/server/auth/index.d.ts +1 -0
  43. package/dist/runtime/server/auth/index.js +1 -0
  44. package/dist/runtime/server/db/client.d.ts +3 -0
  45. package/dist/runtime/server/db/client.js +106 -0
  46. package/dist/runtime/server/db/schema.d.ts +21 -0
  47. package/dist/runtime/server/db/schema.js +0 -0
  48. package/dist/runtime/server/lib/config.d.ts +22 -0
  49. package/dist/runtime/server/lib/config.js +94 -0
  50. package/dist/runtime/server/lib/cookies.d.ts +4 -0
  51. package/dist/runtime/server/lib/cookies.js +42 -0
  52. package/dist/runtime/server/lib/errors.d.ts +4 -0
  53. package/dist/runtime/server/lib/errors.js +25 -0
  54. package/dist/runtime/server/lib/jwt.d.ts +37 -0
  55. package/dist/runtime/server/lib/jwt.js +77 -0
  56. package/dist/runtime/server/lib/password.d.ts +2 -0
  57. package/dist/runtime/server/lib/password.js +8 -0
  58. package/dist/runtime/server/lib/rate-limit.d.ts +6 -0
  59. package/dist/runtime/server/lib/rate-limit.js +163 -0
  60. package/dist/runtime/server/lib/request-identity.d.ts +16 -0
  61. package/dist/runtime/server/lib/request-identity.js +32 -0
  62. package/dist/runtime/server/lib/request-security.d.ts +2 -0
  63. package/dist/runtime/server/lib/request-security.js +37 -0
  64. package/dist/runtime/server/lib/session-store.d.ts +46 -0
  65. package/dist/runtime/server/lib/session-store.js +190 -0
  66. package/dist/runtime/server/plugins/register.d.ts +2 -0
  67. package/dist/runtime/server/plugins/register.js +63 -0
  68. package/dist/runtime/server/token-broker/basic-auth-token-broker.d.ts +6 -0
  69. package/dist/runtime/server/token-broker/basic-auth-token-broker.js +8 -0
  70. package/dist/runtime/server/token-broker/index.d.ts +1 -0
  71. package/dist/runtime/server/token-broker/index.js +1 -0
  72. package/dist/types.d.mts +7 -0
  73. package/package.json +70 -0
package/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # or3-provider-basic-auth
2
+
3
+ Basic-auth provider for OR3 Chat SSR mode.
4
+
5
+ This package registers:
6
+
7
+ - `AuthProvider` (`basic-auth`)
8
+ - `ProviderTokenBroker` (`basic-auth`)
9
+ - provider-owned auth UI adapter + components
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ bun add or3-provider-basic-auth
15
+ ```
16
+
17
+ For local development from the OR3 monorepo:
18
+
19
+ ```bash
20
+ # from /Users/brendon/Documents/or3/or3-chat
21
+ bun add or3-provider-basic-auth@link:../or3-provider-basic-auth
22
+ ```
23
+
24
+ ## Runtime registration
25
+
26
+ Add the module to your generated provider list:
27
+
28
+ ```ts
29
+ export const or3ProviderModules = [
30
+ 'or3-provider-basic-auth/nuxt'
31
+ ] as const;
32
+ ```
33
+
34
+ ## Environment
35
+
36
+ Required:
37
+
38
+ - `AUTH_PROVIDER=basic-auth`
39
+ - `OR3_BASIC_AUTH_JWT_SECRET`
40
+
41
+ Optional:
42
+
43
+ - `OR3_BASIC_AUTH_REFRESH_SECRET` (falls back to `OR3_BASIC_AUTH_JWT_SECRET`)
44
+ - `OR3_BASIC_AUTH_ACCESS_TTL_SECONDS` (default `900`)
45
+ - `OR3_BASIC_AUTH_REFRESH_TTL_SECONDS` (default `2592000`)
46
+ - `OR3_BASIC_AUTH_DB_PATH` (default `./.data/or3-basic-auth.sqlite`)
47
+ - `OR3_BASIC_AUTH_BOOTSTRAP_EMAIL`
48
+ - `OR3_BASIC_AUTH_BOOTSTRAP_PASSWORD`
49
+
50
+ Strict-mode behavior:
51
+
52
+ - `NODE_ENV=production` or `OR3_STRICT_CONFIG=true` fails startup if required secrets are missing.
53
+ - non-strict mode logs diagnostics and leaves provider registration disabled.
54
+
55
+ ## Architecture notes
56
+
57
+ - Credentials/session state is stored in a provider-owned SQLite DB.
58
+ - Canonical OR3 users/workspaces are still resolved by the selected `AuthWorkspaceStore`.
59
+ - Access JWTs are short-lived and validated by `basicAuthProvider.getSession(event)`.
60
+ - Refresh tokens are rotated and hashed at rest; replay attempts revoke active sessions.
61
+
62
+ ## Troubleshooting
63
+
64
+ - `Authentication provider is not configured`
65
+ - Ensure `AUTH_PROVIDER=basic-auth` and `SSR_AUTH_ENABLED=true`.
66
+ - Ensure `OR3_BASIC_AUTH_JWT_SECRET` is set.
67
+
68
+ - `Missing OR3_BASIC_AUTH_JWT_SECRET`
69
+ - In strict mode (`NODE_ENV=production` or `OR3_STRICT_CONFIG=true`), startup fails intentionally.
70
+ - Add the missing secret and restart.
71
+
72
+ - `Invalid credentials` on known user
73
+ - Verify the bootstrap account values.
74
+ - Confirm password updates were propagated after recent `change-password` calls.
75
+
76
+ - `Session expired` during refresh
77
+ - Refresh token may be revoked/rotated/replayed.
78
+ - Sign in again to create a new session chain.
79
+
80
+ ## Intern quick start
81
+
82
+ Implementation order used in this package:
83
+
84
+ 1. `src/runtime/server/lib/password.ts`
85
+ 2. `src/runtime/server/lib/jwt.ts`
86
+ 3. `src/runtime/server/db/client.ts` and `src/runtime/server/lib/session-store.ts`
87
+ 4. `src/runtime/server/api/basic-auth/*.post.ts`
88
+ 5. `src/runtime/server/auth/basic-auth-provider.ts`
89
+ 6. `src/runtime/server/plugins/register.ts`
90
+ 7. `src/runtime/components/*.client.vue` and `src/runtime/plugins/basic-auth-ui.client.ts`
91
+ 8. `src/runtime/**/__tests__/*.test.ts`
92
+
93
+ ## Scripts
94
+
95
+ ```bash
96
+ bun run type-check
97
+ bun run test
98
+ bun run build
99
+ ```
@@ -0,0 +1,5 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ declare const _default: _nuxt_schema.NuxtModule<_nuxt_schema.ModuleOptions, _nuxt_schema.ModuleOptions, false>;
4
+
5
+ export { _default as default };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "or3-provider-basic-auth",
3
+ "configKey": "or3-provider-basic-auth",
4
+ "version": "0.0.1",
5
+ "builder": {
6
+ "@nuxt/module-builder": "1.0.2",
7
+ "unbuild": "3.6.1"
8
+ }
9
+ }
@@ -0,0 +1,42 @@
1
+ import { defineNuxtModule, createResolver, addServerPlugin, addServerHandler, addPlugin } from '@nuxt/kit';
2
+
3
+ const module$1 = defineNuxtModule({
4
+ meta: { name: "or3-provider-basic-auth" },
5
+ setup() {
6
+ const { resolve } = createResolver(import.meta.url);
7
+ addServerPlugin(resolve("runtime/server/plugins/register"));
8
+ addServerHandler({
9
+ route: "/api/basic-auth/sign-in",
10
+ method: "post",
11
+ handler: resolve("runtime/server/api/basic-auth/sign-in.post")
12
+ });
13
+ addServerHandler({
14
+ route: "/api/basic-auth/register",
15
+ method: "post",
16
+ handler: resolve("runtime/server/api/basic-auth/register.post")
17
+ });
18
+ addServerHandler({
19
+ route: "/api/basic-auth/sign-out",
20
+ method: "post",
21
+ handler: resolve("runtime/server/api/basic-auth/sign-out.post")
22
+ });
23
+ addServerHandler({
24
+ route: "/api/basic-auth/refresh",
25
+ method: "post",
26
+ handler: resolve("runtime/server/api/basic-auth/refresh.post")
27
+ });
28
+ addServerHandler({
29
+ route: "/api/basic-auth/change-password",
30
+ method: "post",
31
+ handler: resolve("runtime/server/api/basic-auth/change-password.post")
32
+ });
33
+ addPlugin(resolve("runtime/plugins/basic-auth-ui.client"), {
34
+ append: true
35
+ });
36
+ addPlugin(resolve("runtime/plugins/auth-status.client"), {
37
+ append: true
38
+ });
39
+ }
40
+ });
41
+
42
+ export { module$1 as default };
@@ -0,0 +1,12 @@
1
+ type __VLS_Props = {
2
+ modelValue: boolean;
3
+ username?: string;
4
+ };
5
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
+ "update:modelValue": (value: boolean) => any;
7
+ updated: () => any;
8
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
+ "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
10
+ onUpdated?: (() => any) | undefined;
11
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ export default _default;
@@ -0,0 +1,133 @@
1
+ <template>
2
+ <UModal
3
+ v-model:open="isOpen"
4
+ title="Change Password"
5
+ description="Update your account password"
6
+ :ui="modalUi"
7
+ >
8
+ <template #body>
9
+ <UForm :state="state" @submit.prevent="onSubmit">
10
+ <div class="flex flex-col gap-4">
11
+ <input
12
+ :value="props.username ?? ''"
13
+ type="text"
14
+ autocomplete="username"
15
+ tabindex="-1"
16
+ aria-hidden="true"
17
+ class="sr-only pointer-events-none h-0 w-0 opacity-0"
18
+ readonly
19
+ />
20
+
21
+ <UFormField label="Current Password" name="currentPassword">
22
+ <UInput
23
+ v-model="state.currentPassword"
24
+ type="password"
25
+ placeholder="••••••••"
26
+ autocomplete="current-password"
27
+ required
28
+ class="w-full"
29
+ />
30
+ </UFormField>
31
+
32
+ <USeparator />
33
+
34
+ <UFormField label="New Password" name="newPassword">
35
+ <UInput
36
+ v-model="state.newPassword"
37
+ type="password"
38
+ placeholder="••••••••"
39
+ autocomplete="new-password"
40
+ required
41
+ class="w-full"
42
+ />
43
+ </UFormField>
44
+
45
+ <UFormField label="Confirm New Password" name="confirmNewPassword">
46
+ <UInput
47
+ v-model="state.confirmNewPassword"
48
+ type="password"
49
+ placeholder="••••••••"
50
+ autocomplete="new-password"
51
+ required
52
+ class="w-full"
53
+ />
54
+ </UFormField>
55
+
56
+ <div
57
+ v-if="errorMessage"
58
+ class="flex items-start gap-2 rounded-[var(--md-border-radius)] border border-[var(--md-error)]/30 bg-[var(--md-error)]/8 px-3 py-2.5 text-sm text-[var(--md-error)]"
59
+ >
60
+ <UIcon name="i-lucide-alert-circle" class="w-4 h-4 mt-0.5 shrink-0" />
61
+ {{ errorMessage }}
62
+ </div>
63
+
64
+ <div class="flex justify-end gap-2 pt-1">
65
+ <UButton
66
+ color="neutral"
67
+ variant="outline"
68
+ type="button"
69
+ :disabled="pending"
70
+ @click="close"
71
+ >
72
+ Cancel
73
+ </UButton>
74
+ <UButton type="submit" :loading="pending">
75
+ Update Password
76
+ </UButton>
77
+ </div>
78
+ </div>
79
+ </UForm>
80
+ </template>
81
+ </UModal>
82
+ </template>
83
+
84
+ <script setup>
85
+ import { computed, reactive, ref } from "vue";
86
+ const modalUi = {
87
+ content: "sm:max-w-[400px]"
88
+ };
89
+ const props = defineProps({
90
+ modelValue: { type: Boolean, required: true },
91
+ username: { type: String, required: false }
92
+ });
93
+ const emit = defineEmits(["update:modelValue", "updated"]);
94
+ const pending = ref(false);
95
+ const errorMessage = ref("");
96
+ const state = reactive({
97
+ currentPassword: "",
98
+ newPassword: "",
99
+ confirmNewPassword: ""
100
+ });
101
+ const isOpen = computed({
102
+ get: () => props.modelValue,
103
+ set: (value) => emit("update:modelValue", value)
104
+ });
105
+ function close() {
106
+ errorMessage.value = "";
107
+ emit("update:modelValue", false);
108
+ }
109
+ async function onSubmit() {
110
+ if (state.newPassword !== state.confirmNewPassword) {
111
+ errorMessage.value = "Passwords do not match";
112
+ return;
113
+ }
114
+ pending.value = true;
115
+ errorMessage.value = "";
116
+ try {
117
+ await $fetch("/api/basic-auth/change-password", {
118
+ method: "POST",
119
+ body: {
120
+ currentPassword: state.currentPassword,
121
+ newPassword: state.newPassword,
122
+ confirmNewPassword: state.confirmNewPassword
123
+ }
124
+ });
125
+ emit("updated");
126
+ close();
127
+ } catch {
128
+ errorMessage.value = "Unable to change password";
129
+ } finally {
130
+ pending.value = false;
131
+ }
132
+ }
133
+ </script>
@@ -0,0 +1,12 @@
1
+ type __VLS_Props = {
2
+ modelValue: boolean;
3
+ username?: string;
4
+ };
5
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
+ "update:modelValue": (value: boolean) => any;
7
+ updated: () => any;
8
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
+ "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
10
+ onUpdated?: (() => any) | undefined;
11
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ export default _default;
@@ -0,0 +1,11 @@
1
+ type __VLS_Props = {
2
+ modelValue: boolean;
3
+ };
4
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
5
+ "update:modelValue": (value: boolean) => any;
6
+ registered: () => any;
7
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
8
+ "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
9
+ onRegistered?: (() => any) | undefined;
10
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
11
+ export default _default;
@@ -0,0 +1,164 @@
1
+ <template>
2
+ <UModal
3
+ v-model:open="isOpen"
4
+ title="Create Account"
5
+ description="Register a new account"
6
+ :close="{
7
+ class: 'theme-btn',
8
+ size: 'sm'
9
+ }"
10
+ :ui="modalUi"
11
+ >
12
+ <template #body>
13
+ <UForm :state="state" @submit.prevent="onSubmit">
14
+ <div class="flex flex-col gap-4">
15
+ <input
16
+ :value="state.email"
17
+ type="text"
18
+ autocomplete="username"
19
+ tabindex="-1"
20
+ aria-hidden="true"
21
+ class="sr-only pointer-events-none h-0 w-0 opacity-0"
22
+ readonly
23
+ />
24
+
25
+ <UFormField label="Email" name="email">
26
+ <UInput
27
+ v-model="state.email"
28
+ type="email"
29
+ placeholder="you@example.com"
30
+ autocomplete="username"
31
+ required
32
+ class="w-full"
33
+ />
34
+ </UFormField>
35
+
36
+ <UFormField label="Display name" name="displayName">
37
+ <UInput
38
+ v-model="state.displayName"
39
+ type="text"
40
+ placeholder="Your name"
41
+ autocomplete="name"
42
+ class="w-full"
43
+ />
44
+ </UFormField>
45
+
46
+ <UFormField label="Password" name="password">
47
+ <UInput
48
+ v-model="state.password"
49
+ type="password"
50
+ placeholder="••••••••"
51
+ autocomplete="new-password"
52
+ required
53
+ class="w-full"
54
+ />
55
+ </UFormField>
56
+
57
+ <UFormField label="Confirm password" name="confirmPassword">
58
+ <UInput
59
+ v-model="state.confirmPassword"
60
+ type="password"
61
+ placeholder="••••••••"
62
+ autocomplete="new-password"
63
+ required
64
+ class="w-full"
65
+ />
66
+ </UFormField>
67
+
68
+ <UFormField label="Invite token (optional)" name="inviteToken">
69
+ <UInput
70
+ v-model="state.inviteToken"
71
+ type="text"
72
+ placeholder="paste invite token"
73
+ class="w-full"
74
+ />
75
+ </UFormField>
76
+
77
+ <div
78
+ v-if="errorMessage"
79
+ class="flex items-start gap-2 rounded-[var(--md-border-radius)] border border-[var(--md-error)]/30 bg-[var(--md-error)]/8 px-3 py-2.5 text-sm text-[var(--md-error)]"
80
+ >
81
+ <UIcon name="i-lucide-alert-circle" class="w-4 h-4 mt-0.5 shrink-0" />
82
+ {{ errorMessage }}
83
+ </div>
84
+
85
+ <div class="flex justify-end gap-2 pt-1">
86
+ <UButton
87
+ color="neutral"
88
+ variant="outline"
89
+ type="button"
90
+ :disabled="pending"
91
+ @click="close"
92
+ >
93
+ Cancel
94
+ </UButton>
95
+ <UButton class="theme-btn new-act-btn" type="submit" :loading="pending">
96
+ Create account
97
+ </UButton>
98
+ </div>
99
+ </div>
100
+ </UForm>
101
+ </template>
102
+ </UModal>
103
+ </template>
104
+
105
+ <script setup>
106
+ import { computed, reactive, ref } from "vue";
107
+ const modalUi = {
108
+ content: "sm:max-w-[420px]"
109
+ };
110
+ const props = defineProps({
111
+ modelValue: { type: Boolean, required: true }
112
+ });
113
+ const emit = defineEmits(["update:modelValue", "registered"]);
114
+ const state = reactive({
115
+ email: "",
116
+ displayName: "",
117
+ password: "",
118
+ confirmPassword: "",
119
+ inviteToken: ""
120
+ });
121
+ const pending = ref(false);
122
+ const errorMessage = ref("");
123
+ const isOpen = computed({
124
+ get: () => props.modelValue,
125
+ set: (value) => {
126
+ emit("update:modelValue", value);
127
+ }
128
+ });
129
+ function close() {
130
+ errorMessage.value = "";
131
+ emit("update:modelValue", false);
132
+ }
133
+ async function onSubmit() {
134
+ pending.value = true;
135
+ errorMessage.value = "";
136
+ try {
137
+ await $fetch("/api/basic-auth/register", {
138
+ method: "POST",
139
+ body: {
140
+ email: state.email,
141
+ displayName: state.displayName || void 0,
142
+ password: state.password,
143
+ confirmPassword: state.confirmPassword,
144
+ inviteToken: state.inviteToken || void 0
145
+ }
146
+ });
147
+ emit("registered");
148
+ close();
149
+ } catch (error) {
150
+ const statusCode = typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null;
151
+ if (statusCode === 400) {
152
+ errorMessage.value = "Please check your input and try again.";
153
+ } else if (statusCode === 403) {
154
+ errorMessage.value = "Registration is restricted for this instance.";
155
+ } else if (statusCode === 409) {
156
+ errorMessage.value = "Email is already in use.";
157
+ } else {
158
+ errorMessage.value = "Unable to register right now. Please try again.";
159
+ }
160
+ } finally {
161
+ pending.value = false;
162
+ }
163
+ }
164
+ </script>
@@ -0,0 +1,11 @@
1
+ type __VLS_Props = {
2
+ modelValue: boolean;
3
+ };
4
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
5
+ "update:modelValue": (value: boolean) => any;
6
+ registered: () => any;
7
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
8
+ "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
9
+ onRegistered?: (() => any) | undefined;
10
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
11
+ export default _default;
@@ -0,0 +1,13 @@
1
+ type __VLS_Props = {
2
+ modelValue: boolean;
3
+ };
4
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
5
+ "update:modelValue": (value: boolean) => any;
6
+ "signed-in": () => any;
7
+ "open-register": () => any;
8
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
+ "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
10
+ "onSigned-in"?: (() => any) | undefined;
11
+ "onOpen-register"?: (() => any) | undefined;
12
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
+ export default _default;
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <UModal
3
+ v-model:open="isOpen"
4
+ title="Sign In"
5
+ :close="{
6
+ class: 'theme-btn',
7
+ size: 'sm'
8
+ }"
9
+ description="Enter your credentials to continue"
10
+ :ui="modalUi"
11
+ >
12
+ <template #body>
13
+ <UForm :state="state" @submit.prevent="onSubmit">
14
+ <div class="flex flex-col gap-4">
15
+ <input
16
+ :value="state.email"
17
+ type="text"
18
+ autocomplete="username"
19
+ tabindex="-1"
20
+ aria-hidden="true"
21
+ class="sr-only pointer-events-none h-0 w-0 opacity-0"
22
+ readonly
23
+ />
24
+
25
+ <UFormField label="Email" name="email">
26
+ <UInput
27
+ v-model="state.email"
28
+ type="email"
29
+ placeholder="you@example.com"
30
+ autocomplete="username"
31
+ required
32
+ class="w-full"
33
+ />
34
+ </UFormField>
35
+
36
+ <UFormField label="Password" name="password">
37
+ <UInput
38
+ v-model="state.password"
39
+ type="password"
40
+ placeholder="••••••••"
41
+ autocomplete="current-password"
42
+ required
43
+ class="w-full"
44
+ />
45
+ </UFormField>
46
+
47
+ <div
48
+ v-if="errorMessage"
49
+ class="flex items-start gap-2 rounded-[var(--md-border-radius)] border border-[var(--md-error)]/30 bg-[var(--md-error)]/8 px-3 py-2.5 text-sm text-[var(--md-error)]"
50
+ >
51
+ <UIcon name="i-lucide-alert-circle" class="w-4 h-4 mt-0.5 shrink-0" />
52
+ {{ errorMessage }}
53
+ </div>
54
+
55
+ <div class="flex justify-end gap-2 pt-1">
56
+ <UButton
57
+ class="theme-btn create-act-si-btn"
58
+ color="neutral"
59
+ variant="ghost"
60
+ type="button"
61
+ :disabled="pending"
62
+ @click="emit('open-register')"
63
+ >
64
+ Create account
65
+ </UButton>
66
+ <UButton
67
+ color="neutral"
68
+ variant="outline"
69
+ type="button"
70
+ :disabled="pending"
71
+ @click="close"
72
+ >
73
+ Cancel
74
+ </UButton>
75
+ <UButton type="submit" :loading="pending">
76
+ Sign In
77
+ </UButton>
78
+ </div>
79
+ </div>
80
+ </UForm>
81
+ </template>
82
+ </UModal>
83
+ </template>
84
+
85
+ <script setup>
86
+ import { computed, reactive, ref } from "vue";
87
+ const modalUi = {
88
+ content: "sm:max-w-[380px]"
89
+ };
90
+ const props = defineProps({
91
+ modelValue: { type: Boolean, required: true }
92
+ });
93
+ const emit = defineEmits(["update:modelValue", "signed-in", "open-register"]);
94
+ const state = reactive({
95
+ email: "",
96
+ password: ""
97
+ });
98
+ const pending = ref(false);
99
+ const errorMessage = ref("");
100
+ const isOpen = computed({
101
+ get: () => props.modelValue,
102
+ set: (value) => {
103
+ emit("update:modelValue", value);
104
+ }
105
+ });
106
+ function close() {
107
+ errorMessage.value = "";
108
+ emit("update:modelValue", false);
109
+ }
110
+ async function onSubmit() {
111
+ pending.value = true;
112
+ errorMessage.value = "";
113
+ try {
114
+ await $fetch("/api/basic-auth/sign-in", {
115
+ method: "POST",
116
+ body: {
117
+ email: state.email,
118
+ password: state.password
119
+ }
120
+ });
121
+ emit("signed-in");
122
+ close();
123
+ } catch (error) {
124
+ const statusCode = typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null;
125
+ if (statusCode === 400) {
126
+ errorMessage.value = "Please enter a valid email and password.";
127
+ } else if (statusCode === 401) {
128
+ errorMessage.value = "Invalid credentials";
129
+ } else {
130
+ errorMessage.value = "Unable to sign in right now. Please try again.";
131
+ }
132
+ } finally {
133
+ pending.value = false;
134
+ }
135
+ }
136
+ </script>
@@ -0,0 +1,13 @@
1
+ type __VLS_Props = {
2
+ modelValue: boolean;
3
+ };
4
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
5
+ "update:modelValue": (value: boolean) => any;
6
+ "signed-in": () => any;
7
+ "open-register": () => any;
8
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
+ "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
10
+ "onSigned-in"?: (() => any) | undefined;
11
+ "onOpen-register"?: (() => any) | undefined;
12
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
+ export default _default;
@@ -0,0 +1,12 @@
1
+ type __VLS_Props = {
2
+ email?: string;
3
+ displayName?: string;
4
+ };
5
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
+ "signed-out": () => any;
7
+ "change-password": () => any;
8
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
+ "onSigned-out"?: (() => any) | undefined;
10
+ "onChange-password"?: (() => any) | undefined;
11
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ export default _default;