authrix
Version:
Lightweight, flexible authentication library for Node.js and TypeScript.
33 lines • 11.9 kB
JavaScript
import {a as a$2}from'./chunk-AUHY5VM4.mjs';import {a as a$3}from'./chunk-HRFACFJI.mjs';import {a as a$1}from'./chunk-ML66D24Y.mjs';import {b,d}from'./chunk-2W6WDMNG.mjs';import {a}from'./chunk-ZNCUQ2DE.mjs';import {randomUUID}from'crypto';var w=new Map,A=new Map;async function D(e){try{let{createHash:t}=await import('crypto');return t("sha256").update(e+process.env.JWT_SECRET||"default_secret").digest("hex")}catch{return Buffer.from(e).toString("base64")}}async function B(e,t){return await D(e)===t}function z(e=6){let t="0123456789",r="";for(let o=0;o<e;o++)r+=t[Math.floor(Math.random()*t.length)];return r}function $(){let e=new Date;for(let[t,r]of Array.from(w.entries()))e>r.expiresAt&&w.delete(t);}async function k(e,t={}){let r=a.db;if(!r)throw new Error("Database not configured. Make sure initAuth() is called before using forgot password functions.");let{codeLength:o=6,codeExpiration:l=15,maxAttempts:f=5,rateLimitDelay:g=60,requireExistingUser:m=true,customEmailTemplate:c}=t,a$1=e.toLowerCase().trim();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(a$1))throw new Error("Invalid email format");let p=await r.findUserByEmail(a$1);if(!p&&m)return {success:true,message:"If an account with this email exists, a password reset code has been sent."};if(!p&&!m)throw new Error("No account found with this email address.");let i=new Date,s=`forgot_password_rate_limit_${a$1}`,n=A.get(s);if(n&&i.getTime()-n<g*1e3)throw new Error(`Please wait ${g} seconds before requesting another password reset code.`);try{$();let d=z(o),b=await D(d),P=new Date(Date.now()+l*60*1e3),I=randomUUID();w.set(I,{code:"",hashedCode:b,email:a$1,expiresAt:P,attempts:0,createdAt:new Date,isUsed:!1});let F="Password Reset Code",H=`Your password reset code is: ${d}. This code expires in ${l} minutes.`,v;if(c){let S=c(a$1,d,p?.username);F=S.subject,H=S.text,v=S.html;}else v=`
<div style="max-width: 600px; margin: 0 auto; padding: 20px; font-family: Arial, sans-serif;">
<h2 style="color: #333; text-align: center;">Password Reset</h2>
<div style="background: #f5f5f5; padding: 20px; border-radius: 8px; margin: 20px 0;">
<p>Hello${p?.username?` ${p.username}`:""},</p>
<p>We received a request to reset your password. Use the code below to reset your password:</p>
<div style="text-align: center; margin: 20px 0;">
<span style="background: #007bff; color: white; padding: 12px 24px; border-radius: 4px; font-size: 18px; font-weight: bold; letter-spacing: 2px; display: inline-block;">${d}</span>
</div>
<p><strong>This code expires in ${l} minutes.</strong></p>
<p>If you didn't request this password reset, please ignore this email.</p>
</div>
<p style="color: #666; font-size: 12px; text-align: center;">
This is an automated message, please do not reply.
</p>
</div>
`;return A.set(s,i.getTime()),{success:!0,message:"Password reset code sent to your email address.",codeExpiration:P}}catch(d){throw d instanceof Error&&d.message.includes("rate limit")?d:new Error(`Failed to send password reset code: ${d instanceof Error?d.message:"Unknown error"}`)}}async function M(e,t,r,o={}){let l=a.db;if(!l)throw new Error("Database not configured. Make sure initAuth() is called before using forgot password functions.");let{minPasswordLength:f=8,requireStrongPassword:g=true,invalidateAllSessions:m=true,preventReuse:c=true}=o,a$1=e.toLowerCase().trim();if(!t||t.trim().length===0)throw new Error("Reset code is required");if(!r||r.length<f)throw new Error(`Password must be at least ${f} characters long`);if(g){let i=/[A-Z]/.test(r),s=/[a-z]/.test(r),n=/\d/.test(r),d=/\W/.test(r);if(!i||!s||!n||!d)throw new Error("Password must contain at least one uppercase letter, lowercase letter, number, and special character")}let u=await l.findUserByEmail(a$1);if(!u)throw new Error("Invalid reset code or email address");try{$();let i=null,s=null;for(let[n,d]of Array.from(w.entries()))if(d.email===a$1&&!d.isUsed&&await B(t,d.hashedCode)){i=d,s=n;break}if(!i||!s)throw new Error("Invalid reset code");if(new Date>i.expiresAt)throw w.delete(s),new Error("Reset code has expired");if(i.attempts>=5)throw w.delete(s),new Error("Maximum verification attempts exceeded");i.attempts++,i.isUsed=!0,w.delete(s);}catch{throw new Error("Invalid or expired reset code")}if(c&&u.password)try{let{verifyPassword:i}=await import('./hash-2A3J3NDY.mjs');if(await i(r,u.password))throw new Error("New password cannot be the same as your current password")}catch{}let p=await b(r);try{let i;if(l.updateUser)i=await l.updateUser(u.id,{password:p,passwordChangedAt:new Date});else throw new Error("Database adapter does not support password updates");if(!i)throw new Error("Failed to update password");return {success:!0,message:"Password has been reset successfully",user:{id:i.id,email:i.email,username:i.username}}}catch(i){throw new Error(`Failed to reset password: ${i instanceof Error?i.message:"Unknown error"}`)}}function j(e=12){let t="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*",r="";r+="ABCDEFGHIJKLMNOPQRSTUVWXYZ"[Math.floor(Math.random()*26)],r+="abcdefghijklmnopqrstuvwxyz"[Math.floor(Math.random()*26)],r+="0123456789"[Math.floor(Math.random()*10)],r+="!@#$%^&*"[Math.floor(Math.random()*8)];for(let o=r.length;o<e;o++)r+=t[Math.floor(Math.random()*t.length)];return r.split("").sort(()=>Math.random()-.5).join("")}async function J(e,t={}){let r=a.db;if(!r)throw new Error("Database not configured. Make sure initAuth() is called before using forgot password functions.");let{temporaryPasswordLength:o=12,requireExistingUser:l=true,customEmailTemplate:f}=t,g=e.toLowerCase().trim(),m=await r.findUserByEmail(g);if(!m&&l)return {success:true,message:"If an account with this email exists, a temporary password has been sent."};if(!m&&!l)throw new Error("No account found with this email address.");let c=j(o),a$1=await b(c);try{let u;if(r.updateUser)u=await r.updateUser(m.id,{password:a$1,passwordChangedAt:new Date,mustChangePassword:!0});else throw new Error("Database adapter does not support password updates");if(!u)throw new Error("Failed to update password");let p="Temporary Password",i=`Your temporary password is: ${c}. Please log in and change your password immediately.`,s;if(f){let n=f(g,c,m?.username);p=n.subject,i=n.text,s=n.html;}else s=`
<div style="max-width: 600px; margin: 0 auto; padding: 20px; font-family: Arial, sans-serif;">
<h2 style="color: #333; text-align: center;">Temporary Password</h2>
<div style="background: #f5f5f5; padding: 20px; border-radius: 8px; margin: 20px 0;">
<p>Hello${m?.username?` ${m.username}`:""},</p>
<p>We've generated a temporary password for your account:</p>
<div style="text-align: center; margin: 20px 0;">
<span style="background: #dc3545; color: white; padding: 12px 24px; border-radius: 4px; font-size: 16px; font-weight: bold; display: inline-block;">${c}</span>
</div>
<p><strong>\u26A0\uFE0F Please log in and change this password immediately for security.</strong></p>
<p>If you didn't request this password reset, please contact support immediately.</p>
</div>
<p style="color: #666; font-size: 12px; text-align: center;">
This is an automated message, please do not reply.
</p>
</div>
`;return {success:!0,message:"Temporary password sent to your email address."}}catch(u){throw new Error(`Failed to send temporary password: ${u instanceof Error?u.message:"Unknown error"}`)}}async function E(e,t={}){let r=a.db;if(!r)throw new Error("Database not configured. Make sure initAuth() is called before using SSO functions.");let{autoCreateUser:o=true,updateExistingUser:l=false,requireVerifiedEmail:f=true,mergeUserData:g=true,customUserMapping:m}=t;if(!e.email)throw new Error(`${e.provider} SSO did not provide email address`);if(!e.id)throw new Error(`${e.provider} SSO did not provide user ID`);if(f&&e.verified===false)throw new Error(`${e.provider} SSO email is not verified`);let c=e.email.toLowerCase().trim(),a$2=await r.findUserByEmail(c),u=false;if(a$2){if(l&&g){let s={};if(m)Object.assign(s,m(e));else {if(e.name&&!a$2.firstName&&!a$2.lastName){let n=e.name.split(" ");s.firstName=n[0],n.length>1&&(s.lastName=n.slice(1).join(" "));}if(e.firstName&&!a$2.firstName&&(s.firstName=e.firstName),e.lastName&&!a$2.lastName&&(s.lastName=e.lastName),!a$2.username&&e.email){let d=e.email.split("@")[0].toLowerCase().replace(/[^a-z0-9]/g,"");try{r.findUserByUsername&&await r.findUserByUsername(d)?s.username=`${d}${Math.floor(Math.random()*1e3)}`:s.username=d;}catch{}}}if(Object.keys(s).length>0&&r.updateUser&&(a$2=await r.updateUser(a$2.id,s),!a$2))throw new Error("Failed to update user with SSO data")}}else {if(!o)throw new Error(`No account found for ${c}. Please create an account first.`);let s={email:c,password:d(32),emailVerified:f?e.verified!==false:true,emailVerifiedAt:f&&e.verified!==false?new Date:void 0};if(m)Object.assign(s,m(e));else {if(e.name){let n=e.name.split(" ");s.firstName=n[0],n.length>1&&(s.lastName=n.slice(1).join(" "));}if(e.firstName&&(s.firstName=e.firstName),e.lastName&&(s.lastName=e.lastName),e.email){let d=e.email.split("@")[0].toLowerCase().replace(/[^a-z0-9]/g,"");try{r.findUserByUsername&&await r.findUserByUsername(d)?s.username=`${d}${Math.floor(Math.random()*1e3)}`:s.username=d;}catch{}}}try{a$2=await r.createUser(s),u=!0;}catch(n){if(n instanceof Error&&n.message.includes("already exists")){if(a$2=await r.findUserByEmail(c),!a$2)throw new Error("Failed to create or find user after SSO authentication")}else throw new Error(`Failed to create user: ${n instanceof Error?n.message:"Unknown error"}`)}}let p={id:a$2.id,email:a$2.email,provider:e.provider};a$2.username&&(p.username=a$2.username);let i=a$1(p);return {user:{id:a$2.id,email:a$2.email,username:a$2.username,firstName:a$2.firstName,lastName:a$2.lastName},token:i,cookieOptions:{httpOnly:true,secure:process.env.NODE_ENV==="production",maxAge:1e3*60*60*24*7,sameSite:"lax",path:"/"},isNewUser:u,provider:e.provider}}async function T(e,t={}){try{let{handleGoogleCallback:r}=await import('./providers/google.mjs'),o=await r(e),l={id:o.id,email:o.email,name:o.name,avatar:o.avatar,provider:"google",verified:!0};return E(l,t)}catch(r){throw new Error(`Google SSO failed: ${r instanceof Error?r.message:"Unknown error"}`)}}async function L(e,t={}){try{let{handleGitHubCallback:r}=await import('./providers/github.mjs'),o=await r(e),l={id:o.id,email:o.email,name:o.name,avatar:o.avatar,provider:"github",verified:!0};return E(l,t)}catch(r){throw new Error(`GitHub SSO failed: ${r instanceof Error?r.message:"Unknown error"}`)}}async function te(e,t,r={}){let o={...t,provider:e};return E(o,r)}function y(e){let t={timestamp:Date.now(),nonce:randomUUID(),...e};return Buffer.from(JSON.stringify(t)).toString("base64url")}function O(e,t=3e5){try{let r=JSON.parse(Buffer.from(e,"base64url").toString());if(!r.timestamp||!r.nonce)throw new Error("Invalid state format");if(Date.now()-r.timestamp>t)throw new Error("State has expired");return r}catch(r){throw new Error(`Invalid SSO state: ${r instanceof Error?r.message:"Unknown error"}`)}}var ce={getGoogleAuthUrl(e="/dashboard"){let t=y({redirect:e});return a$3(t)},getGitHubAuthUrl(e="/dashboard"){let t=y({redirect:e});return a$2(t)},async handleCallback(e,t,r,o={}){let l=O(r);return {...e==="google"?await T(t,o):await L(t,o),redirectUrl:l.redirect||"/dashboard"}},generateState(e){return y(e)},verifyState(e,t){return O(e,t)}},ue={async initiate(e,t={}){return await k(e,t)},async reset(e,t,r,o={}){return await M(e,t,r,o)}};export{k as a,M as b,j as c,J as d,E as e,T as f,L as g,te as h,y as i,O as j,ce as k,ue as l};