authrix
Version:
Lightweight, flexible authentication library for Node.js and TypeScript.
1 lines • 3.66 kB
JavaScript
import*as f from'bcryptjs';import {randomBytes}from'crypto';var P=14,b=8,w=128,l=new Map,M=10,y=60*1e3;function v(t){let r=[];return (!t||t.length<b)&&r.push(`Password must be at least ${b} characters long`),t.length>w&&r.push(`Password must not exceed ${w} characters`),/[a-z]/.test(t)||r.push("Password must contain at least one lowercase letter"),/[A-Z]/.test(t)||r.push("Password must contain at least one uppercase letter"),/\d/.test(t)||r.push("Password must contain at least one number"),/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(t)||r.push("Password must contain at least one special character"),/(.)\1{2,}/.test(t)&&r.push("Password must not contain more than 2 consecutive identical characters"),/(?:abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz|012|123|234|345|456|567|678|789)/i.test(t)&&r.push("Password must not contain sequential characters"),{isValid:r.length===0,errors:r}}function $(t){let r=Date.now(),n=(l.get(t)||[]).filter(i=>r-i<y);if(n.length>=M)return false;if(n.push(r),l.set(t,n),l.size>1e3)for(let[i,d]of Array.from(l.entries())){let c=d.filter(s=>r-s<y);c.length===0?l.delete(i):l.set(i,c);}return true}async function N(t,r={}){if(typeof t!="string")throw new Error("Password must be a string");if(r.identifier&&!$(r.identifier))throw new Error("Too many password operations. Please try again later.");if(!r.skipValidation){let n=v(t);if(!n.isValid)throw new Error(`Password validation failed: ${n.errors.join(", ")}`)}let o=Buffer.from(t,"utf8").toString("utf8");try{let n=await f.hash(o,P);return typeof t=="string"&&(t=""),n}catch(n){throw new Error(`Failed to hash password: ${n instanceof Error?n.message:"Unknown error"}`)}}async function T(t,r,o={}){if(typeof t!="string"||typeof r!="string")throw new Error("Password and hash must be strings");if(!t||!r)return await f.compare("dummy-password","$2b$14$dummy.hash.to.prevent.timing.attacks.in.production"),false;if(o.identifier&&!$(o.identifier))throw new Error("Too many password verification attempts. Please try again later.");if(!r.startsWith("$2b$")&&!r.startsWith("$2a$")&&!r.startsWith("$2y$"))return await f.compare("dummy-password","$2b$14$dummy.hash.to.prevent.timing.attacks.in.production"),false;try{let n=Buffer.from(t,"utf8").toString("utf8"),i=await f.compare(n,r);return typeof t=="string"&&(t=""),i}catch{return false}}function k(t=16,r={}){let{includeLowercase:o=true,includeUppercase:n=true,includeNumbers:i=true,includeSymbols:d=true,excludeSimilar:c=true}=r,s="",a=[];if(o){let e=c?"abcdefghijkmnopqrstuvwxyz":"abcdefghijklmnopqrstuvwxyz";s+=e,a.push(e[Math.floor(Math.random()*e.length)]);}if(n){let e=c?"ABCDEFGHJKLMNPQRSTUVWXYZ":"ABCDEFGHIJKLMNOPQRSTUVWXYZ";s+=e,a.push(e[Math.floor(Math.random()*e.length)]);}if(i){let e=c?"23456789":"0123456789";s+=e,a.push(e[Math.floor(Math.random()*e.length)]);}if(d){let e="!@#$%^&*()_+-=[]{}|;:,.<>?";s+=e,a.push(e[Math.floor(Math.random()*e.length)]);}if(!s)throw new Error("At least one character set must be enabled");if(t<8||t>128)throw new Error("Password length must be between 8 and 128 characters");if(a.length>t)throw new Error("Password length must be at least as long as the number of required character types");let p="",g=randomBytes(t*2),h=0;for(let e of a)p+=e;for(let e=a.length;e<t;e++){let m=g[h]%s.length;p+=s[m],h++;}let u=p.split("");for(let e=u.length-1;e>0;e--){let m=g[h]%(e+1);[u[e],u[m]]=[u[m],u[e]],h++;}return u.join("")}function A(t){try{if(!t||!t.startsWith("$2"))return !0;let r=t.split("$");if(r.length<4)return !0;let o=parseInt(r[2]);return isNaN(o)?!0:o<P}catch{return true}}export{v as a,N as b,T as c,k as d,A as e};