UNPKG

authrix

Version:

Lightweight, flexible authentication library for Node.js and TypeScript.

33 lines 12.1 kB
'use strict';var chunk2CZW2LBY_cjs=require('./chunk-2CZW2LBY.cjs'),chunkL5SDNP3F_cjs=require('./chunk-L5SDNP3F.cjs'),chunkRUOJ57HW_cjs=require('./chunk-RUOJ57HW.cjs'),chunkO5GBK76P_cjs=require('./chunk-O5GBK76P.cjs'),chunkIRGTWVM7_cjs=require('./chunk-IRGTWVM7.cjs'),crypto=require('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=chunkIRGTWVM7_cjs.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=e.toLowerCase().trim();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(a))throw new Error("Invalid email format");let p=await r.findUserByEmail(a);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}`,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=crypto.randomUUID();w.set(I,{code:"",hashedCode:b,email:a,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,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=chunkIRGTWVM7_cjs.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=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);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&&!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-YAW4H3KD.cjs');if(await i(r,u.password))throw new Error("New password cannot be the same as your current password")}catch{}let p=await chunkO5GBK76P_cjs.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=chunkIRGTWVM7_cjs.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=await chunkO5GBK76P_cjs.b(c);try{let u;if(r.updateUser)u=await r.updateUser(m.id,{password:a,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=chunkIRGTWVM7_cjs.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=await r.findUserByEmail(c),u=false;if(a){if(l&&g){let s={};if(m)Object.assign(s,m(e));else {if(e.name&&!a.firstName&&!a.lastName){let n=e.name.split(" ");s.firstName=n[0],n.length>1&&(s.lastName=n.slice(1).join(" "));}if(e.firstName&&!a.firstName&&(s.firstName=e.firstName),e.lastName&&!a.lastName&&(s.lastName=e.lastName),!a.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=await r.updateUser(a.id,s),!a))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:chunkO5GBK76P_cjs.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=await r.createUser(s),u=!0;}catch(n){if(n instanceof Error&&n.message.includes("already exists")){if(a=await r.findUserByEmail(c),!a)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.id,email:a.email,provider:e.provider};a.username&&(p.username=a.username);let i=chunkRUOJ57HW_cjs.a(p);return {user:{id:a.id,email:a.email,username:a.username,firstName:a.firstName,lastName:a.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.cjs'),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.cjs'),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:crypto.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 chunkL5SDNP3F_cjs.a(t)},getGitHubAuthUrl(e="/dashboard"){let t=y({redirect:e});return chunk2CZW2LBY_cjs.a(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)}};exports.a=k;exports.b=M;exports.c=j;exports.d=J;exports.e=E;exports.f=T;exports.g=L;exports.h=te;exports.i=y;exports.j=O;exports.k=ce;exports.l=ue;