nuxt-supabase-team-auth
Version:
Drop-in Nuxt 3 module for team-based authentication with Supabase
84 lines (83 loc) • 3.6 kB
JavaScript
import { DEFAULT_PASSWORD_POLICY } from "../types/password-policy.js";
export function validatePassword(password, policy = {}) {
const errors = [];
const mergedPolicy = { ...DEFAULT_PASSWORD_POLICY, ...policy };
if (password.length < mergedPolicy.minLength) {
errors.push(`Password must be at least ${mergedPolicy.minLength} characters`);
}
if (mergedPolicy.requireUppercase && !/[A-Z]/.test(password)) {
errors.push("Password must contain at least one uppercase letter");
}
if (mergedPolicy.requireLowercase && !/[a-z]/.test(password)) {
errors.push("Password must contain at least one lowercase letter");
}
if (mergedPolicy.requireNumbers && !/\d/.test(password)) {
errors.push("Password must contain at least one number");
}
if (mergedPolicy.requireSpecialChars) {
const escapedChars = mergedPolicy.specialChars.replace(/\\/g, "\\\\").replace(/\]/g, "\\]").replace(/\^/g, "\\^").replace(/-/g, "\\-");
const specialCharsRegex = new RegExp(`[${escapedChars}]`);
if (!specialCharsRegex.test(password)) {
errors.push("Password must contain at least one special character");
}
}
if (policy.customValidator) {
const customResult = policy.customValidator(password);
if (customResult !== true) {
errors.push(typeof customResult === "string" ? customResult : "Password does not meet custom requirements");
}
}
return {
isValid: errors.length === 0,
errors
};
}
export function generatePasswordHelpText(policy = {}) {
if (policy.helpText) {
return policy.helpText;
}
const mergedPolicy = { ...DEFAULT_PASSWORD_POLICY, ...policy };
const requirements = [];
requirements.push(`at least ${mergedPolicy.minLength} characters`);
const charRequirements = [];
if (mergedPolicy.requireUppercase) charRequirements.push("uppercase");
if (mergedPolicy.requireLowercase) charRequirements.push("lowercase");
if (mergedPolicy.requireNumbers) charRequirements.push("numbers");
if (mergedPolicy.requireSpecialChars) charRequirements.push("special characters");
if (charRequirements.length > 0) {
requirements.push(charRequirements.join(", "));
}
return `Must be ${requirements.join(" with ")}`;
}
export async function createPasswordSchema(policy = {}) {
const v = await import("valibot");
const mergedPolicy = { ...DEFAULT_PASSWORD_POLICY, ...policy };
const pipes = [
v.string(),
v.minLength(mergedPolicy.minLength, `Password must be at least ${mergedPolicy.minLength} characters`)
];
if (mergedPolicy.requireUppercase) {
pipes.push(v.regex(/[A-Z]/, "Password must contain at least one uppercase letter"));
}
if (mergedPolicy.requireLowercase) {
pipes.push(v.regex(/[a-z]/, "Password must contain at least one lowercase letter"));
}
if (mergedPolicy.requireNumbers) {
pipes.push(v.regex(/\d/, "Password must contain at least one number"));
}
if (mergedPolicy.requireSpecialChars) {
const escapedChars = mergedPolicy.specialChars.replace(/\\/g, "\\\\").replace(/\]/g, "\\]").replace(/\^/g, "\\^").replace(/-/g, "\\-");
const specialCharsRegex = new RegExp(`[${escapedChars}]`);
pipes.push(v.regex(specialCharsRegex, "Password must contain at least one special character"));
}
if (policy.customValidator) {
pipes.push(v.custom((value) => {
const result = policy.customValidator(value);
return result === true;
}, (value) => {
const result = policy.customValidator(value);
return typeof result === "string" ? result : "Password does not meet custom requirements";
}));
}
return v.pipe(...pipes);
}