From 9e3a631b50657e020b380d2cb665ceabb5a46a30 Mon Sep 17 00:00:00 2001 From: kabin thakuri Date: Wed, 25 Mar 2026 10:44:00 +0545 Subject: [PATCH 1/6] feat(poc): add support to better auth endpoints --- packages/vue-user/src/auth-provider.ts | 142 +++++++++++++++++- .../vue-user/src/better-auth/change-email.ts | 27 ++++ .../src/better-auth/change-password.ts | 31 ++++ packages/vue-user/src/better-auth/helper.ts | 60 ++++++++ packages/vue-user/src/better-auth/index.ts | 26 ++++ packages/vue-user/src/better-auth/login.ts | 38 +++++ packages/vue-user/src/better-auth/logout.ts | 17 +++ .../src/better-auth/request-password-reset.ts | 29 ++++ .../better-auth/resend-email-verification.ts | 20 +++ .../src/better-auth/reset-password.ts | 32 ++++ packages/vue-user/src/better-auth/signup.ts | 43 ++++++ .../vue-user/src/better-auth/verify-email.ts | 48 ++++++ packages/vue-user/src/constant.ts | 29 ++++ packages/vue-user/src/index.ts | 3 +- packages/vue-user/src/types/config.ts | 3 + 15 files changed, 546 insertions(+), 2 deletions(-) create mode 100644 packages/vue-user/src/better-auth/change-email.ts create mode 100644 packages/vue-user/src/better-auth/change-password.ts create mode 100644 packages/vue-user/src/better-auth/helper.ts create mode 100644 packages/vue-user/src/better-auth/index.ts create mode 100644 packages/vue-user/src/better-auth/login.ts create mode 100644 packages/vue-user/src/better-auth/logout.ts create mode 100644 packages/vue-user/src/better-auth/request-password-reset.ts create mode 100644 packages/vue-user/src/better-auth/resend-email-verification.ts create mode 100644 packages/vue-user/src/better-auth/reset-password.ts create mode 100644 packages/vue-user/src/better-auth/signup.ts create mode 100644 packages/vue-user/src/better-auth/verify-email.ts diff --git a/packages/vue-user/src/auth-provider.ts b/packages/vue-user/src/auth-provider.ts index 0dc7e377e..f1b0a2f50 100644 --- a/packages/vue-user/src/auth-provider.ts +++ b/packages/vue-user/src/auth-provider.ts @@ -1,4 +1,32 @@ import { + changeEmail as betterAuthChangeEmail, + changePassword as betterAuthChangePassword, + getVerificationStatus as betterAuthGetVerificationStatus, + isLoggedIn as betterAuthIsLoggedIn, + isProfileCompleted as betterAuthIsProfileCompleted, + login as betterAuthLogin, + logout as betterAuthLogout, + requestPasswordReset as betterAuthRequestPasswordReset, + resetPassword as betterAuthResetPassword, + sendVerificationEmail as betterAuthSendVerificationEmail, + signup as betterAuthSignup, + verifyEmail as betterAuthVerifyEmail, + verifySessionRoles as betterAuthVerifySessionRoles, +} from "./better-auth"; +import { + BETTER_AUTH_PATH_CHANGE_EMAIL, + BETTER_AUTH_PATH_CHANGE_PASSWORD, + BETTER_AUTH_PATH_GET_VERIFICATION_STATUS, + BETTER_AUTH_PATH_IS_LOGGED_IN, + BETTER_AUTH_PATH_IS_PROFILE_COMPLETED, + BETTER_AUTH_PATH_LOGIN, + BETTER_AUTH_PATH_LOGOUT, + BETTER_AUTH_PATH_PASSWORD_RESET, + BETTER_AUTH_PATH_PASSWORD_RESET_REQUEST, + BETTER_AUTH_PATH_SEND_VERIFICATION_EMAIL, + BETTER_AUTH_PATH_SIGNUP, + BETTER_AUTH_PATH_VERIFY_EMAIL, + BETTER_AUTH_PATH_VERIFY_SESSION_ROLES, API_PATH_CHANGE_EMAIL, API_PATH_CHANGE_PASSWORD, API_PATH_GET_VERIFICATION_STATUS, @@ -30,11 +58,12 @@ const initAuthProvider = (config?: AppConfig) => { const getAuthProvider = () => { if ( authConfig?.user?.features?.authProvider && - ["laravel-passport", "supertokens"].includes( + ["better-auth", "laravel-passport", "supertokens"].includes( authConfig.user.features.authProvider, ) ) { return authConfig.user.features.authProvider as + | "better-auth" | "laravel-passport" | "supertokens"; } @@ -43,6 +72,117 @@ const getAuthProvider = () => { }; const providers = { + "better-auth": { + doChangeEmail: (email: string) => { + const path = + authConfig?.user?.apiRoutes?.changeEmail || + BETTER_AUTH_PATH_CHANGE_EMAIL; + + return betterAuthChangeEmail(email, authConfig?.apiBaseUrl || "", path); + }, + doChangePassword: (payload: ChangePasswordPayload) => { + const path = + authConfig?.user?.apiRoutes?.changePassword || + BETTER_AUTH_PATH_CHANGE_PASSWORD; + + return betterAuthChangePassword( + payload, + authConfig?.apiBaseUrl || "", + path, + ); + }, + doGetVerificationStatus: () => { + const path = + authConfig?.user?.apiRoutes?.getVerificationStatus || + BETTER_AUTH_PATH_GET_VERIFICATION_STATUS; + + return betterAuthGetVerificationStatus( + authConfig?.apiBaseUrl || "", + path, + ); + }, + doLogin: (credentials: LoginCredentials) => { + const path = authConfig?.user?.apiRoutes?.login || BETTER_AUTH_PATH_LOGIN; + + return betterAuthLogin(credentials, authConfig?.apiBaseUrl || "", path); + }, + doLogout: () => { + const path = + authConfig?.user?.apiRoutes?.logout || BETTER_AUTH_PATH_LOGOUT; + + return betterAuthLogout(authConfig?.apiBaseUrl || "", path); + }, + doRequestPasswordReset: (credentials: PasswordResetRequestPayload) => { + const path = + authConfig?.user?.apiRoutes?.passwordResetRequest || + BETTER_AUTH_PATH_PASSWORD_RESET_REQUEST; + + return betterAuthRequestPasswordReset( + credentials, + authConfig?.apiBaseUrl || "", + path, + ); + }, + doResetPassword: (credentials: PasswordResetPayload) => { + const path = + authConfig?.user?.apiRoutes?.passwordResetRequest || + BETTER_AUTH_PATH_PASSWORD_RESET; + + return betterAuthResetPassword( + credentials, + authConfig?.apiBaseUrl || "", + path, + ); + }, + doSendVerificationEmail: () => { + const path = + authConfig?.user?.apiRoutes?.sendVerificationEmail || + BETTER_AUTH_PATH_SEND_VERIFICATION_EMAIL; + + return betterAuthSendVerificationEmail( + authConfig?.apiBaseUrl || "", + path, + ); + }, + doSignup: (credentials: LoginCredentials) => { + const path = + authConfig?.user?.apiRoutes?.signup || BETTER_AUTH_PATH_SIGNUP; + + return betterAuthSignup(credentials, authConfig?.apiBaseUrl || "", path); + }, + doVerifyEmail: (token: string) => { + const path = + authConfig?.user?.apiRoutes?.verifyEmail || + BETTER_AUTH_PATH_VERIFY_EMAIL; + + return betterAuthVerifyEmail(token, authConfig?.apiBaseUrl || "", path); + }, + isLoggedIn: () => { + const path = + authConfig?.user?.apiRoutes?.isLoggedIn || + BETTER_AUTH_PATH_IS_LOGGED_IN; + + return betterAuthIsLoggedIn(authConfig?.apiBaseUrl || "", path); + }, + isProfileCompleted: () => { + const path = + authConfig?.user?.apiRoutes?.isProfileCompleted || + BETTER_AUTH_PATH_IS_PROFILE_COMPLETED; + + return betterAuthIsProfileCompleted(authConfig?.apiBaseUrl || "", path); + }, + verifySessionRoles: (claims: string[]) => { + const path = + authConfig?.user?.apiRoutes?.verifySessionRoles || + BETTER_AUTH_PATH_VERIFY_SESSION_ROLES; + + return betterAuthVerifySessionRoles( + claims, + authConfig?.apiBaseUrl || "", + path, + ); + }, + }, "laravel-passport": { doChangeEmail: (email: string) => { const path = diff --git a/packages/vue-user/src/better-auth/change-email.ts b/packages/vue-user/src/better-auth/change-email.ts new file mode 100644 index 000000000..d31e478de --- /dev/null +++ b/packages/vue-user/src/better-auth/change-email.ts @@ -0,0 +1,27 @@ +import { AxiosError } from "axios"; + +import client from "../api/axios"; + +const changeEmail = async ( + email: string, + apiBaseUrl: string, + path: string, +): Promise => { + try { + await client(apiBaseUrl).post(path, { + newEmail: email, + }); + } catch (error) { + if (error instanceof AxiosError) { + if (error.response?.status === 409) { + throw new Error("EMAIL_EXISTS"); + } + + throw new Error("CHANGE_EMAIL_FAILED"); + } + + throw new Error("CHANGE_EMAIL_FAILED"); + } +}; + +export default changeEmail; diff --git a/packages/vue-user/src/better-auth/change-password.ts b/packages/vue-user/src/better-auth/change-password.ts new file mode 100644 index 000000000..53b142ca9 --- /dev/null +++ b/packages/vue-user/src/better-auth/change-password.ts @@ -0,0 +1,31 @@ +import { AxiosError } from "axios"; + +import client from "../api/axios"; + +import type { ChangePasswordPayload } from "../types"; + +const changePassword = async ( + payload: ChangePasswordPayload, + apiBaseUrl: string, + path: string, +): Promise => { + try { + await client(apiBaseUrl).post(path, { + currentPassword: payload.currentPassword, + newPassword: payload.newPassword, + revokeOtherSessions: true, + }); + } catch (error) { + if (error instanceof AxiosError) { + if (error.response?.status === 401) { + throw new Error("INVALID_CREDENTIALS"); + } + + throw new Error("CHANGE_PASSWORD_FAILED"); + } + + throw new Error("CHANGE_PASSWORD_FAILED"); + } +}; + +export default changePassword; diff --git a/packages/vue-user/src/better-auth/helper.ts b/packages/vue-user/src/better-auth/helper.ts new file mode 100644 index 000000000..e3715300f --- /dev/null +++ b/packages/vue-user/src/better-auth/helper.ts @@ -0,0 +1,60 @@ +import { AxiosError } from "axios"; + +import client from "../api/axios"; + +/** + * Verify session roles by calling the backend API + */ +export const verifySessionRoles = async ( + claims: string[], + apiBaseUrl: string, + path: string, +): Promise => { + try { + const response = await client(apiBaseUrl).get(path, { + params: { claims }, + }); + + return response.data.valid || false; + } catch (error) { + if (error instanceof AxiosError && error.response?.status === 401) { + return false; + } + return false; + } +}; + +/** + * Check if user is logged in + * For better-auth, we check if a valid session exists + */ +export const isLoggedIn = async ( + apiBaseUrl: string, + path: string, +): Promise => { + try { + const response = await client(apiBaseUrl).get(path); + + return response.data && response.data.sessionId; + } catch { + return false; + } +}; + +/** + * Check if user profile is completed + * For better-auth, we can consider profile as completed if user has email + */ +export const isProfileCompleted = async ( + apiBaseUrl: string, + path: string, +): Promise => { + try { + // Use the same session check endpoint; if session exists, profile is considered complete enough + const response = await client(apiBaseUrl).get(path); + + return !!(response.data && response.data.user?.email); + } catch { + return false; + } +}; diff --git a/packages/vue-user/src/better-auth/index.ts b/packages/vue-user/src/better-auth/index.ts new file mode 100644 index 000000000..c0b03f9f3 --- /dev/null +++ b/packages/vue-user/src/better-auth/index.ts @@ -0,0 +1,26 @@ +import changeEmail from "./change-email"; +import changePassword from "./change-password"; +import { isLoggedIn, isProfileCompleted, verifySessionRoles } from "./helper"; +import login from "./login"; +import logout from "./logout"; +import requestPasswordReset from "./request-password-reset"; +import sendVerificationEmail from "./resend-email-verification"; +import resetPassword from "./reset-password"; +import signup from "./signup"; +import { getVerificationStatus, verifyEmail } from "./verify-email"; + +export { + changeEmail, + changePassword, + getVerificationStatus, + isLoggedIn, + isProfileCompleted, + login, + logout, + requestPasswordReset, + resetPassword, + sendVerificationEmail, + signup, + verifyEmail, + verifySessionRoles, +}; diff --git a/packages/vue-user/src/better-auth/login.ts b/packages/vue-user/src/better-auth/login.ts new file mode 100644 index 000000000..154aa5a31 --- /dev/null +++ b/packages/vue-user/src/better-auth/login.ts @@ -0,0 +1,38 @@ +import { AxiosError } from "axios"; + +import client from "../api/axios"; + +import type { LoginCredentials, UserType } from "../types"; + +const login = async ( + credentials: LoginCredentials, + apiBaseUrl: string, + path: string, +): Promise => { + try { + const response = await client(apiBaseUrl).post(path, { + email: credentials.email, + password: credentials.password, + }); + + if (response.status === 200) { + const user = response.data.user as UserType; + + return user; + } + + return undefined; + } catch (error) { + if (error instanceof AxiosError) { + if (error.response?.status === 401) { + throw new Error("401"); + } + + throw new Error("SOMETHING_WRONG"); + } + + throw new Error("SOMETHING_WRONG"); + } +}; + +export default login; diff --git a/packages/vue-user/src/better-auth/logout.ts b/packages/vue-user/src/better-auth/logout.ts new file mode 100644 index 000000000..bdfcbd97e --- /dev/null +++ b/packages/vue-user/src/better-auth/logout.ts @@ -0,0 +1,17 @@ +import client from "../api/axios"; + +const logout = async (apiBaseUrl: string, path: string): Promise => { + try { + await client(apiBaseUrl).post( + path, + {}, + { + withCredentials: true, + }, + ); + } catch { + throw new Error("LOGOUT_FAILED"); + } +}; + +export default logout; diff --git a/packages/vue-user/src/better-auth/request-password-reset.ts b/packages/vue-user/src/better-auth/request-password-reset.ts new file mode 100644 index 000000000..8af14626e --- /dev/null +++ b/packages/vue-user/src/better-auth/request-password-reset.ts @@ -0,0 +1,29 @@ +import { AxiosError } from "axios"; + +import client from "../api/axios"; + +import type { PasswordResetRequestPayload } from "../types"; + +const requestPasswordReset = async ( + payload: PasswordResetRequestPayload, + apiBaseUrl: string, + path: string, +): Promise => { + try { + const response = await client(apiBaseUrl).post(path, { + email: payload.email, + redirectTo: payload.url || "/reset-password", + }); + + return response.status === 200; + } catch (error) { + if (error instanceof AxiosError) { + // Return false for any error, as this is typically a fire-and-forget operation + return false; + } + + return false; + } +}; + +export default requestPasswordReset; diff --git a/packages/vue-user/src/better-auth/resend-email-verification.ts b/packages/vue-user/src/better-auth/resend-email-verification.ts new file mode 100644 index 000000000..08966c78a --- /dev/null +++ b/packages/vue-user/src/better-auth/resend-email-verification.ts @@ -0,0 +1,20 @@ +import { AxiosError } from "axios"; + +import client from "../api/axios"; + +const sendVerificationEmail = async ( + apiBaseUrl: string, + path: string, +): Promise => { + try { + await client(apiBaseUrl).post(path, {}); + } catch (error) { + if (error instanceof AxiosError) { + throw new Error("SEND_VERIFICATION_FAILED"); + } + + throw new Error("SEND_VERIFICATION_FAILED"); + } +}; + +export default sendVerificationEmail; diff --git a/packages/vue-user/src/better-auth/reset-password.ts b/packages/vue-user/src/better-auth/reset-password.ts new file mode 100644 index 000000000..ab78fa9c9 --- /dev/null +++ b/packages/vue-user/src/better-auth/reset-password.ts @@ -0,0 +1,32 @@ +import { AxiosError } from "axios"; + +import client from "../api/axios"; + +import type { PasswordResetPayload } from "../types"; + +const resetPassword = async ( + payload: PasswordResetPayload, + apiBaseUrl: string, + path: string, +): Promise => { + try { + const response = await client(apiBaseUrl).post(path, { + token: payload.token, + newPassword: payload.password, + }); + + return response.status === 200; + } catch (error) { + if (error instanceof AxiosError) { + if (error.response?.status === 401) { + throw new Error("INVALID_TOKEN"); + } + + throw new Error("RESET_PASSWORD_FAILED"); + } + + throw new Error("RESET_PASSWORD_FAILED"); + } +}; + +export default resetPassword; diff --git a/packages/vue-user/src/better-auth/signup.ts b/packages/vue-user/src/better-auth/signup.ts new file mode 100644 index 000000000..c7bcb6394 --- /dev/null +++ b/packages/vue-user/src/better-auth/signup.ts @@ -0,0 +1,43 @@ +import { AxiosError } from "axios"; + +import client from "../api/axios"; + +import type { LoginCredentials, UserType } from "../types"; + +const signup = async ( + credentials: LoginCredentials, + apiBaseUrl: string, + path: string, +): Promise => { + try { + const response = await client(apiBaseUrl).post(path, { + email: credentials.email, + password: credentials.password, + name: credentials.email, + }); + + if (response.status === 200 || response.status === 201) { + const user = response.data.user as UserType; + + return user; + } + + throw new Error("SOMETHING_WRONG"); + } catch (error) { + if (error instanceof AxiosError) { + if (error.response?.status === 409) { + throw new Error("EMAIL_EXISTS"); + } + + if (error.response?.status === 401) { + throw new Error("INVALID_CREDENTIALS"); + } + + throw new Error("SOMETHING_WRONG"); + } + + throw new Error("SOMETHING_WRONG"); + } +}; + +export default signup; diff --git a/packages/vue-user/src/better-auth/verify-email.ts b/packages/vue-user/src/better-auth/verify-email.ts new file mode 100644 index 000000000..75f87e96c --- /dev/null +++ b/packages/vue-user/src/better-auth/verify-email.ts @@ -0,0 +1,48 @@ +import { AxiosError } from "axios"; + +import client from "../api/axios"; +import { EMAIL_VERIFICATION } from "../constant"; + +const verifyEmail = async ( + token: string, + apiBaseUrl: string, + path: string, +): Promise<{ + status: + | typeof EMAIL_VERIFICATION.OK + | typeof EMAIL_VERIFICATION.EMAIL_VERIFICATION_INVALID_TOKEN_ERROR; +}> => { + try { + await client(apiBaseUrl).get(path, { + params: { token }, + }); + + return { status: EMAIL_VERIFICATION.OK }; + } catch (error) { + if ( + error instanceof AxiosError && + (error.response?.status === 400 || error.response?.status === 404) + ) { + return { + status: EMAIL_VERIFICATION.EMAIL_VERIFICATION_INVALID_TOKEN_ERROR, + }; + } + + throw new Error("VERIFY_EMAIL_FAILED"); + } +}; + +const getVerificationStatus = async ( + apiBaseUrl: string, + path: string, +): Promise => { + try { + const response = await client(apiBaseUrl).get(path); + + return response.data && response.data.isVerified === true; + } catch { + return false; + } +}; + +export { verifyEmail, getVerificationStatus }; diff --git a/packages/vue-user/src/constant.ts b/packages/vue-user/src/constant.ts index 1576cfe4a..6480e6065 100644 --- a/packages/vue-user/src/constant.ts +++ b/packages/vue-user/src/constant.ts @@ -49,6 +49,22 @@ const STATUS_OK = "OK"; const SUPERTOKENS_API_BASE_PATH_DEFAULT = "/auth"; +const BETTER_AUTH_PATH_CHANGE_EMAIL = "/auth/email/change"; +const BETTER_AUTH_PATH_CHANGE_PASSWORD = "/auth/password/change"; +const BETTER_AUTH_PATH_GET_VERIFICATION_STATUS = + "/auth/email/verification-status"; +const BETTER_AUTH_PATH_LOGIN = "/auth/signin"; +const BETTER_AUTH_PATH_LOGOUT = "/auth/signout"; +const BETTER_AUTH_PATH_PASSWORD_RESET = "/auth/password-reset/confirm"; +const BETTER_AUTH_PATH_PASSWORD_RESET_REQUEST = "/auth/password-reset/request"; +const BETTER_AUTH_PATH_SEND_VERIFICATION_EMAIL = + "/auth/email/verification-request"; +const BETTER_AUTH_PATH_SIGNUP = "/auth/signup"; +const BETTER_AUTH_PATH_VERIFY_EMAIL = "/auth/email/verify"; +const BETTER_AUTH_PATH_IS_LOGGED_IN = "/auth/session"; +const BETTER_AUTH_PATH_IS_PROFILE_COMPLETED = "/auth/user"; +const BETTER_AUTH_PATH_VERIFY_SESSION_ROLES = "/auth/session/roles"; + export { API_PATH_CHANGE_EMAIL, API_PATH_CHANGE_PASSWORD, @@ -65,6 +81,19 @@ export { API_PATH_VERIFY_EMAIL, AUTH_CALLBACK_PATH_FACEBOOK, AUTH_CALLBACK_PATH_GOOGLE, + BETTER_AUTH_PATH_CHANGE_EMAIL, + BETTER_AUTH_PATH_CHANGE_PASSWORD, + BETTER_AUTH_PATH_GET_VERIFICATION_STATUS, + BETTER_AUTH_PATH_LOGIN, + BETTER_AUTH_PATH_LOGOUT, + BETTER_AUTH_PATH_PASSWORD_RESET, + BETTER_AUTH_PATH_PASSWORD_RESET_REQUEST, + BETTER_AUTH_PATH_SEND_VERIFICATION_EMAIL, + BETTER_AUTH_PATH_SIGNUP, + BETTER_AUTH_PATH_VERIFY_EMAIL, + BETTER_AUTH_PATH_IS_LOGGED_IN, + BETTER_AUTH_PATH_IS_PROFILE_COMPLETED, + BETTER_AUTH_PATH_VERIFY_SESSION_ROLES, EMAIL_VERIFICATION, ERROR_NOT_FOUND, ERROR_ROLE_ALREADY_EXISTS, diff --git a/packages/vue-user/src/index.ts b/packages/vue-user/src/index.ts index 300308d0e..6fecb7df9 100644 --- a/packages/vue-user/src/index.ts +++ b/packages/vue-user/src/index.ts @@ -27,7 +27,8 @@ const plugin: Plugin = { install: (app: App, options: DzangolabVueUserPluginOptions): void => { updateRouter(options.router, options.config?.user); - if (options?.config?.user?.features?.authProvider !== "laravel-passport") { + const authProvider = options?.config?.user?.features?.authProvider; + if (authProvider !== "laravel-passport" && authProvider !== "better-auth") { initSupertokens(options.config); } diff --git a/packages/vue-user/src/types/config.ts b/packages/vue-user/src/types/config.ts index 673ee932a..13b10dfa8 100644 --- a/packages/vue-user/src/types/config.ts +++ b/packages/vue-user/src/types/config.ts @@ -9,6 +9,8 @@ interface DzangolabVueUserConfig { changeEmail?: string; changePassword?: string; getVerificationStatus?: string; + isLoggedIn?: string; + isProfileCompleted?: string; login?: string; logout?: string; passwordReset?: string; @@ -17,6 +19,7 @@ interface DzangolabVueUserConfig { sendVerificationEmail?: string; signup?: string; verifyEmail?: string; + verifySessionRoles?: string; }; features?: { authProvider?: string; From 5112902955f04eff64e4e4b4e7304574e2b3fc1a Mon Sep 17 00:00:00 2001 From: kabin thakuri Date: Wed, 25 Mar 2026 11:30:37 +0545 Subject: [PATCH 2/6] feat(poc): add methods for otp signup --- packages/vue-user/src/better-auth/index.ts | 4 ++ packages/vue-user/src/better-auth/send-otp.ts | 21 ++++++++++ .../vue-user/src/better-auth/verify-otp.ts | 39 +++++++++++++++++++ packages/vue-user/src/constant.ts | 4 ++ packages/vue-user/src/types/config.ts | 2 + 5 files changed, 70 insertions(+) create mode 100644 packages/vue-user/src/better-auth/send-otp.ts create mode 100644 packages/vue-user/src/better-auth/verify-otp.ts diff --git a/packages/vue-user/src/better-auth/index.ts b/packages/vue-user/src/better-auth/index.ts index c0b03f9f3..4e6b9300e 100644 --- a/packages/vue-user/src/better-auth/index.ts +++ b/packages/vue-user/src/better-auth/index.ts @@ -6,8 +6,10 @@ import logout from "./logout"; import requestPasswordReset from "./request-password-reset"; import sendVerificationEmail from "./resend-email-verification"; import resetPassword from "./reset-password"; +import sendOtp from "./send-otp"; import signup from "./signup"; import { getVerificationStatus, verifyEmail } from "./verify-email"; +import verifyOtp from "./verify-otp"; export { changeEmail, @@ -19,8 +21,10 @@ export { logout, requestPasswordReset, resetPassword, + sendOtp, sendVerificationEmail, signup, verifyEmail, + verifyOtp, verifySessionRoles, }; diff --git a/packages/vue-user/src/better-auth/send-otp.ts b/packages/vue-user/src/better-auth/send-otp.ts new file mode 100644 index 000000000..d1919df1e --- /dev/null +++ b/packages/vue-user/src/better-auth/send-otp.ts @@ -0,0 +1,21 @@ +import client from "../api/axios"; + +import type { LoginCredentials } from "../types"; + +const sendOtp = async ( + credentials: LoginCredentials, + apiBaseUrl: string, + path: string, +): Promise => { + try { + const response = await client(apiBaseUrl).post(path, { + email: credentials.email, + }); + + return response.status === 200; + } catch { + return false; + } +}; + +export default sendOtp; diff --git a/packages/vue-user/src/better-auth/verify-otp.ts b/packages/vue-user/src/better-auth/verify-otp.ts new file mode 100644 index 000000000..eccaad2f2 --- /dev/null +++ b/packages/vue-user/src/better-auth/verify-otp.ts @@ -0,0 +1,39 @@ +import { AxiosError } from "axios"; + +import client from "../api/axios"; + +import type { UserType } from "../types"; + +const verifyOtp = async ( + email: string, + otp: string, + apiBaseUrl: string, + path: string, +): Promise => { + try { + const response = await client(apiBaseUrl).post(path, { + email, + otp, + }); + + if (response.status === 200) { + const user = response.data.user as UserType; + + return user; + } + + return undefined; + } catch (error) { + if (error instanceof AxiosError) { + if (error.response?.status === 401) { + throw new Error("401"); + } + + throw new Error("SOMETHING_WRONG"); + } + + throw new Error("SOMETHING_WRONG"); + } +}; + +export default verifyOtp; diff --git a/packages/vue-user/src/constant.ts b/packages/vue-user/src/constant.ts index 6480e6065..3206a666f 100644 --- a/packages/vue-user/src/constant.ts +++ b/packages/vue-user/src/constant.ts @@ -57,10 +57,12 @@ const BETTER_AUTH_PATH_LOGIN = "/auth/signin"; const BETTER_AUTH_PATH_LOGOUT = "/auth/signout"; const BETTER_AUTH_PATH_PASSWORD_RESET = "/auth/password-reset/confirm"; const BETTER_AUTH_PATH_PASSWORD_RESET_REQUEST = "/auth/password-reset/request"; +const BETTER_AUTH_PATH_SEND_OTP = "/auth/otp/send"; const BETTER_AUTH_PATH_SEND_VERIFICATION_EMAIL = "/auth/email/verification-request"; const BETTER_AUTH_PATH_SIGNUP = "/auth/signup"; const BETTER_AUTH_PATH_VERIFY_EMAIL = "/auth/email/verify"; +const BETTER_AUTH_PATH_VERIFY_OTP = "/auth/otp/verify"; const BETTER_AUTH_PATH_IS_LOGGED_IN = "/auth/session"; const BETTER_AUTH_PATH_IS_PROFILE_COMPLETED = "/auth/user"; const BETTER_AUTH_PATH_VERIFY_SESSION_ROLES = "/auth/session/roles"; @@ -88,9 +90,11 @@ export { BETTER_AUTH_PATH_LOGOUT, BETTER_AUTH_PATH_PASSWORD_RESET, BETTER_AUTH_PATH_PASSWORD_RESET_REQUEST, + BETTER_AUTH_PATH_SEND_OTP, BETTER_AUTH_PATH_SEND_VERIFICATION_EMAIL, BETTER_AUTH_PATH_SIGNUP, BETTER_AUTH_PATH_VERIFY_EMAIL, + BETTER_AUTH_PATH_VERIFY_OTP, BETTER_AUTH_PATH_IS_LOGGED_IN, BETTER_AUTH_PATH_IS_PROFILE_COMPLETED, BETTER_AUTH_PATH_VERIFY_SESSION_ROLES, diff --git a/packages/vue-user/src/types/config.ts b/packages/vue-user/src/types/config.ts index 13b10dfa8..b4507e5d0 100644 --- a/packages/vue-user/src/types/config.ts +++ b/packages/vue-user/src/types/config.ts @@ -16,9 +16,11 @@ interface DzangolabVueUserConfig { passwordReset?: string; passwordResetRequest?: string; refresh?: string; + sendOtp?: string; sendVerificationEmail?: string; signup?: string; verifyEmail?: string; + verifyOtp?: string; verifySessionRoles?: string; }; features?: { From 4fcd65491bd01a9795e9a03ab84d259c3c0be7f1 Mon Sep 17 00:00:00 2001 From: kabin thakuri Date: Wed, 25 Mar 2026 15:51:53 +0545 Subject: [PATCH 3/6] chore(user): update route for betterauth authentication --- packages/vue-user/src/constant.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vue-user/src/constant.ts b/packages/vue-user/src/constant.ts index 3206a666f..7e08bcc83 100644 --- a/packages/vue-user/src/constant.ts +++ b/packages/vue-user/src/constant.ts @@ -53,14 +53,14 @@ const BETTER_AUTH_PATH_CHANGE_EMAIL = "/auth/email/change"; const BETTER_AUTH_PATH_CHANGE_PASSWORD = "/auth/password/change"; const BETTER_AUTH_PATH_GET_VERIFICATION_STATUS = "/auth/email/verification-status"; -const BETTER_AUTH_PATH_LOGIN = "/auth/signin"; -const BETTER_AUTH_PATH_LOGOUT = "/auth/signout"; +const BETTER_AUTH_PATH_LOGIN = "/auth/sign-in/email"; +const BETTER_AUTH_PATH_LOGOUT = "/auth/sign-out"; const BETTER_AUTH_PATH_PASSWORD_RESET = "/auth/password-reset/confirm"; const BETTER_AUTH_PATH_PASSWORD_RESET_REQUEST = "/auth/password-reset/request"; const BETTER_AUTH_PATH_SEND_OTP = "/auth/otp/send"; const BETTER_AUTH_PATH_SEND_VERIFICATION_EMAIL = "/auth/email/verification-request"; -const BETTER_AUTH_PATH_SIGNUP = "/auth/signup"; +const BETTER_AUTH_PATH_SIGNUP = "/auth/sign-up/email"; const BETTER_AUTH_PATH_VERIFY_EMAIL = "/auth/email/verify"; const BETTER_AUTH_PATH_VERIFY_OTP = "/auth/otp/verify"; const BETTER_AUTH_PATH_IS_LOGGED_IN = "/auth/session"; From 4423f4b1d904127f5ca561acd448bd33d9acb463 Mon Sep 17 00:00:00 2001 From: kabin thakuri Date: Wed, 25 Mar 2026 15:59:14 +0545 Subject: [PATCH 4/6] chore(user): add prepare script for github version installation --- packages/vue-user/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vue-user/package.json b/packages/vue-user/package.json index a39cfd032..6b5642262 100644 --- a/packages/vue-user/package.json +++ b/packages/vue-user/package.json @@ -19,6 +19,7 @@ "build": "vite build && vue-tsc --emitDeclarationOnly", "lint": "eslint .", "lint:fix": "eslint . --fix", + "prepare": "vite build && vue-tsc --emitDeclarationOnly", "snapshot:update": "vitest --environment jsdom run --update", "sort-package": "npx sort-package-json", "test": "pnpm build && vitest --environment jsdom run --coverage --passWithNoTests", From cc738e4e9b5a4dbe03dd2a3f563c5830e1d85a54 Mon Sep 17 00:00:00 2001 From: kabin thakuri Date: Wed, 25 Mar 2026 16:37:19 +0545 Subject: [PATCH 5/6] chore: add prepare script to al packages for github version installation --- packages/vue-config/package.json | 1 + packages/vue-form/package.json | 1 + packages/vue-i18n/package.json | 1 + packages/vue-layout/package.json | 1 + packages/vue-tanstack-table/package.json | 1 + packages/vue-ui/package.json | 1 + 6 files changed, 6 insertions(+) diff --git a/packages/vue-config/package.json b/packages/vue-config/package.json index deb07fb54..1a07dd50b 100644 --- a/packages/vue-config/package.json +++ b/packages/vue-config/package.json @@ -21,6 +21,7 @@ "build": "vite build && vue-tsc --emitDeclarationOnly", "lint": "eslint .", "lint:fix": "eslint . --fix", + "prepare": "vite build && vue-tsc --emitDeclarationOnly", "sort-package": "npx sort-package-json", "test": "vitest --environment jsdom run --coverage --passWithNoTests", "test:component": "vitest --environment jsdom run component/ --passWithNoTests", diff --git a/packages/vue-form/package.json b/packages/vue-form/package.json index 885b90047..fcf057d06 100644 --- a/packages/vue-form/package.json +++ b/packages/vue-form/package.json @@ -21,6 +21,7 @@ "build": "vite build && vue-tsc --emitDeclarationOnly", "lint": "eslint .", "lint:fix": "eslint . --fix", + "prepare": "vite build && vue-tsc --emitDeclarationOnly", "snapshot:update": "vitest --environment jsdom run --update", "sort-package": "npx sort-package-json", "test": "vitest --environment jsdom run --coverage --passWithNoTests", diff --git a/packages/vue-i18n/package.json b/packages/vue-i18n/package.json index f64e54799..65d23fe1a 100644 --- a/packages/vue-i18n/package.json +++ b/packages/vue-i18n/package.json @@ -19,6 +19,7 @@ "build": "vite build && vue-tsc --emitDeclarationOnly", "lint": "eslint .", "lint:fix": "eslint . --fix", + "prepare": "vite build && vue-tsc --emitDeclarationOnly", "snapshot:update": "vitest --environment jsdom run --update", "sort-package": "npx sort-package-json", "test": "vitest --environment jsdom run --coverage --passWithNoTests", diff --git a/packages/vue-layout/package.json b/packages/vue-layout/package.json index 3bc06088c..ddc87fded 100644 --- a/packages/vue-layout/package.json +++ b/packages/vue-layout/package.json @@ -19,6 +19,7 @@ "build": "vite build && vue-tsc --emitDeclarationOnly", "lint": "eslint .", "lint:fix": "eslint . --fix", + "prepare": "vite build && vue-tsc --emitDeclarationOnly", "snapshot:update": "vitest --environment jsdom run --update", "sort-package": "npx sort-package-json", "test": "vitest --environment jsdom run --coverage --passWithNoTests", diff --git a/packages/vue-tanstack-table/package.json b/packages/vue-tanstack-table/package.json index 64e1b739f..c8d2bd5d6 100644 --- a/packages/vue-tanstack-table/package.json +++ b/packages/vue-tanstack-table/package.json @@ -19,6 +19,7 @@ "build": "vite build && vue-tsc --emitDeclarationOnly", "lint": "eslint .", "lint:fix": "eslint . --fix", + "prepare": "vite build && vue-tsc --emitDeclarationOnly", "snapshot:update": "vitest --environment jsdom run --update", "sort-package": "npx sort-package-json", "test": "vitest --environment jsdom run --coverage --passWithNoTests", diff --git a/packages/vue-ui/package.json b/packages/vue-ui/package.json index 3d77ba8d7..05cca5ca2 100644 --- a/packages/vue-ui/package.json +++ b/packages/vue-ui/package.json @@ -21,6 +21,7 @@ "build": "vite build && vue-tsc --emitDeclarationOnly", "lint": "eslint .", "lint:fix": "eslint . --fix", + "prepare": "vite build && vue-tsc --emitDeclarationOnly", "snapshot:update": "vitest --environment jsdom run --update", "sort-package": "npx sort-package-json", "test": "vitest --environment jsdom run --coverage --passWithNoTests", From 4e21388bc6ee1fc3ca62862c8d128fd5ff192f90 Mon Sep 17 00:00:00 2001 From: kabin thakuri Date: Thu, 26 Mar 2026 11:29:48 +0545 Subject: [PATCH 6/6] chore(user): update betterauth route --- packages/vue-user/src/auth-provider.ts | 11 +---- packages/vue-user/src/better-auth/helper.ts | 45 ++++++++++++--------- packages/vue-user/src/constant.ts | 27 ++++++------- 3 files changed, 40 insertions(+), 43 deletions(-) diff --git a/packages/vue-user/src/auth-provider.ts b/packages/vue-user/src/auth-provider.ts index f1b0a2f50..1149126b1 100644 --- a/packages/vue-user/src/auth-provider.ts +++ b/packages/vue-user/src/auth-provider.ts @@ -26,7 +26,6 @@ import { BETTER_AUTH_PATH_SEND_VERIFICATION_EMAIL, BETTER_AUTH_PATH_SIGNUP, BETTER_AUTH_PATH_VERIFY_EMAIL, - BETTER_AUTH_PATH_VERIFY_SESSION_ROLES, API_PATH_CHANGE_EMAIL, API_PATH_CHANGE_PASSWORD, API_PATH_GET_VERIFICATION_STATUS, @@ -172,15 +171,7 @@ const providers = { return betterAuthIsProfileCompleted(authConfig?.apiBaseUrl || "", path); }, verifySessionRoles: (claims: string[]) => { - const path = - authConfig?.user?.apiRoutes?.verifySessionRoles || - BETTER_AUTH_PATH_VERIFY_SESSION_ROLES; - - return betterAuthVerifySessionRoles( - claims, - authConfig?.apiBaseUrl || "", - path, - ); + return betterAuthVerifySessionRoles(claims); }, }, "laravel-passport": { diff --git a/packages/vue-user/src/better-auth/helper.ts b/packages/vue-user/src/better-auth/helper.ts index e3715300f..689c10a35 100644 --- a/packages/vue-user/src/better-auth/helper.ts +++ b/packages/vue-user/src/better-auth/helper.ts @@ -1,28 +1,35 @@ -import { AxiosError } from "axios"; - import client from "../api/axios"; +import useUserStore from "../store"; -/** - * Verify session roles by calling the backend API - */ -export const verifySessionRoles = async ( - claims: string[], - apiBaseUrl: string, - path: string, -): Promise => { - try { - const response = await client(apiBaseUrl).get(path, { - params: { claims }, +import type { UserType } from "../types/auth"; + +export async function verifySessionRoles( + supportedRoles: string[], +): Promise { + const userStore = useUserStore(); + const { logout } = userStore; + + const user: UserType | undefined = userStore.getUser(); + + if (user && user.roles) { + const hasSupportedRoles = await user.roles.some((userRole) => { + if (typeof userRole === "string") { + return supportedRoles.includes(userRole); + } + return supportedRoles.includes(userRole.role); }); - return response.data.valid || false; - } catch (error) { - if (error instanceof AxiosError && error.response?.status === 401) { - return false; + if (hasSupportedRoles) { + return true; + } else { + await logout(); } - return false; + } else { + await logout(); } -}; + + return false; +} /** * Check if user is logged in diff --git a/packages/vue-user/src/constant.ts b/packages/vue-user/src/constant.ts index 7e08bcc83..a251ec20a 100644 --- a/packages/vue-user/src/constant.ts +++ b/packages/vue-user/src/constant.ts @@ -49,23 +49,23 @@ const STATUS_OK = "OK"; const SUPERTOKENS_API_BASE_PATH_DEFAULT = "/auth"; -const BETTER_AUTH_PATH_CHANGE_EMAIL = "/auth/email/change"; -const BETTER_AUTH_PATH_CHANGE_PASSWORD = "/auth/password/change"; +const BETTER_AUTH_PATH_CHANGE_EMAIL = "/poc/auth/email/change"; +const BETTER_AUTH_PATH_CHANGE_PASSWORD = "/poc/auth/password/change"; const BETTER_AUTH_PATH_GET_VERIFICATION_STATUS = "/auth/email/verification-status"; -const BETTER_AUTH_PATH_LOGIN = "/auth/sign-in/email"; -const BETTER_AUTH_PATH_LOGOUT = "/auth/sign-out"; -const BETTER_AUTH_PATH_PASSWORD_RESET = "/auth/password-reset/confirm"; -const BETTER_AUTH_PATH_PASSWORD_RESET_REQUEST = "/auth/password-reset/request"; -const BETTER_AUTH_PATH_SEND_OTP = "/auth/otp/send"; +const BETTER_AUTH_PATH_LOGIN = "/poc/auth/signin"; +const BETTER_AUTH_PATH_LOGOUT = "/poc/auth/signout"; +const BETTER_AUTH_PATH_PASSWORD_RESET = "/poc/auth/password-reset/confirm"; +const BETTER_AUTH_PATH_PASSWORD_RESET_REQUEST = + "/poc/auth/password-reset/request"; +const BETTER_AUTH_PATH_SEND_OTP = "/poc/auth/otp/send"; const BETTER_AUTH_PATH_SEND_VERIFICATION_EMAIL = "/auth/email/verification-request"; -const BETTER_AUTH_PATH_SIGNUP = "/auth/sign-up/email"; -const BETTER_AUTH_PATH_VERIFY_EMAIL = "/auth/email/verify"; -const BETTER_AUTH_PATH_VERIFY_OTP = "/auth/otp/verify"; -const BETTER_AUTH_PATH_IS_LOGGED_IN = "/auth/session"; -const BETTER_AUTH_PATH_IS_PROFILE_COMPLETED = "/auth/user"; -const BETTER_AUTH_PATH_VERIFY_SESSION_ROLES = "/auth/session/roles"; +const BETTER_AUTH_PATH_SIGNUP = "/poc/auth/signup"; +const BETTER_AUTH_PATH_VERIFY_EMAIL = "/poc/auth/email/verify"; +const BETTER_AUTH_PATH_VERIFY_OTP = "/poc/auth/otp/verify"; +const BETTER_AUTH_PATH_IS_LOGGED_IN = "/poc/auth/session"; +const BETTER_AUTH_PATH_IS_PROFILE_COMPLETED = "/poc/auth/user"; export { API_PATH_CHANGE_EMAIL, @@ -97,7 +97,6 @@ export { BETTER_AUTH_PATH_VERIFY_OTP, BETTER_AUTH_PATH_IS_LOGGED_IN, BETTER_AUTH_PATH_IS_PROFILE_COMPLETED, - BETTER_AUTH_PATH_VERIFY_SESSION_ROLES, EMAIL_VERIFICATION, ERROR_NOT_FOUND, ERROR_ROLE_ALREADY_EXISTS,