@bagelink/auth 1.12.3 → 1.12.8
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/api.d.ts +54 -1
- package/dist/index.cjs +154 -1181
- package/dist/index.d.ts +0 -9
- package/dist/index.mjs +155 -1182
- package/dist/routes.d.ts +0 -36
- package/dist/types.d.ts +68 -1
- package/dist/useAuth.d.ts +17 -1
- package/package.json +1 -1
- package/src/api.ts +118 -0
- package/src/index.ts +0 -13
- package/src/routes.ts +0 -96
- package/src/types.ts +81 -1
- package/src/useAuth.ts +103 -0
- package/dist/Callback-BHqVaZZm.cjs +0 -4
- package/dist/Callback-C-XghN_z.js +0 -4
- package/dist/ForgotPasswordPage-BV9tyhHl.cjs +0 -4
- package/dist/ForgotPasswordPage-DvttMGb0.js +0 -4
- package/dist/LoginPage-hv1wc54S.cjs +0 -4
- package/dist/LoginPage-klj1NV4J.js +0 -4
- package/dist/ResetPasswordPage-COPrJmW8.cjs +0 -4
- package/dist/ResetPasswordPage-nvQ4uupb.js +0 -4
- package/dist/SignupPage-m36w9PLJ.cjs +0 -4
- package/dist/SignupPage-oUFYApYW.js +0 -4
- package/dist/components/auth/ForgotPasswordForm.vue.d.ts +0 -23
- package/dist/components/auth/LoginForm.vue.d.ts +0 -58
- package/dist/components/auth/ResetPasswordForm.vue.d.ts +0 -28
- package/dist/components/auth/SignupForm.vue.d.ts +0 -34
- package/dist/components/index.d.ts +0 -4
- package/dist/pages/Callback.vue.d.ts +0 -2
- package/dist/pages/ForgotPasswordPage.vue.d.ts +0 -13
- package/dist/pages/LoginPage.vue.d.ts +0 -38
- package/dist/pages/ResetPasswordPage.vue.d.ts +0 -13
- package/dist/pages/SignupPage.vue.d.ts +0 -16
- package/src/components/auth/ForgotPasswordForm.vue +0 -97
- package/src/components/auth/LoginForm.vue +0 -258
- package/src/components/auth/ResetPasswordForm.vue +0 -156
- package/src/components/auth/SignupForm.vue +0 -231
- package/src/components/index.ts +0 -5
- package/src/pages/Callback.vue +0 -196
- package/src/pages/ForgotPasswordPage.vue +0 -42
- package/src/pages/LoginPage.vue +0 -68
- package/src/pages/ResetPasswordPage.vue +0 -47
- package/src/pages/SignupPage.vue +0 -46
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { useAuth } from '@bagelink/auth'
|
|
3
|
-
import { Btn, Icon, PasswordInput } from '@bagelink/vue'
|
|
4
|
-
import { computed, ref } from 'vue'
|
|
5
|
-
|
|
6
|
-
export interface ResetPasswordTexts {
|
|
7
|
-
title?: string
|
|
8
|
-
invalidLinkTitle?: string
|
|
9
|
-
invalidLinkMessage?: string
|
|
10
|
-
newPasswordLabel?: string
|
|
11
|
-
confirmPasswordLabel?: string
|
|
12
|
-
submitButton?: string
|
|
13
|
-
backToLogin?: string
|
|
14
|
-
passwordMismatchError?: string
|
|
15
|
-
invalidTokenError?: string
|
|
16
|
-
successTitle?: string
|
|
17
|
-
successMessage?: string
|
|
18
|
-
goToLogin?: string
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface Props {
|
|
22
|
-
token?: string
|
|
23
|
-
showSuccess?: boolean
|
|
24
|
-
useHebrewDefaults?: boolean
|
|
25
|
-
texts?: Partial<ResetPasswordTexts>
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
29
|
-
useHebrewDefaults: false
|
|
30
|
-
})
|
|
31
|
-
defineEmits<{
|
|
32
|
-
switchForm: [form: string]
|
|
33
|
-
}>()
|
|
34
|
-
|
|
35
|
-
const { resetPassword } = useAuth()
|
|
36
|
-
|
|
37
|
-
const error = ref<string>('')
|
|
38
|
-
const internalSuccess = ref<boolean>(false)
|
|
39
|
-
const newPassword = ref<string>('')
|
|
40
|
-
const confirmPassword = ref<string>('')
|
|
41
|
-
|
|
42
|
-
const isValidToken = computed(() => !!props.token)
|
|
43
|
-
const isSuccess = computed(() => props.showSuccess || internalSuccess.value)
|
|
44
|
-
|
|
45
|
-
// Text configuration with conditional defaults
|
|
46
|
-
const texts = computed(() => {
|
|
47
|
-
const hebrewDefaults = {
|
|
48
|
-
title: 'איפוס סיסמה',
|
|
49
|
-
invalidLinkTitle: 'קישור לא תקין',
|
|
50
|
-
invalidLinkMessage: 'קישור איפוס הסיסמה לא תקין או פג תוקף',
|
|
51
|
-
newPasswordLabel: 'סיסמה חדשה',
|
|
52
|
-
confirmPasswordLabel: 'אימות סיסמה',
|
|
53
|
-
submitButton: 'איפוס סיסמה',
|
|
54
|
-
backToLogin: 'חזרה להתחברות',
|
|
55
|
-
passwordMismatchError: 'הסיסמאות לא תואמות',
|
|
56
|
-
invalidTokenError: 'אסימון לא תקין',
|
|
57
|
-
successTitle: 'הסיסמה אופסה בהצלחה!',
|
|
58
|
-
successMessage: 'הסיסמה שלכם אופסה. כעת תוכלו להתחברות עם הסיסמה החדשה.',
|
|
59
|
-
goToLogin: 'מעבר להתחברות'
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const englishDefaults = {
|
|
63
|
-
title: 'Reset Password',
|
|
64
|
-
invalidLinkTitle: 'Invalid Link',
|
|
65
|
-
invalidLinkMessage: 'This reset link is invalid or has expired',
|
|
66
|
-
newPasswordLabel: 'New Password',
|
|
67
|
-
confirmPasswordLabel: 'Confirm Password',
|
|
68
|
-
submitButton: 'Reset Password',
|
|
69
|
-
backToLogin: 'Back to Login',
|
|
70
|
-
passwordMismatchError: 'Passwords do not match',
|
|
71
|
-
invalidTokenError: 'Invalid token',
|
|
72
|
-
successTitle: 'Password Reset Successfully!',
|
|
73
|
-
successMessage: 'Your password has been reset. You can now log in with your new password.',
|
|
74
|
-
goToLogin: 'Go to Login'
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const defaults = props.useHebrewDefaults ? hebrewDefaults : englishDefaults
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
title: props.texts?.title ?? defaults.title,
|
|
81
|
-
invalidLinkTitle: props.texts?.invalidLinkTitle ?? defaults.invalidLinkTitle,
|
|
82
|
-
invalidLinkMessage: props.texts?.invalidLinkMessage ?? defaults.invalidLinkMessage,
|
|
83
|
-
newPasswordLabel: props.texts?.newPasswordLabel ?? defaults.newPasswordLabel,
|
|
84
|
-
confirmPasswordLabel: props.texts?.confirmPasswordLabel ?? defaults.confirmPasswordLabel,
|
|
85
|
-
submitButton: props.texts?.submitButton ?? defaults.submitButton,
|
|
86
|
-
backToLogin: props.texts?.backToLogin ?? defaults.backToLogin,
|
|
87
|
-
passwordMismatchError: props.texts?.passwordMismatchError ?? defaults.passwordMismatchError,
|
|
88
|
-
invalidTokenError: props.texts?.invalidTokenError ?? defaults.invalidTokenError,
|
|
89
|
-
successTitle: props.texts?.successTitle ?? defaults.successTitle,
|
|
90
|
-
successMessage: props.texts?.successMessage ?? defaults.successMessage,
|
|
91
|
-
goToLogin: props.texts?.goToLogin ?? defaults.goToLogin
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
const textDirection = computed(() => ({ 'auth-rtl': props.useHebrewDefaults }))
|
|
96
|
-
|
|
97
|
-
async function handleResetPassword() {
|
|
98
|
-
if (newPassword.value !== confirmPassword.value) {
|
|
99
|
-
error.value = texts.value.passwordMismatchError
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
if (!props.token) {
|
|
103
|
-
error.value = texts.value.invalidTokenError
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
try {
|
|
107
|
-
await resetPassword(props.token, newPassword.value)
|
|
108
|
-
internalSuccess.value = true
|
|
109
|
-
error.value = ''
|
|
110
|
-
} catch (err: unknown) {
|
|
111
|
-
error.value = err instanceof Error ? err.message : 'Reset failed'
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
</script>
|
|
115
|
-
|
|
116
|
-
<template>
|
|
117
|
-
<div v-if="!isValidToken" class="txt-center" :class="textDirection">
|
|
118
|
-
<h1 class="txt20 bold mb-1">
|
|
119
|
-
{{ texts.invalidLinkTitle }}
|
|
120
|
-
</h1>
|
|
121
|
-
<p class="opacity-7 mb-2">
|
|
122
|
-
{{ texts.invalidLinkMessage }}
|
|
123
|
-
</p>
|
|
124
|
-
<Btn :value="texts.backToLogin" @click="$emit('switchForm', 'login')" />
|
|
125
|
-
</div>
|
|
126
|
-
<div v-else-if="isSuccess" class="txt-center" :class="textDirection">
|
|
127
|
-
<Icon name="check_circle" size="4" class="txt-green mb-1" />
|
|
128
|
-
<h1 class="txt20 bold mb-1">
|
|
129
|
-
{{ texts.successTitle }}
|
|
130
|
-
</h1>
|
|
131
|
-
<p class="opacity-7 mb-2">
|
|
132
|
-
{{ texts.successMessage }}
|
|
133
|
-
</p>
|
|
134
|
-
<Btn :value="texts.goToLogin" @click="$emit('switchForm', 'login')" />
|
|
135
|
-
</div>
|
|
136
|
-
<form v-else :class="textDirection" @submit.prevent="handleResetPassword">
|
|
137
|
-
<h1 class="txt20 bold txt-center mb-1">
|
|
138
|
-
{{ texts.title }}
|
|
139
|
-
</h1>
|
|
140
|
-
<PasswordInput
|
|
141
|
-
v-model="newPassword" class="mb-05" :label="texts.newPasswordLabel"
|
|
142
|
-
autocomplete="new-password"
|
|
143
|
-
/>
|
|
144
|
-
<PasswordInput v-model="confirmPassword" :label="texts.confirmPasswordLabel" autocomplete="new-password" />
|
|
145
|
-
<Btn type="submit" class="w-100 mt-2" :value="texts.submitButton" />
|
|
146
|
-
<Btn
|
|
147
|
-
thin flat class="txt-12 mt-075 underline block" :value="texts.backToLogin"
|
|
148
|
-
@click="$emit('switchForm', 'login')"
|
|
149
|
-
/>
|
|
150
|
-
<div class="h-20px pt-075">
|
|
151
|
-
<p v-if="error" class="txt-center color-red txt-12">
|
|
152
|
-
{{ error }}
|
|
153
|
-
</p>
|
|
154
|
-
</div>
|
|
155
|
-
</form>
|
|
156
|
-
</template>
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { useAuth } from '@bagelink/auth'
|
|
3
|
-
import { Btn, PasswordInput, TextInput } from '@bagelink/vue'
|
|
4
|
-
import { computed, ref } from 'vue'
|
|
5
|
-
|
|
6
|
-
export interface SignupTexts {
|
|
7
|
-
title?: string
|
|
8
|
-
firstNameLabel?: string
|
|
9
|
-
lastNameLabel?: string
|
|
10
|
-
emailLabel?: string
|
|
11
|
-
passwordLabel?: string
|
|
12
|
-
confirmPasswordLabel?: string
|
|
13
|
-
signupButton?: string
|
|
14
|
-
alreadyHaveAccount?: string
|
|
15
|
-
// Validation errors
|
|
16
|
-
emailRequiredError?: string
|
|
17
|
-
emailInvalidError?: string
|
|
18
|
-
passwordRequiredError?: string
|
|
19
|
-
passwordTooShortError?: string
|
|
20
|
-
passwordMismatchError?: string
|
|
21
|
-
firstNameRequiredError?: string
|
|
22
|
-
lastNameRequiredError?: string
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface Props {
|
|
26
|
-
showNames?: boolean
|
|
27
|
-
useHebrewDefaults?: boolean
|
|
28
|
-
errorState?: 'normal' | 'email-required' | 'email-invalid' | 'password-short' | 'password-mismatch' | 'name-required' | 'server-error'
|
|
29
|
-
texts?: Partial<SignupTexts>
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
33
|
-
showNames: true,
|
|
34
|
-
useHebrewDefaults: false,
|
|
35
|
-
errorState: 'normal',
|
|
36
|
-
texts: () => ({})
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
defineEmits<{
|
|
40
|
-
switchForm: [form: string]
|
|
41
|
-
}>()
|
|
42
|
-
|
|
43
|
-
const { signup, login } = useAuth()
|
|
44
|
-
|
|
45
|
-
// Internal error for actual validation
|
|
46
|
-
const internalError = ref<string>('')
|
|
47
|
-
|
|
48
|
-
// Use computed for error that reacts to errorState changes
|
|
49
|
-
const error = computed(() => {
|
|
50
|
-
if (props.errorState !== 'normal') {
|
|
51
|
-
return getDevError()
|
|
52
|
-
}
|
|
53
|
-
return internalError.value
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
const form = ref({
|
|
57
|
-
email: '',
|
|
58
|
-
password: '',
|
|
59
|
-
confirmPassword: '',
|
|
60
|
-
first_name: '',
|
|
61
|
-
last_name: '',
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
// Text configuration with conditional defaults
|
|
65
|
-
const texts = computed(() => {
|
|
66
|
-
const hebrewDefaults = {
|
|
67
|
-
title: 'יצירת חשבון',
|
|
68
|
-
firstNameLabel: 'שם פרטי',
|
|
69
|
-
lastNameLabel: 'שם משפחה',
|
|
70
|
-
emailLabel: 'איימיל',
|
|
71
|
-
passwordLabel: 'סיסמה',
|
|
72
|
-
confirmPasswordLabel: 'אימות סיסמה',
|
|
73
|
-
signupButton: 'יצירת חשבון',
|
|
74
|
-
alreadyHaveAccount: 'יש לך כבר חשבון?',
|
|
75
|
-
// Validation errors
|
|
76
|
-
emailRequiredError: 'איימיל נדרש',
|
|
77
|
-
emailInvalidError: 'יש להזין כתובת איימיל תקינה',
|
|
78
|
-
passwordRequiredError: 'סיסמה נדרשת',
|
|
79
|
-
passwordTooShortError: 'הסיסמה חייבת להיות לפחות 8 תווים',
|
|
80
|
-
passwordMismatchError: 'הסיסמאות לא תואמות',
|
|
81
|
-
firstNameRequiredError: 'שם פרטי נדרש',
|
|
82
|
-
lastNameRequiredError: 'שם משפחה נדרש'
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const englishDefaults = {
|
|
86
|
-
title: 'Create Account',
|
|
87
|
-
firstNameLabel: 'First Name',
|
|
88
|
-
lastNameLabel: 'Last Name',
|
|
89
|
-
emailLabel: 'Email',
|
|
90
|
-
passwordLabel: 'Password',
|
|
91
|
-
confirmPasswordLabel: 'Confirm Password',
|
|
92
|
-
signupButton: 'Create Account',
|
|
93
|
-
alreadyHaveAccount: 'Already have an account?',
|
|
94
|
-
// Validation errors
|
|
95
|
-
emailRequiredError: 'Email is required',
|
|
96
|
-
emailInvalidError: 'Please enter a valid email address',
|
|
97
|
-
passwordRequiredError: 'Password is required',
|
|
98
|
-
passwordTooShortError: 'Password must be at least 8 characters',
|
|
99
|
-
passwordMismatchError: 'Passwords do not match',
|
|
100
|
-
firstNameRequiredError: 'First name is required',
|
|
101
|
-
lastNameRequiredError: 'Last name is required'
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const defaults = props.useHebrewDefaults ? hebrewDefaults : englishDefaults
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
title: props.texts.title || defaults.title,
|
|
108
|
-
firstNameLabel: props.texts.firstNameLabel || defaults.firstNameLabel,
|
|
109
|
-
lastNameLabel: props.texts.lastNameLabel || defaults.lastNameLabel,
|
|
110
|
-
emailLabel: props.texts.emailLabel || defaults.emailLabel,
|
|
111
|
-
passwordLabel: props.texts.passwordLabel || defaults.passwordLabel,
|
|
112
|
-
confirmPasswordLabel: props.texts.confirmPasswordLabel || defaults.confirmPasswordLabel,
|
|
113
|
-
signupButton: props.texts.signupButton || defaults.signupButton,
|
|
114
|
-
alreadyHaveAccount: props.texts.alreadyHaveAccount || defaults.alreadyHaveAccount,
|
|
115
|
-
// Validation errors
|
|
116
|
-
emailRequiredError: props.texts.emailRequiredError || defaults.emailRequiredError,
|
|
117
|
-
emailInvalidError: props.texts.emailInvalidError || defaults.emailInvalidError,
|
|
118
|
-
passwordRequiredError: props.texts.passwordRequiredError || defaults.passwordRequiredError,
|
|
119
|
-
passwordTooShortError: props.texts.passwordTooShortError || defaults.passwordTooShortError,
|
|
120
|
-
passwordMismatchError: props.texts.passwordMismatchError || defaults.passwordMismatchError,
|
|
121
|
-
firstNameRequiredError: props.texts.firstNameRequiredError || defaults.firstNameRequiredError,
|
|
122
|
-
lastNameRequiredError: props.texts.lastNameRequiredError || defaults.lastNameRequiredError
|
|
123
|
-
}
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
const textDirection = computed(() => ({ 'auth-rtl': props.useHebrewDefaults }))
|
|
127
|
-
|
|
128
|
-
function getDevError() {
|
|
129
|
-
switch (props.errorState) {
|
|
130
|
-
case 'email-required': return texts.value.emailRequiredError
|
|
131
|
-
case 'email-invalid': return texts.value.emailInvalidError
|
|
132
|
-
case 'password-short': return texts.value.passwordTooShortError
|
|
133
|
-
case 'password-mismatch': return texts.value.passwordMismatchError
|
|
134
|
-
case 'name-required': return props.showNames ? texts.value.firstNameRequiredError : null
|
|
135
|
-
case 'server-error': return props.useHebrewDefaults ? 'כתובת האימייל כבר קיימת או שגיאת שרת' : 'Email already exists or server error'
|
|
136
|
-
default: return null
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function validateForm() {
|
|
141
|
-
// Email validation
|
|
142
|
-
if (!form.value.email.trim()) {
|
|
143
|
-
return texts.value.emailRequiredError
|
|
144
|
-
}
|
|
145
|
-
const emailRegex = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/
|
|
146
|
-
if (!emailRegex.test(form.value.email)) {
|
|
147
|
-
return texts.value.emailInvalidError
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Password validation
|
|
151
|
-
if (!form.value.password) {
|
|
152
|
-
return texts.value.passwordRequiredError
|
|
153
|
-
}
|
|
154
|
-
if (form.value.password.length < 8) {
|
|
155
|
-
return texts.value.passwordTooShortError
|
|
156
|
-
}
|
|
157
|
-
if (form.value.password !== form.value.confirmPassword) {
|
|
158
|
-
return texts.value.passwordMismatchError
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Name validation (only if names are shown)
|
|
162
|
-
if (props.showNames) {
|
|
163
|
-
if (!form.value.first_name.trim()) {
|
|
164
|
-
return texts.value.firstNameRequiredError
|
|
165
|
-
}
|
|
166
|
-
if (!form.value.last_name.trim()) {
|
|
167
|
-
return texts.value.lastNameRequiredError
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return null
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function handleSignup() {
|
|
175
|
-
// Don't process if we're showing a development error
|
|
176
|
-
if (props.errorState !== 'normal') {
|
|
177
|
-
return
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Clear previous errors
|
|
181
|
-
internalError.value = ''
|
|
182
|
-
|
|
183
|
-
// Validate form first
|
|
184
|
-
const validationError = validateForm()
|
|
185
|
-
if (validationError) {
|
|
186
|
-
internalError.value = validationError
|
|
187
|
-
return
|
|
188
|
-
}
|
|
189
|
-
const { message, success } = await signup(form.value).catch((error) => {
|
|
190
|
-
console.error(error)
|
|
191
|
-
// Handle specific server errors
|
|
192
|
-
const errorMessage = error.response?.data?.detail as string
|
|
193
|
-
if (errorMessage?.toLowerCase().includes('email')) {
|
|
194
|
-
return { success: false, message: 'Email already exists or invalid' }
|
|
195
|
-
}
|
|
196
|
-
if (errorMessage?.toLowerCase().includes('password')) {
|
|
197
|
-
return { success: false, message: 'Password does not meet requirements' }
|
|
198
|
-
}
|
|
199
|
-
return { success: false, message: errorMessage || 'Registration failed. Please try again.' }
|
|
200
|
-
})
|
|
201
|
-
if (!success) {
|
|
202
|
-
internalError.value = message as string
|
|
203
|
-
return
|
|
204
|
-
}
|
|
205
|
-
await login({ email: form.value.email, password: form.value.password })
|
|
206
|
-
}
|
|
207
|
-
</script>
|
|
208
|
-
|
|
209
|
-
<template>
|
|
210
|
-
<form :class="textDirection" @submit.prevent="handleSignup">
|
|
211
|
-
<h1 class="txt20 bold txt-center mb-1">
|
|
212
|
-
{{ texts.title }}
|
|
213
|
-
</h1>
|
|
214
|
-
|
|
215
|
-
<TextInput v-if="props.showNames" v-model="form.first_name" :label="texts.firstNameLabel" />
|
|
216
|
-
<TextInput v-if="props.showNames" v-model="form.last_name" :label="texts.lastNameLabel" />
|
|
217
|
-
<TextInput v-model="form.email" type="email" :label="texts.emailLabel" autocomplete="username" />
|
|
218
|
-
<PasswordInput v-model="form.password" class="mb-05" :label="texts.passwordLabel" />
|
|
219
|
-
<PasswordInput v-model="form.confirmPassword" :label="texts.confirmPasswordLabel" />
|
|
220
|
-
<Btn class="w-100 mt-2" :value="texts.signupButton" type="submit" />
|
|
221
|
-
<Btn
|
|
222
|
-
thin flat class="txt-12 mt-075 underline block" :value="texts.alreadyHaveAccount"
|
|
223
|
-
@click="$emit('switchForm', 'login')"
|
|
224
|
-
/>
|
|
225
|
-
<div class="h-20px pt-075">
|
|
226
|
-
<p v-if="error" class="txt-center color-red txt-12">
|
|
227
|
-
{{ error }}
|
|
228
|
-
</p>
|
|
229
|
-
</div>
|
|
230
|
-
</form>
|
|
231
|
-
</template>
|
package/src/components/index.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// Export form components
|
|
2
|
-
export { default as ForgotPasswordForm } from './auth/ForgotPasswordForm.vue'
|
|
3
|
-
export { default as LoginForm } from './auth/LoginForm.vue'
|
|
4
|
-
export { default as ResetPasswordForm } from './auth/ResetPasswordForm.vue'
|
|
5
|
-
export { default as SignupForm } from './auth/SignupForm.vue'
|
package/src/pages/Callback.vue
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { AuthenticationResponse, SSOProvider } from '@bagelink/auth'
|
|
3
|
-
import { useAuth } from '@bagelink/auth'
|
|
4
|
-
import { Btn, Card, Icon, Loading } from '@bagelink/vue'
|
|
5
|
-
import { computed, onMounted, ref } from 'vue'
|
|
6
|
-
import { useRoute, useRouter } from 'vue-router'
|
|
7
|
-
|
|
8
|
-
import { providers } from '../constants'
|
|
9
|
-
|
|
10
|
-
const isLoading = ref(true)
|
|
11
|
-
const error = ref<string | null>(null)
|
|
12
|
-
const success = ref(false)
|
|
13
|
-
const isLinking = ref(false)
|
|
14
|
-
const provider = ref<SSOProvider | null>(null)
|
|
15
|
-
const authResponse = ref<AuthenticationResponse | null>(null)
|
|
16
|
-
|
|
17
|
-
const { sso, user, accountInfo } = useAuth()
|
|
18
|
-
const route = useRoute()
|
|
19
|
-
const router = useRouter()
|
|
20
|
-
const timeout = 4000
|
|
21
|
-
// Get provider info for better UI
|
|
22
|
-
const providerInfo = computed(() => {
|
|
23
|
-
if (provider.value === null) return null
|
|
24
|
-
return providers[provider.value]
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
async function linkCallback() {
|
|
28
|
-
isLinking.value = true
|
|
29
|
-
const { redirect } = route.query
|
|
30
|
-
try {
|
|
31
|
-
await sso.handleLinkCallback()
|
|
32
|
-
success.value = true
|
|
33
|
-
setTimeout(() => {
|
|
34
|
-
const redirectPath = typeof redirect === 'string' ? redirect : '/'
|
|
35
|
-
router.push(redirectPath)
|
|
36
|
-
}, timeout)
|
|
37
|
-
} catch (err: unknown) {
|
|
38
|
-
const errorMessage = err instanceof Error ? err.message : 'Failed to link account'
|
|
39
|
-
error.value = errorMessage
|
|
40
|
-
} finally {
|
|
41
|
-
isLoading.value = false
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function handleCallback() {
|
|
46
|
-
const { state, redirect } = route.query
|
|
47
|
-
provider.value = sessionStorage.getItem(`oauth_provider:${state}`) as SSOProvider
|
|
48
|
-
console.log('[Callback] Query params:', { state, redirect })
|
|
49
|
-
console.log('[Callback] Full route query:', route.query)
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
const response = await sso.handleCallback()
|
|
53
|
-
if (response === null) {
|
|
54
|
-
error.value = `Failed to authenticate with ${providerInfo.value?.name ?? 'provider'}`
|
|
55
|
-
} else {
|
|
56
|
-
authResponse.value = response
|
|
57
|
-
success.value = true
|
|
58
|
-
console.log('[Callback] Login successful, redirect param:', redirect)
|
|
59
|
-
// Auto-redirect will handle navigation, but fallback to manual redirect
|
|
60
|
-
// if auto-redirect is disabled or router not connected
|
|
61
|
-
setTimeout(() => {
|
|
62
|
-
const redirectPath = typeof redirect === 'string' ? redirect : '/'
|
|
63
|
-
console.log('[Callback] Manual fallback redirecting to:', redirectPath)
|
|
64
|
-
router.push(redirectPath)
|
|
65
|
-
}, timeout)
|
|
66
|
-
}
|
|
67
|
-
} catch (err: unknown) {
|
|
68
|
-
const errorMessage = err instanceof Error ? err.message : 'Authentication failed'
|
|
69
|
-
error.value = errorMessage
|
|
70
|
-
} finally {
|
|
71
|
-
isLoading.value = false
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
onMounted(async () => {
|
|
76
|
-
if (user.value) {
|
|
77
|
-
await linkCallback()
|
|
78
|
-
} else {
|
|
79
|
-
await handleCallback()
|
|
80
|
-
}
|
|
81
|
-
})
|
|
82
|
-
</script>
|
|
83
|
-
|
|
84
|
-
<template>
|
|
85
|
-
<div class="flex justify-content-center align-items-center vh-100 px-1">
|
|
86
|
-
<Card class="p-2 txt-center w450px shadow">
|
|
87
|
-
<!-- Loading State -->
|
|
88
|
-
<div v-if="isLoading" class="flex column align-items-center gap-1">
|
|
89
|
-
<Loading />
|
|
90
|
-
<div class="mt-1">
|
|
91
|
-
<h2 class="mb-05 pb-0 mt-0 txt24 m_txt20">
|
|
92
|
-
{{ isLinking ? 'Linking Account' : 'Authenticating' }}
|
|
93
|
-
</h2>
|
|
94
|
-
<p class="opacity-7 txt-14">
|
|
95
|
-
Please wait while we {{ isLinking ? 'link your' : 'complete the' }} {{ providerInfo?.name
|
|
96
|
-
|| 'OAuth' }} {{ isLinking ? 'account' : 'authentication' }}.
|
|
97
|
-
</p>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
|
|
101
|
-
<!-- Success State -->
|
|
102
|
-
<div v-else-if="success && !error" class="flex column align-items-center gap-1">
|
|
103
|
-
<div class="flex justify-content-center align-items-center mb-1">
|
|
104
|
-
<div class="relative">
|
|
105
|
-
<Icon name="check_circle" size="5" class="txt-green line-height-1" />
|
|
106
|
-
<div
|
|
107
|
-
v-if="providerInfo"
|
|
108
|
-
class="absolute flex justify-content-center align-items-center bg-white rounded"
|
|
109
|
-
style="bottom: -8px; right: -8px; width: 40px; height: 40px; border: 3px solid white;"
|
|
110
|
-
>
|
|
111
|
-
<Icon :name="providerInfo.icon" size="1.5" :style="{ color: providerInfo.color }" />
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
|
|
116
|
-
<div>
|
|
117
|
-
<h2 class="mb-05 pb-0 mt-0 txt24 m_txt20">
|
|
118
|
-
{{ isLinking ? 'Account Linked!' : 'Welcome Back!' }}
|
|
119
|
-
</h2>
|
|
120
|
-
<p class="opacity-7 txt-14 mb-1">
|
|
121
|
-
{{ isLinking
|
|
122
|
-
? `Successfully linked your ${providerInfo?.name || 'account'}.`
|
|
123
|
-
: `You've successfully signed in with ${providerInfo?.name || 'your account'}.`
|
|
124
|
-
}}
|
|
125
|
-
</p>
|
|
126
|
-
|
|
127
|
-
<!-- Show user info if available -->
|
|
128
|
-
<div v-if="user || accountInfo" class="bg-gray-light rounded p-1 mt-1">
|
|
129
|
-
<div class="flex column gap-05">
|
|
130
|
-
<div v-if="user?.name" class="flex align-items-center gap-05 justify-content-center">
|
|
131
|
-
<Icon name="person" size="1.2" class="opacity-7" />
|
|
132
|
-
<span class="txt-14 bold">{{ user.name }}</span>
|
|
133
|
-
</div>
|
|
134
|
-
<div v-if="user?.email" class="flex align-items-center gap-05 justify-content-center">
|
|
135
|
-
<Icon name="email" size="1.2" class="opacity-7" />
|
|
136
|
-
<span class="txt-14 opacity-7">{{ user.email }}</span>
|
|
137
|
-
</div>
|
|
138
|
-
<div
|
|
139
|
-
v-if="accountInfo?.authentication_methods"
|
|
140
|
-
class="flex gap-05 justify-content-center mt-05"
|
|
141
|
-
>
|
|
142
|
-
<Icon
|
|
143
|
-
v-for="method in accountInfo.authentication_methods" :key="method.id"
|
|
144
|
-
:name="method.type === 'sso' ? (method.provider || 'link') : 'password'" size="1.2"
|
|
145
|
-
class="opacity-5"
|
|
146
|
-
/>
|
|
147
|
-
</div>
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
|
|
151
|
-
<p class="txt-12 opacity-5 mt-1">
|
|
152
|
-
Redirecting you to home...
|
|
153
|
-
</p>
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
|
|
157
|
-
<!-- Error State -->
|
|
158
|
-
<div v-else-if="error" class="flex column align-items-center gap-1">
|
|
159
|
-
<Icon name="error" size="5" class="txt-red mb-1 line-height-1" />
|
|
160
|
-
<div>
|
|
161
|
-
<h2 class="mb-05 pb-0 mt-0 txt24 m_txt20">
|
|
162
|
-
Authentication Failed
|
|
163
|
-
</h2>
|
|
164
|
-
<p class="opacity-7 txt-14 mb-1">
|
|
165
|
-
{{ error }}
|
|
166
|
-
</p>
|
|
167
|
-
|
|
168
|
-
<div class="bg-red-light rounded p-1 mt-1 mb-1">
|
|
169
|
-
<p class="txt-12 opacity-7">
|
|
170
|
-
This could happen if:
|
|
171
|
-
</p>
|
|
172
|
-
<ul class="txt-12 opacity-7 txt-start mt-05" style="list-style: none; padding-left: 0;">
|
|
173
|
-
<li class="mb-025">
|
|
174
|
-
• The authorization was cancelled
|
|
175
|
-
</li>
|
|
176
|
-
<li class="mb-025">
|
|
177
|
-
• The state parameter was invalid
|
|
178
|
-
</li>
|
|
179
|
-
<li class="mb-025">
|
|
180
|
-
• The OAuth provider denied access
|
|
181
|
-
</li>
|
|
182
|
-
<li class="mb-025">
|
|
183
|
-
• Network connectivity issues
|
|
184
|
-
</li>
|
|
185
|
-
</ul>
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
<div class="flex gap-05 justify-content-center">
|
|
189
|
-
<Btn outline value="Try Again" to="/login" />
|
|
190
|
-
<Btn value="Go Home" to="/" />
|
|
191
|
-
</div>
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
194
|
-
</Card>
|
|
195
|
-
</div>
|
|
196
|
-
</template>
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { ForgotPasswordTexts } from '../components/auth/ForgotPasswordForm.vue'
|
|
3
|
-
import { Card } from '@bagelink/vue'
|
|
4
|
-
import { useRouter } from 'vue-router'
|
|
5
|
-
import ForgotPasswordForm from '../components/auth/ForgotPasswordForm.vue'
|
|
6
|
-
|
|
7
|
-
interface Props {
|
|
8
|
-
/** Custom texts for the forgot password form */
|
|
9
|
-
texts?: Partial<ForgotPasswordTexts>
|
|
10
|
-
/** Card styling */
|
|
11
|
-
cardWidth?: string
|
|
12
|
-
cardShadow?: boolean
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
16
|
-
cardWidth: '450px',
|
|
17
|
-
cardShadow: true
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const router = useRouter()
|
|
21
|
-
|
|
22
|
-
function switchForm(form: string) {
|
|
23
|
-
if (form === 'login') {
|
|
24
|
-
router.push('/login')
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
</script>
|
|
28
|
-
|
|
29
|
-
<template>
|
|
30
|
-
<div class="flex justify-content-center align-items-center min-vh-100 px-1 py-2">
|
|
31
|
-
<Card
|
|
32
|
-
:class="{ shadow: cardShadow }"
|
|
33
|
-
:style="{ width: cardWidth, maxWidth: '100%' }"
|
|
34
|
-
class="p-2"
|
|
35
|
-
>
|
|
36
|
-
<ForgotPasswordForm
|
|
37
|
-
:texts="texts"
|
|
38
|
-
@switch-form="switchForm"
|
|
39
|
-
/>
|
|
40
|
-
</Card>
|
|
41
|
-
</div>
|
|
42
|
-
</template>
|