@sqrzro/server 2.0.0-bz.3 → 2.0.0-bz.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +25 -1
  3. package/auth.d.ts +1 -0
  4. package/auth.js +1 -0
  5. package/cache.d.ts +1 -0
  6. package/cache.js +1 -0
  7. package/dist/auth/index.cjs +59 -0
  8. package/dist/auth/index.cjs.map +1 -0
  9. package/dist/auth/index.d.cts +100 -0
  10. package/dist/auth/index.d.ts +100 -0
  11. package/dist/auth/index.js +21 -0
  12. package/dist/auth/index.js.map +1 -0
  13. package/dist/cache/index.cjs +10 -0
  14. package/dist/cache/index.cjs.map +1 -0
  15. package/dist/cache/index.d.cts +4 -0
  16. package/dist/cache/index.d.ts +4 -0
  17. package/dist/cache/index.js +7 -0
  18. package/dist/cache/index.js.map +1 -0
  19. package/dist/database/schema.cjs +16 -0
  20. package/dist/database/schema.cjs.map +1 -0
  21. package/dist/database/schema.d.cts +288 -0
  22. package/dist/database/schema.d.ts +288 -0
  23. package/dist/database/schema.js +7 -0
  24. package/dist/database/schema.js.map +1 -0
  25. package/dist/forms/index.cjs +22 -0
  26. package/dist/forms/index.cjs.map +1 -0
  27. package/dist/forms/index.d.cts +50 -0
  28. package/dist/forms/index.d.ts +50 -0
  29. package/dist/forms/index.js +8 -0
  30. package/dist/forms/index.js.map +1 -0
  31. package/dist/lists/index.cjs +9 -0
  32. package/dist/lists/index.cjs.map +1 -0
  33. package/dist/lists/index.d.cts +18 -0
  34. package/dist/lists/index.d.ts +18 -0
  35. package/dist/lists/index.js +7 -0
  36. package/dist/lists/index.js.map +1 -0
  37. package/dist/mail/index.cjs +9 -0
  38. package/dist/mail/index.cjs.map +1 -0
  39. package/dist/mail/index.d.cts +13 -0
  40. package/dist/mail/index.d.ts +13 -0
  41. package/dist/mail/index.js +5 -0
  42. package/dist/mail/index.js.map +1 -0
  43. package/dist/middleware.cjs +9 -0
  44. package/dist/middleware.cjs.map +1 -0
  45. package/dist/middleware.d.cts +5 -0
  46. package/dist/middleware.d.ts +5 -4
  47. package/dist/middleware.js +7 -36
  48. package/dist/middleware.js.map +1 -0
  49. package/dist/url/index.cjs +12 -0
  50. package/dist/url/index.cjs.map +1 -0
  51. package/dist/url/index.d.cts +33 -0
  52. package/dist/url/index.d.ts +33 -0
  53. package/dist/url/index.js +7 -0
  54. package/dist/url/index.js.map +1 -0
  55. package/forms.d.ts +1 -0
  56. package/forms.js +1 -0
  57. package/jest.config.js +7 -0
  58. package/lists.d.ts +1 -0
  59. package/lists.js +1 -0
  60. package/mail.d.ts +1 -0
  61. package/mail.js +1 -0
  62. package/middleware.js +1 -1
  63. package/package.json +74 -20
  64. package/schema.d.ts +1 -0
  65. package/schema.js +1 -0
  66. package/url.d.ts +1 -0
  67. package/url.js +1 -0
  68. package/dist/AuthService.d.ts +0 -10
  69. package/dist/AuthService.js +0 -36
  70. package/dist/DataService.d.ts +0 -29
  71. package/dist/DataService.js +0 -64
  72. package/dist/LoginRequest.d.ts +0 -4
  73. package/dist/LoginRequest.js +0 -11
  74. package/dist/PasswordService.d.ts +0 -6
  75. package/dist/PasswordService.js +0 -63
  76. package/dist/RequestService.d.ts +0 -21
  77. package/dist/RequestService.js +0 -121
  78. package/dist/SessionService.d.ts +0 -5
  79. package/dist/SessionService.js +0 -56
  80. package/dist/index.d.ts +0 -7
  81. package/dist/index.js +0 -10
  82. package/dist/interfaces.d.ts +0 -11
  83. package/dist/interfaces.js +0 -2
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2023 Richard Carter
1
+ Copyright 2024 Richard Carter
2
2
 
3
3
  Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
4
 
package/README.md CHANGED
@@ -1 +1,25 @@
1
- Square Zero Components
1
+ <div align="center">
2
+
3
+ <h1>
4
+ <img src="assets/logo.svg" alt="" width="64" style="margin-bottom: 5px">
5
+ <br />
6
+ Square Zero Server
7
+ </h1>
8
+
9
+ </div>
10
+
11
+ <hr />
12
+
13
+ ## @sqrzro/server/auth
14
+
15
+ ## @sqrzro/server/cache
16
+
17
+ ## @sqrzro/server/db
18
+
19
+ ## @sqrzro/server/forms
20
+
21
+ ## @sqrzro/server/lists
22
+
23
+ ## @sqrzro/server/mail
24
+
25
+ ## @sqrzro/server/url
package/auth.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/auth/index';
package/auth.js ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/auth/index';
package/cache.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/cache/index';
package/cache.js ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/cache/index';
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+
3
+ var drizzleOrm = require('drizzle-orm');
4
+ var postgresJs = require('drizzle-orm/postgres-js');
5
+ var de = require('postgres');
6
+ var pgCore = require('drizzle-orm/pg-core');
7
+ var server = require('next/server');
8
+ var C = require('joi');
9
+ var otplib = require('otplib');
10
+ var ze = require('qrcode');
11
+ var adapterDrizzle = require('@lucia-auth/adapter-drizzle');
12
+ var lucia = require('lucia');
13
+ var headers = require('next/headers');
14
+ var pathToRegexp = require('path-to-regexp');
15
+ var redis = require('redis');
16
+ var utility = require('@sqrzro/utility');
17
+ var Y = require('bcryptjs');
18
+
19
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
20
+
21
+ var de__default = /*#__PURE__*/_interopDefault(de);
22
+ var C__default = /*#__PURE__*/_interopDefault(C);
23
+ var ze__default = /*#__PURE__*/_interopDefault(ze);
24
+ var Y__default = /*#__PURE__*/_interopDefault(Y);
25
+
26
+ function pe(){if(!process.env.DATABASE_URL)throw new Error("DATABASE_URL is not defined");return postgresJs.drizzle(de__default.default(process.env.DATABASE_URL,{prepare:!1}))}var o=globalThis.szdb??pe();process.env.VERCEL_ENV||(globalThis.szdb=o);var be=10,he=pgCore.pgEnum("mfaType",["TOTP","HARDWARE"]),we=pgCore.pgEnum("scope",["ANON","MFA","AUTHED"]),y=pgCore.pgSchema("auth"),i=y.table("user_credentials",{id:pgCore.text("id").primaryKey(),email:pgCore.text("email").notNull().unique(),password:pgCore.text("password"),role:pgCore.integer("role").notNull().default(be)}),L=y.table("sessions",{id:pgCore.text("id").primaryKey(),userId:pgCore.text("userId").notNull().references(()=>i.id),scope:we("scope").notNull().default("ANON"),expiresAt:pgCore.timestamp("expiresAt").notNull()}),c=y.table("resets",{id:pgCore.text("id").primaryKey(),userId:pgCore.text("userId").notNull().references(()=>i.id),expiresAt:pgCore.timestamp("expiresAt").notNull()}),u=y.table("mfas",{id:pgCore.text("id").primaryKey(),name:pgCore.text("name").notNull(),userId:pgCore.text("userId").notNull().references(()=>i.id),type:he("type").notNull().default("TOTP"),secret:pgCore.text("secret").notNull(),verifiedAt:pgCore.timestamp("verifiedAt")}),h=y.table("client_credentials",{id:pgCore.text("id").primaryKey(),alias:pgCore.text("alias").notNull().unique(),secret:pgCore.text("secret").notNull().unique()});var O=class extends Error{messages;constructor(r){super(JSON.stringify(r)),this.messages=r,this.name="ValidationError";}},b=O;var ye={"alternatives.all":"","alternatives.any":"","alternatives.match":"","alternatives.one":"","alternatives.types":"","any.custom":"","any.default":"","any.failover":"","any.invalid":"","any.only":"","any.ref":"","any.required":"{{#label}} is required","any.unknown":"","array.base":"","array.excludes":"","array.includesRequiredBoth":"","array.includesRequiredKnowns":"","array.includesRequiredUnknowns":"","array.includes":"","array.length":"","array.max":"","array.min":"","array.orderedLength":"","array.sort":"","array.sort.mismatching":"","array.sort.unsupported":"","array.sparse":"","array.unique":"","array.hasKnown":"","array.hasUnknown":"","binary.base":"","binary.length":"","binary.max":"","binary.min":"","boolean.base":"","date.base":"","date.format":"","date.greater":"","date.less":"","date.max":"","date.min":"","date.strict":"","function.arity":"","function.class":"","function.maxArity":"","function.minArity":"","number.base":"{{#label}} should be a number","number.greater":"","number.infinity":"","number.integer":"","number.less":"","number.max":"","number.min":"{{#label}} should be greater than or equal to {{#limit}}","number.multiple":"","number.negative":"","number.port":"","number.positive":"","number.precision":"","number.unsafe":"","object.unknown":"","object.and":"","object.assert":"","object.base":"","object.length":"","object.max":"","object.min":"","object.missing":"","object.nand":"","object.pattern.match":"","object.refType":"","object.regex":"","object.rename.multiple":"","object.rename.override":"","object.schema":"","object.instance":"","object.with":"","object.without":"","object.xor":"","object.oxor":"","string.alphanum":"","string.base64":"","string.base":"","string.creditCard":"","string.dataUri":"","string.domain":"","string.email":"","string.empty":"{{#label}} is required","string.guid":"","string.hexAlign":"","string.hex":"","string.hostname":"","string.ipVersion":"","string.ip":"","string.isoDate":"","string.isoDuration":"","string.length":"","string.lowercase":"","string.max":"","string.min":"","string.normalize":"","string.pattern.base":"","string.pattern.name":"","string.pattern.invert.base":"","string.pattern.invert.name":"","string.token":"","string.trim":"","string.uppercase":"","string.uri":"","string.uriCustomScheme":"","string.uriRelativeOnly":"","symbol.base":"","symbol.map":""},I=ye;function Ae(){return Object.entries(I).reduce((e,[r,t])=>t?{...e,[r]:t}:e,{})}function Fe(e){let r=e.details.reduce((t,s)=>({...t,[s.path.join(".")]:s.message.replace(/"/gu,"")}),{});return new b(r)}async function _(e,r,t){try{return [await r.validateAsync(e,{abortEarly:!1,messages:t||Ae(),stripUnknown:!0}),null]}catch(s){return s instanceof C__default.default.ValidationError?[null,Fe(s)]:s instanceof Error?[null,s]:[null,new Error("Unknown validation error occured")]}}function m(e){return C__default.default.object(e)}function xe(e){return {cause:e.cause,message:e.message,name:e.name,stack:e.stack}}function Se(e){return !!Object.prototype.hasOwnProperty.call(e,"fn")}async function w(e){let r={...e.formData};if(e.request){let[s,n]=await _(e.formData,e.request);if(n!==null)return n instanceof b&&e.onValidationError?.(n),[null,xe(n)];r=s;}if(!Se(e))return await e.onSuccess?.(r),[r,null];let t=null;if(t=await e.fn(r),!t)throw new Error("NO_MODEL");return await e.onSuccess?.(t),[t,null]}var Re=m({token:C__default.default.string().pattern(/^[0-9]{6}$/u).required()}),V=Re;var A=null;function ve(e){return e.includes("localhost")||e.includes("127.0.0.1")}async function z(){if(!process.env.REDIS_URL)throw new Error("REDIS_URL is not defined. Access to the cache is not possible.");return A||(A=redis.createClient({socket:{tls:!ve(process.env.REDIS_URL)},url:process.env.REDIS_URL}),await A.connect(),A)}async function J(e){return (await z()).get(e)}async function H(e,r){await(await z()).set(e,r);}function j(){let e=headers.headers().get("x-origin");if(e)return e;let r=headers.headers().get("x-forwarded-proto"),t=headers.headers().get("x-forwarded-host");if(r&&t)return `${r}://${t}`;throw new Error("No origin could be determined")}var $="/auth/login",Me=16,R={ANON:{allowedRoute:"/auth/(login|password)",redirectOnUnauth:$},MFA:{allowedRoute:"/auth/mfa",redirectOnUnauth:"/auth/mfa"},AUTHED:{allowedRoute:"*",redirectOnAuth:"/"}},ke=new adapterDrizzle.DrizzlePostgreSQLAdapter(o,L,i),d=new lucia.Lucia(ke,{sessionCookie:{attributes:{secure:process.env.NODE_ENV==="production"},name:process.env.AUTH_COOKIE_NAME||"auth_session"},getSessionAttributes:e=>({scope:e.scope}),getUserAttributes:e=>({email:e.email,role:e.role})});function p(e="",r=Me){return `${e?`${e}_`:""}${lucia.generateId(r)}`}async function B(e){let r=d.createBlankSessionCookie();return headers.cookies().set(r.name,r.value,r.attributes),d.invalidateSession(e)}async function W(e){return d.invalidateUserSessions(e)}async function E(e,r="ANON"){let t=await d.createSession(e,{scope:r}),s=d.createSessionCookie(t.id);return headers.cookies().set(s.name,s.value,s.attributes),!0}function v(){return headers.cookies().get(d.sessionCookieName)?.value??null}function $r(){return !!v()}function qe(e,r){return r?e===r:F().includes(e)}async function K(e){let r=v();if(!r)return null;let{user:t}=await d.validateSession(r);return !t||!qe(t.role,e)?null:utility.getFromObject(t,["id","email","role"])}function Le(e,r){return r?r==="*"?!0:pathToRegexp.match(r)(e)!==!1:!1}async function G(){let e=await J(`${j()}:scopes`);return e?JSON.parse(e):R}async function Q(e){return (await G())[e]}async function Ie(e){let r={ANON:{...R.ANON,...e?.ANON},MFA:{...R.MFA,...e?.MFA},AUTHED:{...R.AUTHED,...e?.AUTHED}};return H(`${j()}:scopes`,JSON.stringify(r))}async function Ce(e,r){let t=await G(),{session:s,user:n}=await d.validateSession(e),l=t[s&&F().includes(n?.role)?s.scope:"ANON"];return Le(r,l.allowedRoute)?null:l.redirectOnUnauth||$}async function Br(e,r){let t=e.nextUrl.searchParams.get("id")||"",s=e.nextUrl.searchParams.get("pathname")||"/";return await Ie(r),server.NextResponse.json({redirect:await Ce(t,s)})}function f(){return process.env.AUTH_MFA_ENABLED!=="false"}async function tt(e,r){if(!f()||!r)return null;let[t]=await o.select().from(i).where(drizzleOrm.eq(i.email,r)).limit(1);if(!t)return null;let s=otplib.authenticator.generateSecret(),n=otplib.authenticator.keyuri(r,e,s);return await o.delete(u).where(drizzleOrm.and(drizzleOrm.eq(u.userId,t.id),drizzleOrm.isNull(u.verifiedAt))),await o.insert(u).values({id:p(),name:"Default",secret:s,userId:t.id}),new Promise((l,g)=>{ze__default.default.toDataURL(n,{rendererOpts:{quality:1},margin:0,scale:2},(T,ce)=>{T&&g(T),l(ce);});})}async function st(e){if(!f())return !1;let[r]=await o.select().from(u).where(drizzleOrm.and(drizzleOrm.eq(u.userId,e.id),drizzleOrm.isNotNull(u.verifiedAt))).limit(1);return !!r}async function Je(e){f()&&await o.update(u).set({verifiedAt:new Date}).where(drizzleOrm.eq(u.userId,e));}async function He(e,r){if(!f())return !1;let[t]=await o.select().from(u).where(drizzleOrm.eq(u.userId,e)).limit(1);return t?otplib.authenticator.check(r,t.secret):!1}async function $e(e){if(!f())return !1;let r=await K();return !r||!await He(r.id,e.token)?!1:(await Je(r.id),E(r.id,"AUTHED"))}async function ot(e){return f()?await w({fn:$e,formData:e,request:V}):[null,new Error("MFA is not enabled")]}var Be=12,Z={min:8,upper:1,lower:1,number:1,symbol:1};function We(e,r){return e.length>=r}function Ke(e,r){return e.replace(/[^A-Z]/gu,"").length>=r}function Ge(e,r){return e.replace(/[^a-z]/gu,"").length>=r}function Qe(e,r){return e.replace(/[^0-9]/gu,"").length>=r}function Xe(e,r){return e.replace(/[^$]/gu,"").length>=r}var Ye={min:We,upper:Ke,lower:Ge,number:Qe,symbol:Xe};async function S(e){return await Y__default.default.hash(e,Be)}async function U(e,r){return !e||!r?!1:await Y__default.default.compare(e,r)}async function Ze(e,r=Z){let s=Object.entries(r).reduce((n,[l,g])=>(n[l]=Ye[l](e,g),n),{});return Promise.resolve(s)}async function at(e,r=Z){let t=await Ze(e,r);return Promise.resolve(Object.values(t).every(Boolean))}var er=m({email:C__default.default.string().email({minDomainSegments:2,tlds:!1}).required(),password:C__default.default.string().min(8).required()}),re=er;var tr=m({email:C__default.default.string().max(60).email({minDomainSegments:2,tlds:!1}).required().messages({"any.required":"Please provide your email address, so we can send you a reset link","string.empty":"Please provide your email address, so we can send you a reset link","string.email":"Please make sure your email address is valid","string.max":"Please make sure your email address is valid"})}),te=tr;var sr=m({token:C__default.default.string().pattern(/[a-z0-9]{40}/u).required(),password:C__default.default.string().required().messages({"any.required":"Please provide your new password","string.empty":"Please provide your new password"})}),oe=sr;var or=40,nr=36e5;async function Tt(){let e=v();e&&await B(e);}function F(){let e=process.env.AUTH_ALLOWED_ROLES;if(!e)throw new Error("AUTH_ALLOWED_ROLES is not defined. Authentication will not be possible.");return e.split(",").map(r=>Number(r)).filter(r=>!isNaN(r))}async function ae(e){return await E(e,f()?"MFA":"AUTHED"),(await Q("AUTHED"))?.redirectOnAuth||null}async function le(e){let[r]=await o.select().from(i).where(drizzleOrm.and(drizzleOrm.eq(i.email,e),drizzleOrm.inArray(i.role,F()))).limit(1);return r}async function ir({email:e,password:r}){let t=await le(e);if(!t?.password||!await U(r,t.password))throw new b({email:"",password:""});let s=await ae(t.id);if(!s)throw new b({email:"",password:""});return s}async function Dt(e,r){return console.log("handleLoginForm",r),await w({fn:ir,formData:e,onSuccess:async()=>{console.log("onSuccess",r),await r?.();},request:re})}async function Ot({email:e,password:r,role:t}){let s=r?await S(r):null,[n]=await o.insert(i).values({id:p(),email:e,password:s,role:t}).returning();return n}async function ar(e){let r=await le(e);if(!r)return null;await o.delete(c).where(drizzleOrm.eq(c.userId,r.id));let t=p("",or);return await o.insert(c).values({id:t,userId:r.id,expiresAt:new Date(new Date().getTime()+nr)}),t}async function Nt(e,r){async function t(n){let l=await ar(n.email);return l?r(n.email,l):!0}return await w({fn:t,formData:e,request:te})}async function lr(e,r){let[t]=await o.select().from(c).where(drizzleOrm.eq(c.id,r)).limit(1);return !t||(await o.delete(c).where(drizzleOrm.eq(c.id,r)),!t||t.expiresAt<new Date)?null:(await W(t.userId),await o.update(i).set({password:await S(e)}).where(drizzleOrm.and(drizzleOrm.eq(i.id,t.userId),drizzleOrm.inArray(i.role,F()))),t.userId)}async function jt(e){async function r(s){let n=await lr(s.password,s.token);return n?ae(n):null}return await w({fn:r,formData:e,request:oe})}var ur=16,cr=64;async function _t(e){let[r]=await o.select().from(h).where(drizzleOrm.eq(h.id,e)).limit(1);return r}async function Vt({alias:e,id:r,secret:t}){let[s]=await o.insert(h).values({alias:e,id:r||p("",ur),secret:await S(t||p("",cr))}).returning();return s}async function zt(e){let{headers:r}=e,t=r.get("authorization");if(!t)return null;let s=Buffer.from(t.replace("Basic ",""),"base64").toString("utf-8").replace(/:$/u,""),[n,...l]=s.split("-"),[g]=await o.select().from(h).where(drizzleOrm.eq(h.id,n)).limit(1);return g&&await U(l.join(""),g.secret)?g:null}
27
+
28
+ exports.checkMFAEnabled = f;
29
+ exports.checkPasswordComplexity = at;
30
+ exports.checkRouteAllowed = Le;
31
+ exports.checkSessionExists = $r;
32
+ exports.checkUserHasMFA = st;
33
+ exports.createUserSession = E;
34
+ exports.generateID = p;
35
+ exports.generateMFA = tt;
36
+ exports.getAllowedRoles = F;
37
+ exports.getClientByID = _t;
38
+ exports.getPasswordComplexity = Ze;
39
+ exports.getScopeByID = Q;
40
+ exports.getScopes = G;
41
+ exports.getSessionID = v;
42
+ exports.getSessionUser = K;
43
+ exports.handleClientAuth = zt;
44
+ exports.handleLoginForm = Dt;
45
+ exports.handleLogout = Tt;
46
+ exports.handleMFAForm = ot;
47
+ exports.handlePasswordForm = Nt;
48
+ exports.handlePasswordResetForm = jt;
49
+ exports.handleSession = Br;
50
+ exports.hashPassword = S;
51
+ exports.invalidateSession = B;
52
+ exports.invalidateUserSessions = W;
53
+ exports.lucia = d;
54
+ exports.registerClient = Vt;
55
+ exports.registerUser = Ot;
56
+ exports.setScopes = Ie;
57
+ exports.verifyPassword = U;
58
+ //# sourceMappingURL=out.js.map
59
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/auth/AuthService.ts","../../src/database/DatabaseService.ts","../../src/database/schema.ts","../../src/forms/FormService.ts","../../src/forms/ValidationError.ts","../../src/forms/ValidationService.ts","../../src/forms/lang.ts","../../src/auth/MFAService.ts","../../src/auth/MFARequest.ts","../../src/auth/SessionService.ts","../../src/cache/CacheService.ts","../../src/url/URLService.ts","../../src/auth/PasswordService.ts","../../src/auth/LoginRequest.ts","../../src/auth/PasswordRequest.ts","../../src/auth/PasswordResetRequest.ts","../../src/auth/ClientService.ts"],"names":["and","eq","inArray","drizzle","postgres","createSingleton","db","integer","pgEnum","pgSchema","text","timestamp","DEFAULT_ROLE","mfaType","scope","authSchema","authUserTable","authSessionTable","authResetTable","authMFATable","authClientTable","NextResponse","ValidationError","messages","ValidationError_default","Joi","lang_default","getErrorMessages","acc","key","value","transformErrors","error","cur","validateSchema","formData","validation","err","createSchema","schema","serializeError","hasFn","args","submitForm","data","validated","validationError","model","isNotNull","isNull","authenticator","qrcode","MFARequest","MFARequest_default","DrizzlePostgreSQLAdapter","Lucia","generateId","cookies","match","createClient","client","isLocalhost","url","getClient","getFromCache","setToCache","headers","getOrigin","origin","proto","host","getFromObject","DEFAULT_REDIRECT","ID_LENGTH","DEFAULT_SCOPES","adapter","lucia","attributes","generateID","prefix","length","invalidateSession","id","cookie","invalidateUserSessions","createUserSession","session","sessionCookie","getSessionID","checkSessionExists","checkUserRole","userRole","targetRole","getAllowedRoles","getSessionUser","role","sessionID","user","checkRouteAllowed","pathname","route","getScopes","scopes","getScopeByID","setScopes","customScopes","validateSessionFromID","handleSession","request","checkMFAEnabled","generateMFA","name","email","secret","otpauth","resolve","reject","checkUserHasMFA","mfa","markAsVerified","userID","validateUserToken","token","handleMFA","handleMFAForm","bcrypt","PW_SALT_ROUNDS","PASSWORD_RULES","checkPasswordMin","password","checkPasswordUpper","checkPasswordLower","checkPasswordNumber","checkPasswordSymbol","PASSWORD_FUNCTIONS","hashPassword","verifyPassword","encrypted","getPasswordComplexity","rules","validity","rule","checkPasswordComplexity","LoginRequest","LoginRequest_default","PasswordRequest","PasswordRequest_default","PasswordResetRequest","PasswordResetRequest_default","RESET_TOKEN_LENGTH","RESET_TOKEN_EXPIRY","handleLogout","roles","handleUserSession","getUserByEmail","loginUser","handleLoginForm","onSuccess","registerUser","hash","createPasswordResetToken","handlePasswordForm","mailFn","mutateFn","validatePasswordResetToken","result","handlePasswordResetForm","SECRET_LENGTH","getClientByID","registerClient","alias","handleClientAuth","header","auth"],"mappings":"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","sourcesContent":["import type { Errorable } from '@sqrzro/interfaces';\nimport { and, eq, inArray } from 'drizzle-orm';\n\nimport { db } from '../database/DatabaseService';\nimport { authResetTable, authUserTable } from '../database/schema';\n\nimport { submitForm } from '../forms/FormService';\nimport ValidationError from '../forms/ValidationError';\n\nimport { checkMFAEnabled } from './MFAService';\nimport { hashPassword, verifyPassword } from './PasswordService';\nimport {\n createUserSession,\n generateID,\n getScopeByID,\n getSessionID,\n invalidateSession,\n invalidateUserSessions,\n} from './SessionService';\n\nimport LoginRequest from './LoginRequest';\nimport PasswordRequest from './PasswordRequest';\nimport PasswordResetRequest from './PasswordResetRequest';\n\nimport type {\n LoginFormFields,\n PasswordFormFields,\n PasswordResetFormFields,\n UserObject,\n} from './interfaces';\n\nconst RESET_TOKEN_LENGTH = 40;\n\n// Set expiry to 1 hour (in ms)\nconst RESET_TOKEN_EXPIRY = 3600000;\n\nexport async function handleLogout(): Promise<void> {\n const id = getSessionID();\n\n if (id) {\n await invalidateSession(id);\n }\n}\n\ninterface LoginUserArgs {\n email: string;\n password: string;\n role?: number;\n token?: string;\n}\n\nexport function getAllowedRoles(): number[] {\n const roles = process.env.AUTH_ALLOWED_ROLES;\n\n if (!roles) {\n throw new Error('AUTH_ALLOWED_ROLES is not defined. Authentication will not be possible.');\n }\n\n return roles\n .split(',')\n .map((role) => Number(role))\n .filter((role) => !isNaN(role));\n}\n\nasync function handleUserSession(userID: string): Promise<string | null> {\n await createUserSession(userID, checkMFAEnabled() ? 'MFA' : 'AUTHED');\n\n const scope = await getScopeByID('AUTHED');\n return scope?.redirectOnAuth || null;\n}\n\nasync function getUserByEmail(email: string): Promise<UserObject | null> {\n const [user] = await db\n .select()\n .from(authUserTable)\n .where(and(eq(authUserTable.email, email), inArray(authUserTable.role, getAllowedRoles())))\n .limit(1);\n\n return user;\n}\n\nasync function loginUser({ email, password }: LoginUserArgs): Promise<string> {\n const user = await getUserByEmail(email);\n\n if (!user?.password || !(await verifyPassword(password, user.password))) {\n throw new ValidationError({ email: '', password: '' });\n }\n\n const session = await handleUserSession(user.id);\n\n if (!session) {\n throw new ValidationError({ email: '', password: '' });\n }\n\n return session;\n}\n\nexport async function handleLoginForm(\n formData: LoginFormFields,\n onSuccess?: () => Promise<void>\n): Promise<Errorable<string>> {\n console.log('handleLoginForm', onSuccess);\n\n const response = await submitForm<LoginFormFields, string>({\n fn: loginUser,\n formData,\n onSuccess: async () => {\n console.log('onSuccess', onSuccess);\n await onSuccess?.();\n },\n request: LoginRequest,\n });\n\n return response;\n}\n\ninterface RegisterUserArgs {\n email: string;\n password?: string;\n role?: number;\n}\n\nexport async function registerUser({\n email,\n password,\n role,\n}: RegisterUserArgs): Promise<UserObject | null> {\n const hash = password ? await hashPassword(password) : null;\n\n const [user] = await db\n .insert(authUserTable)\n .values({ id: generateID(), email, password: hash, role })\n .returning();\n\n return user;\n}\n\nasync function createPasswordResetToken(email: string): Promise<string | null> {\n const user = await getUserByEmail(email);\n\n if (!user) {\n return null;\n }\n\n await db.delete(authResetTable).where(eq(authResetTable.userId, user.id));\n\n const id = generateID('', RESET_TOKEN_LENGTH);\n\n await db.insert(authResetTable).values({\n id,\n userId: user.id,\n expiresAt: new Date(new Date().getTime() + RESET_TOKEN_EXPIRY),\n });\n\n return id;\n}\n\nexport async function handlePasswordForm(\n formData: PasswordFormFields,\n mailFn: (email: string, token: string) => Promise<boolean>\n): Promise<Errorable<boolean>> {\n async function mutateFn(data: PasswordFormFields): Promise<boolean> {\n const token = await createPasswordResetToken(data.email);\n\n if (!token) {\n // Return true, even though the email doesn't exist (to prevent user enumeration)\n return true;\n }\n\n return mailFn(data.email, token);\n }\n\n const response = await submitForm<PasswordFormFields, boolean>({\n fn: mutateFn,\n formData,\n request: PasswordRequest,\n });\n\n return response;\n}\n\nasync function validatePasswordResetToken(password: string, token: string): Promise<string | null> {\n const [result] = await db\n .select()\n .from(authResetTable)\n .where(eq(authResetTable.id, token))\n .limit(1);\n\n if (!result) {\n return null;\n }\n\n await db.delete(authResetTable).where(eq(authResetTable.id, token));\n\n if (!result || result.expiresAt < new Date()) {\n return null;\n }\n\n await invalidateUserSessions(result.userId);\n\n await db\n .update(authUserTable)\n .set({ password: await hashPassword(password) })\n .where(\n and(eq(authUserTable.id, result.userId), inArray(authUserTable.role, getAllowedRoles()))\n );\n\n return result.userId;\n}\n\nexport async function handlePasswordResetForm(\n formData: PasswordResetFormFields\n): Promise<Errorable<string | null>> {\n async function mutateFn(data: PasswordResetFormFields): Promise<string | null> {\n const userID = await validatePasswordResetToken(data.password, data.token);\n\n if (!userID) {\n return null;\n }\n\n return handleUserSession(userID);\n }\n\n const response = await submitForm<PasswordResetFormFields, string | null>({\n fn: mutateFn,\n formData,\n request: PasswordResetRequest,\n });\n\n return response;\n}\n","import { drizzle } from 'drizzle-orm/postgres-js';\nimport type { PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport postgres from 'postgres';\n\ndeclare global {\n var szdb: ReturnType<typeof createSingleton> | undefined; // eslint-disable-line no-var, vars-on-top\n}\n\nfunction createSingleton(): PostgresJsDatabase {\n if (!process.env.DATABASE_URL) {\n throw new Error('DATABASE_URL is not defined');\n }\n return drizzle(postgres(process.env.DATABASE_URL, { prepare: false }));\n}\n\nexport const db = globalThis.szdb ?? createSingleton();\n\nif (!process.env.VERCEL_ENV) {\n globalThis.szdb = db;\n}\n","/* istanbul ignore file */\n\nimport { integer, pgEnum, pgSchema, text, timestamp } from 'drizzle-orm/pg-core';\n\nconst DEFAULT_ROLE = 10;\n\nexport const mfaType = pgEnum('mfaType', ['TOTP', 'HARDWARE']);\nexport const scope = pgEnum('scope', ['ANON', 'MFA', 'AUTHED']);\n\nexport type Scope = (typeof scope.enumValues)[number];\n\nexport const authSchema = pgSchema('auth');\n\nexport const authUserTable = authSchema.table('user_credentials', {\n id: text('id').primaryKey(),\n email: text('email').notNull().unique(),\n password: text('password'),\n role: integer('role').notNull().default(DEFAULT_ROLE),\n});\n\nexport type AuthUser = typeof authUserTable.$inferSelect;\n\nexport const authSessionTable = authSchema.table('sessions', {\n id: text('id').primaryKey(),\n userId: text('userId')\n .notNull()\n .references(() => authUserTable.id),\n scope: scope('scope').notNull().default('ANON'),\n expiresAt: timestamp('expiresAt').notNull(),\n});\n\nexport type AuthSession = typeof authSessionTable.$inferSelect;\n\nexport const authResetTable = authSchema.table('resets', {\n id: text('id').primaryKey(),\n userId: text('userId')\n .notNull()\n .references(() => authUserTable.id),\n expiresAt: timestamp('expiresAt').notNull(),\n});\n\nexport type AuthReset = typeof authResetTable.$inferSelect;\n\nexport const authMFATable = authSchema.table('mfas', {\n id: text('id').primaryKey(),\n name: text('name').notNull(),\n userId: text('userId')\n .notNull()\n .references(() => authUserTable.id),\n type: mfaType('type').notNull().default('TOTP'),\n secret: text('secret').notNull(),\n verifiedAt: timestamp('verifiedAt'),\n});\n\nexport type AuthMFA = typeof authMFATable.$inferSelect;\n\nexport const authClientTable = authSchema.table('client_credentials', {\n id: text('id').primaryKey(),\n alias: text('alias').notNull().unique(),\n secret: text('secret').notNull().unique(),\n});\n\nexport type AuthClient = typeof authClientTable.$inferSelect;\n","/* eslint-disable max-statements */\n\nimport type { SerializedError, SerializedErrorable } from '@sqrzro/interfaces';\nimport type Joi from 'joi';\nimport { NextResponse } from 'next/server';\n\nimport ValidationError from './ValidationError';\nimport { validateSchema } from './ValidationService';\n\nfunction serializeError(err: Error): SerializedError {\n return {\n cause: err.cause,\n message: err.message,\n name: err.name,\n stack: err.stack,\n };\n}\n\ninterface SubmitFormArgs<F extends object> {\n formData: F;\n onSuccess?: (model: F) => Promise<void> | void;\n onValidationError?: (error: ValidationError) => void;\n request?: Joi.ObjectSchema<F>;\n}\n\ninterface SubmitFormArgsWithFn<F extends object, M> extends Omit<SubmitFormArgs<F>, 'onSuccess'> {\n fn: (data: F) => Promise<M | null>;\n onSuccess?: (model: M) => Promise<void> | void;\n}\n\nfunction hasFn<F extends object, M>(\n args: SubmitFormArgs<F> | SubmitFormArgsWithFn<F, M>\n): args is SubmitFormArgsWithFn<F, M> {\n return Boolean(Object.prototype.hasOwnProperty.call(args, 'fn'));\n}\n\nexport async function submitForm<F extends object>(\n args: SubmitFormArgs<F>\n): Promise<SerializedErrorable<F>>;\nexport async function submitForm<F extends object, M>(\n args: SubmitFormArgsWithFn<F, M>\n): Promise<SerializedErrorable<M>>;\n\nexport async function submitForm<F extends object, M>(\n args: SubmitFormArgs<F> | SubmitFormArgsWithFn<F, M>\n): Promise<[F, null] | [M, null] | [null, SerializedError]> {\n let data = { ...args.formData };\n\n if (args.request) {\n const [validated, validationError] = await validateSchema<F>(args.formData, args.request);\n\n if (validationError !== null) {\n if (validationError instanceof ValidationError) {\n args.onValidationError?.(validationError);\n }\n\n return [null, serializeError(validationError)];\n }\n\n data = validated;\n }\n\n if (!hasFn(args)) {\n // try {\n await args.onSuccess?.(data);\n // } catch (err) {\n // if (err instanceof Error) {\n // return [null, serializeError(err)];\n // }\n\n // return [\n // null,\n // serializeError(\n // new Error('The submitForm onSuccess function encountered an unknown error')\n // ),\n // ];\n // }\n\n return [data, null];\n }\n\n let model: Awaited<M> | null = null;\n\n // try {\n model = await args.fn(data);\n // } catch (err: unknown) {\n // if (err instanceof ValidationError) {\n // args.onValidationError?.(err);\n // return [null, serializeError(err)];\n // }\n\n // if (err instanceof Error) {\n // return [null, serializeError(err)];\n // }\n\n // return [\n // null,\n // serializeError(\n // new Error('The function supplied to submitForm encountered an unknown error')\n // ),\n // ];\n // }\n\n if (!model) {\n throw new Error('NO_MODEL');\n // return [\n // null,\n // serializeError(\n // new Error('No model has been returned from the function supplied to submitForm')\n // ),\n // ];\n }\n\n // try {\n await args.onSuccess?.(model);\n // } catch (err) {\n // if (err instanceof Error) {\n // return [null, serializeError(err)];\n // }\n\n // return [\n // null,\n // serializeError(\n // new Error('The submitForm onSuccess function encountered an unknown error')\n // ),\n // ];\n // }\n\n return [model, null];\n}\n\nexport async function submitAPIRequest<F extends object, M>(\n args: SubmitFormArgsWithFn<F, M>\n): Promise<[M, null] | [null, NextResponse]> {\n const [response, error] = await submitForm<F, M>(args);\n\n if (error !== null) {\n if (error.name === 'ValidationError') {\n try {\n const errors = JSON.parse(error.message) as Record<string, string>;\n return [null, NextResponse.json(errors, { status: 422 })];\n } catch (err) {\n return [\n null,\n NextResponse.json({ message: 'An unknown error occured' }, { status: 500 }),\n ];\n }\n }\n return [null, NextResponse.json({ message: error.message }, { status: 500 })];\n }\n\n return [response, null];\n}\n","class ValidationError extends Error {\n public messages: Record<string, string>;\n\n public constructor(messages: Record<string, string>) {\n super(JSON.stringify(messages));\n\n this.messages = messages;\n this.name = 'ValidationError';\n }\n}\n\nexport default ValidationError;\n","import type { Errorable } from '@sqrzro/interfaces';\nimport Joi from 'joi';\n\nimport lang from './lang';\nimport ValidationError from './ValidationError';\n\nexport function validate(): typeof Joi {\n return Joi;\n}\n\nexport type ValidationCustomHelpers<V = any> = Joi.CustomHelpers<V>; // eslint-disable-line @typescript-eslint/no-explicit-any\nexport type ValidationExternalHelpers<V = any> = Joi.ExternalHelpers<V>; // eslint-disable-line @typescript-eslint/no-explicit-any\nexport type ValidationErrorReport = Joi.ErrorReport;\n\nexport function getDefaultErrorMessages(): Record<string, string> {\n return lang;\n}\n\nfunction getErrorMessages(): Record<string, string> {\n return Object.entries(lang).reduce((acc, [key, value]) => {\n if (!value) {\n return acc;\n }\n return {\n ...acc,\n [key]: value,\n };\n }, {});\n}\n\nfunction transformErrors(error: Joi.ValidationError): ValidationError {\n const messages = error.details.reduce(\n (acc, cur) => ({\n ...acc,\n [cur.path.join('.')]: cur.message.replace(/\"/gu, ''),\n }),\n {}\n );\n return new ValidationError(messages);\n}\n\n/**\n * This function takes FormData and a schema. It then attempts to transform the FormData into an\n * object that matches `T`. This is because the FormData object is not typed and we want to be able\n * to validate it properly typed.\n *\n * Once transformed, the object is validated against the schema. This will result in either a\n * properly typed `T` object or an array of validation errors.\n * @param formData\n * @param validation\n * @returns\n */\nexport async function validateSchema<T>(\n formData: Partial<T>,\n validation: Joi.ObjectSchema<T>,\n messages?: Record<string, string>\n): Promise<Errorable<T>> {\n try {\n const validated = await validation.validateAsync(formData, {\n abortEarly: false,\n messages: messages || getErrorMessages(),\n stripUnknown: true,\n });\n\n return [validated, null];\n } catch (err) {\n if (err instanceof Joi.ValidationError) {\n return [null, transformErrors(err)];\n }\n\n if (err instanceof Error) {\n return [null, err];\n }\n\n return [null, new Error('Unknown validation error occured')];\n }\n}\n\nexport function createSchema<T>(schema: Joi.SchemaMap<T, true>): Joi.ObjectSchema<T> {\n return Joi.object<T>(schema);\n}\n\nexport function extendSchema<T, U extends T>(\n schema: Joi.ObjectSchema<T>,\n appends: Joi.PartialSchemaMap<U>\n): Joi.ObjectSchema<U> {\n return schema.append<U>(appends);\n}\n","const messages: Record<string, string> = {\n 'alternatives.all': '',\n 'alternatives.any': '',\n 'alternatives.match': '',\n 'alternatives.one': '',\n 'alternatives.types': '',\n 'any.custom': '',\n 'any.default': '',\n 'any.failover': '',\n 'any.invalid': '',\n 'any.only': '',\n 'any.ref': '',\n 'any.required': '{{#label}} is required',\n 'any.unknown': '',\n 'array.base': '',\n 'array.excludes': '',\n 'array.includesRequiredBoth': '',\n 'array.includesRequiredKnowns': '',\n 'array.includesRequiredUnknowns': '',\n 'array.includes': '',\n 'array.length': '',\n 'array.max': '',\n 'array.min': '',\n 'array.orderedLength': '',\n 'array.sort': '',\n 'array.sort.mismatching': '',\n 'array.sort.unsupported': '',\n 'array.sparse': '',\n 'array.unique': '',\n 'array.hasKnown': '',\n 'array.hasUnknown': '',\n 'binary.base': '',\n 'binary.length': '',\n 'binary.max': '',\n 'binary.min': '',\n 'boolean.base': '',\n 'date.base': '',\n 'date.format': '',\n 'date.greater': '',\n 'date.less': '',\n 'date.max': '',\n 'date.min': '',\n 'date.strict': '',\n 'function.arity': '',\n 'function.class': '',\n 'function.maxArity': '',\n 'function.minArity': '',\n 'number.base': '{{#label}} should be a number',\n 'number.greater': '',\n 'number.infinity': '',\n 'number.integer': '',\n 'number.less': '',\n 'number.max': '',\n 'number.min': '{{#label}} should be greater than or equal to {{#limit}}',\n 'number.multiple': '',\n 'number.negative': '',\n 'number.port': '',\n 'number.positive': '',\n 'number.precision': '',\n 'number.unsafe': '',\n 'object.unknown': '',\n 'object.and': '',\n 'object.assert': '',\n 'object.base': '',\n 'object.length': '',\n 'object.max': '',\n 'object.min': '',\n 'object.missing': '',\n 'object.nand': '',\n 'object.pattern.match': '',\n 'object.refType': '',\n 'object.regex': '',\n 'object.rename.multiple': '',\n 'object.rename.override': '',\n 'object.schema': '',\n 'object.instance': '',\n 'object.with': '',\n 'object.without': '',\n 'object.xor': '',\n 'object.oxor': '',\n 'string.alphanum': '',\n 'string.base64': '',\n 'string.base': '',\n 'string.creditCard': '',\n 'string.dataUri': '',\n 'string.domain': '',\n 'string.email': '',\n 'string.empty': '{{#label}} is required',\n 'string.guid': '',\n 'string.hexAlign': '',\n 'string.hex': '',\n 'string.hostname': '',\n 'string.ipVersion': '',\n 'string.ip': '',\n 'string.isoDate': '',\n 'string.isoDuration': '',\n 'string.length': '',\n 'string.lowercase': '',\n 'string.max': '',\n 'string.min': '',\n 'string.normalize': '',\n 'string.pattern.base': '',\n 'string.pattern.name': '',\n 'string.pattern.invert.base': '',\n 'string.pattern.invert.name': '',\n 'string.token': '',\n 'string.trim': '',\n 'string.uppercase': '',\n 'string.uri': '',\n 'string.uriCustomScheme': '',\n 'string.uriRelativeOnly': '',\n 'symbol.base': '',\n 'symbol.map': '',\n};\n\nexport default messages;\n","import type { Errorable } from '@sqrzro/interfaces';\nimport { and, eq, isNotNull, isNull } from 'drizzle-orm';\nimport { authenticator } from 'otplib';\nimport qrcode from 'qrcode';\n\nimport { db } from '../database/DatabaseService';\nimport { authMFATable, authUserTable } from '../database/schema';\n\nimport { submitForm } from '../forms/FormService';\n\nimport MFARequest from './MFARequest';\nimport type { MFAFormFields, UserObject } from './interfaces';\nimport { createUserSession, generateID, getSessionUser } from './SessionService';\n\nexport function checkMFAEnabled(): boolean {\n return process.env.AUTH_MFA_ENABLED !== 'false';\n}\n\nexport async function generateMFA(name: string, email?: string): Promise<string | null> {\n if (!checkMFAEnabled() || !email) {\n return null;\n }\n\n const [user] = await db\n .select()\n .from(authUserTable)\n .where(eq(authUserTable.email, email))\n .limit(1);\n\n if (!user) {\n return null;\n }\n\n const secret = authenticator.generateSecret();\n const otpauth = authenticator.keyuri(email, name, secret);\n\n // Delete all the unverified MFA entries for this user\n\n await db\n .delete(authMFATable)\n .where(and(eq(authMFATable.userId, user.id), isNull(authMFATable.verifiedAt)));\n\n // Add the new MFA entry\n\n await db.insert(authMFATable).values({\n id: generateID(),\n name: 'Default',\n secret,\n userId: user.id,\n });\n\n return new Promise((resolve, reject) => {\n qrcode.toDataURL(\n otpauth,\n { rendererOpts: { quality: 1 }, margin: 0, scale: 2 },\n (err, data) => {\n if (err) {\n reject(err);\n }\n resolve(data);\n }\n );\n });\n}\n\nexport async function checkUserHasMFA(user: UserObject): Promise<boolean> {\n if (!checkMFAEnabled()) {\n return false;\n }\n\n const [mfa] = await db\n .select()\n .from(authMFATable)\n .where(and(eq(authMFATable.userId, user.id), isNotNull(authMFATable.verifiedAt)))\n .limit(1);\n\n return Boolean(mfa);\n}\n\nasync function markAsVerified(userID: string): Promise<void> {\n if (!checkMFAEnabled()) {\n return;\n }\n\n await db\n .update(authMFATable)\n .set({ verifiedAt: new Date() })\n .where(eq(authMFATable.userId, userID));\n}\n\nasync function validateUserToken(userID: string, token: string): Promise<boolean> {\n if (!checkMFAEnabled()) {\n return false;\n }\n\n const [mfa] = await db\n .select()\n .from(authMFATable)\n .where(eq(authMFATable.userId, userID))\n .limit(1);\n\n if (!mfa) {\n return false;\n }\n\n return authenticator.check(token, mfa.secret);\n}\n\nasync function handleMFA(formData: MFAFormFields): Promise<boolean> {\n if (!checkMFAEnabled()) {\n return false;\n }\n\n const user = await getSessionUser();\n\n if (!user) {\n return false;\n }\n\n const isValid = await validateUserToken(user.id, formData.token);\n\n if (!isValid) {\n return false;\n }\n\n await markAsVerified(user.id);\n return createUserSession(user.id, 'AUTHED');\n}\n\nexport async function handleMFAForm(formData: MFAFormFields): Promise<Errorable<boolean>> {\n if (!checkMFAEnabled()) {\n return [null, new Error('MFA is not enabled')];\n }\n\n const response = await submitForm<MFAFormFields, boolean>({\n fn: handleMFA,\n formData,\n request: MFARequest,\n });\n\n return response;\n}\n","/* eslint-disable @typescript-eslint/no-magic-numbers */\n\nimport Joi from 'joi';\n\nimport { createSchema } from '../forms/ValidationService';\nimport type { MFAFormFields } from './interfaces';\n\nconst MFARequest = createSchema<MFAFormFields>({\n token: Joi.string()\n .pattern(/^[0-9]{6}$/u)\n .required(),\n});\n\nexport default MFARequest;\n","import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle';\nimport { Lucia, generateId } from 'lucia';\nimport { cookies } from 'next/headers';\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\nimport { match } from 'path-to-regexp';\n\nimport { getAllowedRoles } from './AuthService';\n\nimport { db } from '../database/DatabaseService';\nimport { authSessionTable, authUserTable } from '../database/schema';\nimport type { Scope } from '../database/schema';\n\nimport { getFromCache, setToCache } from '../cache/CacheService';\nimport { getOrigin } from '../url/URLService';\nimport { getFromObject } from '@sqrzro/utility';\n\nconst DEFAULT_REDIRECT = '/auth/login';\nconst ID_LENGTH = 16;\n\ninterface ScopeData {\n allowedRoute?: string;\n redirectOnAuth?: string;\n redirectOnUnauth?: string;\n}\n\nexport type ScopeObject = Record<Scope, ScopeData>;\n\ninterface DatabaseSessionAttributes {\n scope: Scope;\n}\n\ninterface DatabaseUserAttributes {\n email: string;\n role: number;\n}\n\ndeclare module 'lucia' {\n interface Register {\n Lucia: typeof lucia;\n DatabaseSessionAttributes: DatabaseSessionAttributes;\n DatabaseUserAttributes: DatabaseUserAttributes;\n }\n}\n\nconst DEFAULT_SCOPES: ScopeObject = {\n ANON: {\n allowedRoute: '/auth/(login|password)',\n redirectOnUnauth: DEFAULT_REDIRECT,\n },\n MFA: {\n allowedRoute: '/auth/mfa',\n redirectOnUnauth: '/auth/mfa',\n },\n AUTHED: {\n allowedRoute: '*',\n redirectOnAuth: '/',\n },\n};\n\nconst adapter = new DrizzlePostgreSQLAdapter(db, authSessionTable, authUserTable);\n\nexport const lucia = new Lucia(adapter, {\n sessionCookie: {\n attributes: {\n secure: process.env.NODE_ENV === 'production',\n },\n name: process.env.AUTH_COOKIE_NAME || 'auth_session',\n },\n getSessionAttributes: (attributes: DatabaseSessionAttributes): DatabaseSessionAttributes => ({\n scope: attributes.scope,\n }),\n getUserAttributes: (attributes: DatabaseUserAttributes): DatabaseUserAttributes => ({\n email: attributes.email,\n role: attributes.role,\n }),\n});\n\nexport function generateID<T extends string = string>(prefix = '', length = ID_LENGTH): T {\n return `${prefix ? `${prefix}_` : ''}${generateId(length)}` as T;\n}\n\nexport async function invalidateSession(id: string): Promise<void> {\n const cookie = lucia.createBlankSessionCookie();\n cookies().set(cookie.name, cookie.value, cookie.attributes);\n\n return lucia.invalidateSession(id);\n}\n\nexport async function invalidateUserSessions(id: string): Promise<void> {\n return lucia.invalidateUserSessions(id);\n}\n\nexport async function createUserSession(id: string, scope: Scope = 'ANON'): Promise<boolean> {\n const session = await lucia.createSession(id, { scope });\n const sessionCookie = lucia.createSessionCookie(session.id);\n cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);\n\n return true;\n}\n\nexport function getSessionID(): string | null {\n return cookies().get(lucia.sessionCookieName)?.value ?? null;\n}\n\nexport function checkSessionExists(): boolean {\n return Boolean(getSessionID());\n}\n\nfunction checkUserRole(userRole: number, targetRole?: number): boolean {\n if (targetRole) {\n return userRole === targetRole;\n }\n return getAllowedRoles().includes(userRole);\n}\n\nexport async function getSessionUser(role?: number): Promise<{\n id: string;\n email: string;\n role: number;\n} | null> {\n const sessionID = getSessionID();\n\n if (!sessionID) {\n return null;\n }\n\n const { user } = await lucia.validateSession(sessionID);\n\n if (!user || !checkUserRole(user.role, role)) {\n return null;\n }\n\n return getFromObject(user, ['id', 'email', 'role']);\n}\n\nexport function checkRouteAllowed(pathname: string, route?: string): boolean {\n if (!route) {\n return false;\n }\n\n if (route === '*') {\n return true;\n }\n\n return match(route)(pathname) !== false;\n}\n\nexport async function getScopes(): Promise<ScopeObject> {\n const scopes = await getFromCache(`${getOrigin()}:scopes`);\n return scopes ? (JSON.parse(scopes) as ScopeObject) : DEFAULT_SCOPES;\n}\n\nexport async function getScopeByID(id: Scope): Promise<ScopeData> {\n const scopes = await getScopes();\n return scopes[id];\n}\n\nexport async function setScopes(customScopes?: Partial<ScopeObject>): Promise<void> {\n const scopes = {\n ANON: {\n ...DEFAULT_SCOPES.ANON,\n ...customScopes?.ANON,\n },\n MFA: {\n ...DEFAULT_SCOPES.MFA,\n ...customScopes?.MFA,\n },\n AUTHED: {\n ...DEFAULT_SCOPES.AUTHED,\n ...customScopes?.AUTHED,\n },\n };\n return setToCache(`${getOrigin()}:scopes`, JSON.stringify(scopes));\n}\n\nasync function validateSessionFromID(sessionID: string, pathname: string): Promise<string | null> {\n const scopes = await getScopes();\n const { session, user } = await lucia.validateSession(sessionID);\n\n const scope =\n scopes[session && getAllowedRoles().includes(user?.role) ? session.scope : 'ANON'];\n\n return checkRouteAllowed(pathname, scope.allowedRoute)\n ? null\n : scope.redirectOnUnauth || DEFAULT_REDIRECT;\n}\n\nexport async function handleSession(\n request: NextRequest,\n customScopes?: Partial<ScopeObject>\n): Promise<NextResponse> {\n const sessionID = request.nextUrl.searchParams.get('id') || '';\n const pathname = request.nextUrl.searchParams.get('pathname') || '/';\n\n await setScopes(customScopes);\n\n return NextResponse.json({\n redirect: await validateSessionFromID(sessionID, pathname),\n });\n}\n","import { createClient } from 'redis';\n\nlet client: ReturnType<typeof createClient> | null = null;\n\nfunction isLocalhost(url: string): boolean {\n return url.includes('localhost') || url.includes('127.0.0.1');\n}\n\nasync function getClient(): Promise<ReturnType<typeof createClient>> {\n if (!process.env.REDIS_URL) {\n throw new Error('REDIS_URL is not defined. Access to the cache is not possible.');\n }\n\n if (client) {\n return client;\n }\n\n client = createClient({\n socket: {\n tls: !isLocalhost(process.env.REDIS_URL),\n },\n url: process.env.REDIS_URL,\n });\n\n await client.connect();\n return client;\n}\n\nexport async function getFromCache(key: string): Promise<string | null> {\n return (await getClient()).get(key);\n}\n\nexport async function setToCache(key: string, value: string): Promise<void> {\n await (await getClient()).set(key, value);\n}\n","import { headers } from 'next/headers';\n\n/**\n * Uses a number of methods to determine the current origin.\n *\n * First, it checks if an `x-origin` header. This will have been set by the `handleMiddleware`\n * function, further up the chain. For more information on how it is being set, see the\n * `middleware` documentation.\n *\n * There are some situations where middleware is not being applied, such as API routes (because the\n * redirection is not necessary, and API routes handle their own authentication). In these cases,\n * this function tries to piece together the origin from the `x-forwarded-proto` and\n * `x-forwarded-host` headers.\n *\n * Finally, if the origin cannot be determined, an error is thrown.\n *\n * @returns The origin of the current request.\n */\nexport function getOrigin(): string {\n const origin = headers().get('x-origin');\n\n if (origin) {\n return origin;\n }\n\n const proto = headers().get('x-forwarded-proto');\n const host = headers().get('x-forwarded-host');\n\n if (proto && host) {\n return `${proto}://${host}`;\n }\n\n throw new Error('No origin could be determined');\n}\n\nexport function getPathname(): string {\n const pathname = headers().get('x-pathname');\n\n if (pathname) {\n return pathname;\n }\n\n throw new Error(\n 'No pathname could be determined. Please make sure middleware is being applied to the current request.'\n );\n}\n\n/**\n * Builds a URL from the current origin and a given pathname. For more information on how the origin\n * is determined, see the `getOrigin` function. This function then just concatenates the origin and\n * the pathname, and performs some cleanup to ensure the URL is valid.\n *\n * @param pathname\n * @returns The URL, with the origin and pathname combined.\n */\nexport function makeURL(pathname?: string): string {\n const origin = getOrigin();\n\n if (!pathname) {\n return origin;\n }\n\n const isSecure = origin.startsWith('https://');\n\n const protocol = isSecure ? 'https://' : 'http://';\n const originWithoutProtocol = origin.replace(/^https?:\\/\\//u, '');\n\n const cleanURL = `${originWithoutProtocol}/${pathname}`.replace(/\\/+/gu, '/');\n return `${protocol}${cleanURL}`;\n}\n\n/**\n * @deprecated Use `makeURL` instead.\n */\nexport function getURL(pathname?: string): string {\n return makeURL(pathname);\n}\n","import bcrypt from 'bcryptjs';\n\nconst PW_SALT_ROUNDS = 12;\n\ntype PasswordRule = 'lower' | 'min' | 'number' | 'symbol' | 'upper';\n\nexport type PasswordRuleObject = Partial<Record<PasswordRule, number>>;\ntype PasswordValidityObject = Record<PasswordRule, boolean>;\n\nconst PASSWORD_RULES: PasswordRuleObject = {\n min: 8,\n upper: 1,\n lower: 1,\n number: 1,\n symbol: 1,\n};\n\nfunction checkPasswordMin(password: string, value: number): boolean {\n return password.length >= value;\n}\n\nfunction checkPasswordUpper(password: string, value: number): boolean {\n return password.replace(/[^A-Z]/gu, '').length >= value;\n}\n\nfunction checkPasswordLower(password: string, value: number): boolean {\n return password.replace(/[^a-z]/gu, '').length >= value;\n}\n\nfunction checkPasswordNumber(password: string, value: number): boolean {\n return password.replace(/[^0-9]/gu, '').length >= value;\n}\n\nfunction checkPasswordSymbol(password: string, value: number): boolean {\n return password.replace(/[^$]/gu, '').length >= value;\n}\n\nconst PASSWORD_FUNCTIONS: Record<PasswordRule, (password: string, value: number) => boolean> = {\n min: checkPasswordMin,\n upper: checkPasswordUpper,\n lower: checkPasswordLower,\n number: checkPasswordNumber,\n symbol: checkPasswordSymbol,\n};\n\nexport async function hashPassword(password: string): Promise<string> {\n const hash = await bcrypt.hash(password, PW_SALT_ROUNDS);\n return hash;\n}\n\nexport async function verifyPassword(data?: string, encrypted?: string): Promise<boolean> {\n if (!data || !encrypted) {\n return false;\n }\n\n const verified = await bcrypt.compare(data, encrypted);\n return verified;\n}\n\nexport async function getPasswordComplexity(\n password: string,\n rules: Partial<PasswordRuleObject> = PASSWORD_RULES\n): Promise<Partial<PasswordValidityObject>> {\n const entries = Object.entries(rules) as [PasswordRule, number][];\n\n const validity = entries.reduce<Partial<PasswordValidityObject>>((acc, [rule, value]) => {\n acc[rule] = PASSWORD_FUNCTIONS[rule](password, value);\n return acc;\n }, {});\n\n return Promise.resolve(validity);\n}\n\nexport async function checkPasswordComplexity(\n password: string,\n rules: Partial<PasswordRuleObject> = PASSWORD_RULES\n): Promise<Partial<boolean>> {\n const validity = await getPasswordComplexity(password, rules);\n return Promise.resolve(Object.values(validity).every(Boolean));\n}\n","/* eslint-disable @typescript-eslint/no-magic-numbers */\n\nimport Joi from 'joi';\n\nimport { createSchema } from '../forms/ValidationService';\nimport type { LoginFormFields } from './interfaces';\n\nconst LoginRequest = createSchema<LoginFormFields>({\n email: Joi.string().email({ minDomainSegments: 2, tlds: false }).required(),\n password: Joi.string().min(8).required(),\n});\n\nexport default LoginRequest;\n","/* eslint-disable @typescript-eslint/no-magic-numbers */\n\nimport Joi from 'joi';\n\nimport { createSchema } from '../forms/ValidationService';\nimport type { PasswordFormFields } from './interfaces';\n\nconst PasswordRequest = createSchema<PasswordFormFields>({\n email: Joi.string().max(60).email({ minDomainSegments: 2, tlds: false }).required().messages({\n 'any.required': 'Please provide your email address, so we can send you a reset link',\n 'string.empty': 'Please provide your email address, so we can send you a reset link',\n 'string.email': 'Please make sure your email address is valid',\n 'string.max': 'Please make sure your email address is valid',\n }),\n});\n\nexport default PasswordRequest;\n","/* eslint-disable @typescript-eslint/no-magic-numbers */\n\nimport Joi from 'joi';\n\nimport { createSchema } from '../forms/ValidationService';\nimport type { PasswordResetFormFields } from './interfaces';\n\nconst PasswordResetRequest = createSchema<PasswordResetFormFields>({\n token: Joi.string()\n .pattern(/[a-z0-9]{40}/u)\n .required(),\n password: Joi.string().required().messages({\n 'any.required': 'Please provide your new password',\n 'string.empty': 'Please provide your new password',\n }),\n});\n\nexport default PasswordResetRequest;\n","import type { NextRequest } from 'next/server';\nimport { eq } from 'drizzle-orm';\n\nimport { db } from '../database/DatabaseService';\nimport { authClientTable } from '../database/schema';\nimport type { AuthClient } from '../database/schema';\n\nimport { hashPassword, verifyPassword } from './PasswordService';\nimport { generateID } from './SessionService';\n\nconst ID_LENGTH = 16;\nconst SECRET_LENGTH = 64;\n\nexport async function getClientByID(id: string): Promise<AuthClient | null> {\n const [client] = await db\n .select()\n .from(authClientTable)\n .where(eq(authClientTable.id, id))\n .limit(1);\n\n return client;\n}\n\ninterface RegisterClientArgs {\n alias: string;\n id?: string;\n secret?: string;\n}\n\nexport async function registerClient({\n alias,\n id,\n secret,\n}: RegisterClientArgs): Promise<AuthClient | null> {\n const [client] = await db\n .insert(authClientTable)\n .values({\n alias,\n id: id || generateID('', ID_LENGTH),\n secret: await hashPassword(secret || generateID('', SECRET_LENGTH)),\n })\n .returning();\n\n return client;\n}\n\nexport async function handleClientAuth(request: NextRequest): Promise<AuthClient | null> {\n const { headers } = request;\n const header = headers.get('authorization');\n\n if (!header) {\n return null;\n }\n\n const auth = Buffer.from(header.replace('Basic ', ''), 'base64')\n .toString('utf-8')\n .replace(/:$/u, '');\n\n const [id, ...secret] = auth.split('-');\n\n const [client] = await db\n .select()\n .from(authClientTable)\n .where(eq(authClientTable.id, id))\n .limit(1);\n\n if (!client) {\n return null;\n }\n\n const isVerified = await verifyPassword(secret.join(''), client.secret);\n return isVerified ? client : null;\n}\n"]}
@@ -0,0 +1,100 @@
1
+ import { Errorable } from '@sqrzro/interfaces';
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+ import { AuthClient, Scope } from '../database/schema.cjs';
4
+ import { Lucia } from 'lucia';
5
+ import 'drizzle-orm/pg-core';
6
+
7
+ interface LoginFormFields {
8
+ email: string;
9
+ password: string;
10
+ }
11
+ interface MFAFormFields {
12
+ token: string;
13
+ }
14
+ interface PasswordFormFields {
15
+ email: string;
16
+ }
17
+ interface PasswordResetFormFields {
18
+ password: string;
19
+ token: string;
20
+ }
21
+ interface UserObject {
22
+ id: string;
23
+ email: string;
24
+ password?: string | null;
25
+ role?: number;
26
+ }
27
+
28
+ declare function handleLogout(): Promise<void>;
29
+ declare function getAllowedRoles(): number[];
30
+ declare function handleLoginForm(formData: LoginFormFields, onSuccess?: () => Promise<void>): Promise<Errorable<string>>;
31
+ interface RegisterUserArgs {
32
+ email: string;
33
+ password?: string;
34
+ role?: number;
35
+ }
36
+ declare function registerUser({ email, password, role, }: RegisterUserArgs): Promise<UserObject | null>;
37
+ declare function handlePasswordForm(formData: PasswordFormFields, mailFn: (email: string, token: string) => Promise<boolean>): Promise<Errorable<boolean>>;
38
+ declare function handlePasswordResetForm(formData: PasswordResetFormFields): Promise<Errorable<string | null>>;
39
+
40
+ declare function getClientByID(id: string): Promise<AuthClient | null>;
41
+ interface RegisterClientArgs {
42
+ alias: string;
43
+ id?: string;
44
+ secret?: string;
45
+ }
46
+ declare function registerClient({ alias, id, secret, }: RegisterClientArgs): Promise<AuthClient | null>;
47
+ declare function handleClientAuth(request: NextRequest): Promise<AuthClient | null>;
48
+
49
+ declare function checkMFAEnabled(): boolean;
50
+ declare function generateMFA(name: string, email?: string): Promise<string | null>;
51
+ declare function checkUserHasMFA(user: UserObject): Promise<boolean>;
52
+ declare function handleMFAForm(formData: MFAFormFields): Promise<Errorable<boolean>>;
53
+
54
+ interface ScopeData {
55
+ allowedRoute?: string;
56
+ redirectOnAuth?: string;
57
+ redirectOnUnauth?: string;
58
+ }
59
+ type ScopeObject = Record<Scope, ScopeData>;
60
+ interface DatabaseSessionAttributes {
61
+ scope: Scope;
62
+ }
63
+ interface DatabaseUserAttributes {
64
+ email: string;
65
+ role: number;
66
+ }
67
+ declare module 'lucia' {
68
+ interface Register {
69
+ Lucia: typeof lucia;
70
+ DatabaseSessionAttributes: DatabaseSessionAttributes;
71
+ DatabaseUserAttributes: DatabaseUserAttributes;
72
+ }
73
+ }
74
+ declare const lucia: Lucia<DatabaseSessionAttributes, DatabaseUserAttributes>;
75
+ declare function generateID<T extends string = string>(prefix?: string, length?: number): T;
76
+ declare function invalidateSession(id: string): Promise<void>;
77
+ declare function invalidateUserSessions(id: string): Promise<void>;
78
+ declare function createUserSession(id: string, scope?: Scope): Promise<boolean>;
79
+ declare function getSessionID(): string | null;
80
+ declare function checkSessionExists(): boolean;
81
+ declare function getSessionUser(role?: number): Promise<{
82
+ id: string;
83
+ email: string;
84
+ role: number;
85
+ } | null>;
86
+ declare function checkRouteAllowed(pathname: string, route?: string): boolean;
87
+ declare function getScopes(): Promise<ScopeObject>;
88
+ declare function getScopeByID(id: Scope): Promise<ScopeData>;
89
+ declare function setScopes(customScopes?: Partial<ScopeObject>): Promise<void>;
90
+ declare function handleSession(request: NextRequest, customScopes?: Partial<ScopeObject>): Promise<NextResponse>;
91
+
92
+ type PasswordRule = 'lower' | 'min' | 'number' | 'symbol' | 'upper';
93
+ type PasswordRuleObject = Partial<Record<PasswordRule, number>>;
94
+ type PasswordValidityObject = Record<PasswordRule, boolean>;
95
+ declare function hashPassword(password: string): Promise<string>;
96
+ declare function verifyPassword(data?: string, encrypted?: string): Promise<boolean>;
97
+ declare function getPasswordComplexity(password: string, rules?: Partial<PasswordRuleObject>): Promise<Partial<PasswordValidityObject>>;
98
+ declare function checkPasswordComplexity(password: string, rules?: Partial<PasswordRuleObject>): Promise<Partial<boolean>>;
99
+
100
+ export { type LoginFormFields, type MFAFormFields, type PasswordFormFields, type PasswordResetFormFields, type PasswordRuleObject, type ScopeObject, type UserObject, checkMFAEnabled, checkPasswordComplexity, checkRouteAllowed, checkSessionExists, checkUserHasMFA, createUserSession, generateID, generateMFA, getAllowedRoles, getClientByID, getPasswordComplexity, getScopeByID, getScopes, getSessionID, getSessionUser, handleClientAuth, handleLoginForm, handleLogout, handleMFAForm, handlePasswordForm, handlePasswordResetForm, handleSession, hashPassword, invalidateSession, invalidateUserSessions, lucia, registerClient, registerUser, setScopes, verifyPassword };
@@ -0,0 +1,100 @@
1
+ import { Errorable } from '@sqrzro/interfaces';
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+ import { AuthClient, Scope } from '../database/schema.js';
4
+ import { Lucia } from 'lucia';
5
+ import 'drizzle-orm/pg-core';
6
+
7
+ interface LoginFormFields {
8
+ email: string;
9
+ password: string;
10
+ }
11
+ interface MFAFormFields {
12
+ token: string;
13
+ }
14
+ interface PasswordFormFields {
15
+ email: string;
16
+ }
17
+ interface PasswordResetFormFields {
18
+ password: string;
19
+ token: string;
20
+ }
21
+ interface UserObject {
22
+ id: string;
23
+ email: string;
24
+ password?: string | null;
25
+ role?: number;
26
+ }
27
+
28
+ declare function handleLogout(): Promise<void>;
29
+ declare function getAllowedRoles(): number[];
30
+ declare function handleLoginForm(formData: LoginFormFields, onSuccess?: () => Promise<void>): Promise<Errorable<string>>;
31
+ interface RegisterUserArgs {
32
+ email: string;
33
+ password?: string;
34
+ role?: number;
35
+ }
36
+ declare function registerUser({ email, password, role, }: RegisterUserArgs): Promise<UserObject | null>;
37
+ declare function handlePasswordForm(formData: PasswordFormFields, mailFn: (email: string, token: string) => Promise<boolean>): Promise<Errorable<boolean>>;
38
+ declare function handlePasswordResetForm(formData: PasswordResetFormFields): Promise<Errorable<string | null>>;
39
+
40
+ declare function getClientByID(id: string): Promise<AuthClient | null>;
41
+ interface RegisterClientArgs {
42
+ alias: string;
43
+ id?: string;
44
+ secret?: string;
45
+ }
46
+ declare function registerClient({ alias, id, secret, }: RegisterClientArgs): Promise<AuthClient | null>;
47
+ declare function handleClientAuth(request: NextRequest): Promise<AuthClient | null>;
48
+
49
+ declare function checkMFAEnabled(): boolean;
50
+ declare function generateMFA(name: string, email?: string): Promise<string | null>;
51
+ declare function checkUserHasMFA(user: UserObject): Promise<boolean>;
52
+ declare function handleMFAForm(formData: MFAFormFields): Promise<Errorable<boolean>>;
53
+
54
+ interface ScopeData {
55
+ allowedRoute?: string;
56
+ redirectOnAuth?: string;
57
+ redirectOnUnauth?: string;
58
+ }
59
+ type ScopeObject = Record<Scope, ScopeData>;
60
+ interface DatabaseSessionAttributes {
61
+ scope: Scope;
62
+ }
63
+ interface DatabaseUserAttributes {
64
+ email: string;
65
+ role: number;
66
+ }
67
+ declare module 'lucia' {
68
+ interface Register {
69
+ Lucia: typeof lucia;
70
+ DatabaseSessionAttributes: DatabaseSessionAttributes;
71
+ DatabaseUserAttributes: DatabaseUserAttributes;
72
+ }
73
+ }
74
+ declare const lucia: Lucia<DatabaseSessionAttributes, DatabaseUserAttributes>;
75
+ declare function generateID<T extends string = string>(prefix?: string, length?: number): T;
76
+ declare function invalidateSession(id: string): Promise<void>;
77
+ declare function invalidateUserSessions(id: string): Promise<void>;
78
+ declare function createUserSession(id: string, scope?: Scope): Promise<boolean>;
79
+ declare function getSessionID(): string | null;
80
+ declare function checkSessionExists(): boolean;
81
+ declare function getSessionUser(role?: number): Promise<{
82
+ id: string;
83
+ email: string;
84
+ role: number;
85
+ } | null>;
86
+ declare function checkRouteAllowed(pathname: string, route?: string): boolean;
87
+ declare function getScopes(): Promise<ScopeObject>;
88
+ declare function getScopeByID(id: Scope): Promise<ScopeData>;
89
+ declare function setScopes(customScopes?: Partial<ScopeObject>): Promise<void>;
90
+ declare function handleSession(request: NextRequest, customScopes?: Partial<ScopeObject>): Promise<NextResponse>;
91
+
92
+ type PasswordRule = 'lower' | 'min' | 'number' | 'symbol' | 'upper';
93
+ type PasswordRuleObject = Partial<Record<PasswordRule, number>>;
94
+ type PasswordValidityObject = Record<PasswordRule, boolean>;
95
+ declare function hashPassword(password: string): Promise<string>;
96
+ declare function verifyPassword(data?: string, encrypted?: string): Promise<boolean>;
97
+ declare function getPasswordComplexity(password: string, rules?: Partial<PasswordRuleObject>): Promise<Partial<PasswordValidityObject>>;
98
+ declare function checkPasswordComplexity(password: string, rules?: Partial<PasswordRuleObject>): Promise<Partial<boolean>>;
99
+
100
+ export { type LoginFormFields, type MFAFormFields, type PasswordFormFields, type PasswordResetFormFields, type PasswordRuleObject, type ScopeObject, type UserObject, checkMFAEnabled, checkPasswordComplexity, checkRouteAllowed, checkSessionExists, checkUserHasMFA, createUserSession, generateID, generateMFA, getAllowedRoles, getClientByID, getPasswordComplexity, getScopeByID, getScopes, getSessionID, getSessionUser, handleClientAuth, handleLoginForm, handleLogout, handleMFAForm, handlePasswordForm, handlePasswordResetForm, handleSession, hashPassword, invalidateSession, invalidateUserSessions, lucia, registerClient, registerUser, setScopes, verifyPassword };
@@ -0,0 +1,21 @@
1
+ import { eq, and, isNull, isNotNull, inArray } from 'drizzle-orm';
2
+ import { drizzle } from 'drizzle-orm/postgres-js';
3
+ import de from 'postgres';
4
+ import { pgEnum, pgSchema, text, integer, timestamp } from 'drizzle-orm/pg-core';
5
+ import { NextResponse } from 'next/server';
6
+ import C from 'joi';
7
+ import { authenticator } from 'otplib';
8
+ import ze from 'qrcode';
9
+ import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle';
10
+ import { Lucia, generateId } from 'lucia';
11
+ import { cookies, headers } from 'next/headers';
12
+ import { match } from 'path-to-regexp';
13
+ import { createClient } from 'redis';
14
+ import { getFromObject } from '@sqrzro/utility';
15
+ import Y from 'bcryptjs';
16
+
17
+ function pe(){if(!process.env.DATABASE_URL)throw new Error("DATABASE_URL is not defined");return drizzle(de(process.env.DATABASE_URL,{prepare:!1}))}var o=globalThis.szdb??pe();process.env.VERCEL_ENV||(globalThis.szdb=o);var be=10,he=pgEnum("mfaType",["TOTP","HARDWARE"]),we=pgEnum("scope",["ANON","MFA","AUTHED"]),y=pgSchema("auth"),i=y.table("user_credentials",{id:text("id").primaryKey(),email:text("email").notNull().unique(),password:text("password"),role:integer("role").notNull().default(be)}),L=y.table("sessions",{id:text("id").primaryKey(),userId:text("userId").notNull().references(()=>i.id),scope:we("scope").notNull().default("ANON"),expiresAt:timestamp("expiresAt").notNull()}),c=y.table("resets",{id:text("id").primaryKey(),userId:text("userId").notNull().references(()=>i.id),expiresAt:timestamp("expiresAt").notNull()}),u=y.table("mfas",{id:text("id").primaryKey(),name:text("name").notNull(),userId:text("userId").notNull().references(()=>i.id),type:he("type").notNull().default("TOTP"),secret:text("secret").notNull(),verifiedAt:timestamp("verifiedAt")}),h=y.table("client_credentials",{id:text("id").primaryKey(),alias:text("alias").notNull().unique(),secret:text("secret").notNull().unique()});var O=class extends Error{messages;constructor(r){super(JSON.stringify(r)),this.messages=r,this.name="ValidationError";}},b=O;var ye={"alternatives.all":"","alternatives.any":"","alternatives.match":"","alternatives.one":"","alternatives.types":"","any.custom":"","any.default":"","any.failover":"","any.invalid":"","any.only":"","any.ref":"","any.required":"{{#label}} is required","any.unknown":"","array.base":"","array.excludes":"","array.includesRequiredBoth":"","array.includesRequiredKnowns":"","array.includesRequiredUnknowns":"","array.includes":"","array.length":"","array.max":"","array.min":"","array.orderedLength":"","array.sort":"","array.sort.mismatching":"","array.sort.unsupported":"","array.sparse":"","array.unique":"","array.hasKnown":"","array.hasUnknown":"","binary.base":"","binary.length":"","binary.max":"","binary.min":"","boolean.base":"","date.base":"","date.format":"","date.greater":"","date.less":"","date.max":"","date.min":"","date.strict":"","function.arity":"","function.class":"","function.maxArity":"","function.minArity":"","number.base":"{{#label}} should be a number","number.greater":"","number.infinity":"","number.integer":"","number.less":"","number.max":"","number.min":"{{#label}} should be greater than or equal to {{#limit}}","number.multiple":"","number.negative":"","number.port":"","number.positive":"","number.precision":"","number.unsafe":"","object.unknown":"","object.and":"","object.assert":"","object.base":"","object.length":"","object.max":"","object.min":"","object.missing":"","object.nand":"","object.pattern.match":"","object.refType":"","object.regex":"","object.rename.multiple":"","object.rename.override":"","object.schema":"","object.instance":"","object.with":"","object.without":"","object.xor":"","object.oxor":"","string.alphanum":"","string.base64":"","string.base":"","string.creditCard":"","string.dataUri":"","string.domain":"","string.email":"","string.empty":"{{#label}} is required","string.guid":"","string.hexAlign":"","string.hex":"","string.hostname":"","string.ipVersion":"","string.ip":"","string.isoDate":"","string.isoDuration":"","string.length":"","string.lowercase":"","string.max":"","string.min":"","string.normalize":"","string.pattern.base":"","string.pattern.name":"","string.pattern.invert.base":"","string.pattern.invert.name":"","string.token":"","string.trim":"","string.uppercase":"","string.uri":"","string.uriCustomScheme":"","string.uriRelativeOnly":"","symbol.base":"","symbol.map":""},I=ye;function Ae(){return Object.entries(I).reduce((e,[r,t])=>t?{...e,[r]:t}:e,{})}function Fe(e){let r=e.details.reduce((t,s)=>({...t,[s.path.join(".")]:s.message.replace(/"/gu,"")}),{});return new b(r)}async function _(e,r,t){try{return [await r.validateAsync(e,{abortEarly:!1,messages:t||Ae(),stripUnknown:!0}),null]}catch(s){return s instanceof C.ValidationError?[null,Fe(s)]:s instanceof Error?[null,s]:[null,new Error("Unknown validation error occured")]}}function m(e){return C.object(e)}function xe(e){return {cause:e.cause,message:e.message,name:e.name,stack:e.stack}}function Se(e){return !!Object.prototype.hasOwnProperty.call(e,"fn")}async function w(e){let r={...e.formData};if(e.request){let[s,n]=await _(e.formData,e.request);if(n!==null)return n instanceof b&&e.onValidationError?.(n),[null,xe(n)];r=s;}if(!Se(e))return await e.onSuccess?.(r),[r,null];let t=null;if(t=await e.fn(r),!t)throw new Error("NO_MODEL");return await e.onSuccess?.(t),[t,null]}var Re=m({token:C.string().pattern(/^[0-9]{6}$/u).required()}),V=Re;var A=null;function ve(e){return e.includes("localhost")||e.includes("127.0.0.1")}async function z(){if(!process.env.REDIS_URL)throw new Error("REDIS_URL is not defined. Access to the cache is not possible.");return A||(A=createClient({socket:{tls:!ve(process.env.REDIS_URL)},url:process.env.REDIS_URL}),await A.connect(),A)}async function J(e){return (await z()).get(e)}async function H(e,r){await(await z()).set(e,r);}function j(){let e=headers().get("x-origin");if(e)return e;let r=headers().get("x-forwarded-proto"),t=headers().get("x-forwarded-host");if(r&&t)return `${r}://${t}`;throw new Error("No origin could be determined")}var $="/auth/login",Me=16,R={ANON:{allowedRoute:"/auth/(login|password)",redirectOnUnauth:$},MFA:{allowedRoute:"/auth/mfa",redirectOnUnauth:"/auth/mfa"},AUTHED:{allowedRoute:"*",redirectOnAuth:"/"}},ke=new DrizzlePostgreSQLAdapter(o,L,i),d=new Lucia(ke,{sessionCookie:{attributes:{secure:process.env.NODE_ENV==="production"},name:process.env.AUTH_COOKIE_NAME||"auth_session"},getSessionAttributes:e=>({scope:e.scope}),getUserAttributes:e=>({email:e.email,role:e.role})});function p(e="",r=Me){return `${e?`${e}_`:""}${generateId(r)}`}async function B(e){let r=d.createBlankSessionCookie();return cookies().set(r.name,r.value,r.attributes),d.invalidateSession(e)}async function W(e){return d.invalidateUserSessions(e)}async function E(e,r="ANON"){let t=await d.createSession(e,{scope:r}),s=d.createSessionCookie(t.id);return cookies().set(s.name,s.value,s.attributes),!0}function v(){return cookies().get(d.sessionCookieName)?.value??null}function $r(){return !!v()}function qe(e,r){return r?e===r:F().includes(e)}async function K(e){let r=v();if(!r)return null;let{user:t}=await d.validateSession(r);return !t||!qe(t.role,e)?null:getFromObject(t,["id","email","role"])}function Le(e,r){return r?r==="*"?!0:match(r)(e)!==!1:!1}async function G(){let e=await J(`${j()}:scopes`);return e?JSON.parse(e):R}async function Q(e){return (await G())[e]}async function Ie(e){let r={ANON:{...R.ANON,...e?.ANON},MFA:{...R.MFA,...e?.MFA},AUTHED:{...R.AUTHED,...e?.AUTHED}};return H(`${j()}:scopes`,JSON.stringify(r))}async function Ce(e,r){let t=await G(),{session:s,user:n}=await d.validateSession(e),l=t[s&&F().includes(n?.role)?s.scope:"ANON"];return Le(r,l.allowedRoute)?null:l.redirectOnUnauth||$}async function Br(e,r){let t=e.nextUrl.searchParams.get("id")||"",s=e.nextUrl.searchParams.get("pathname")||"/";return await Ie(r),NextResponse.json({redirect:await Ce(t,s)})}function f(){return process.env.AUTH_MFA_ENABLED!=="false"}async function tt(e,r){if(!f()||!r)return null;let[t]=await o.select().from(i).where(eq(i.email,r)).limit(1);if(!t)return null;let s=authenticator.generateSecret(),n=authenticator.keyuri(r,e,s);return await o.delete(u).where(and(eq(u.userId,t.id),isNull(u.verifiedAt))),await o.insert(u).values({id:p(),name:"Default",secret:s,userId:t.id}),new Promise((l,g)=>{ze.toDataURL(n,{rendererOpts:{quality:1},margin:0,scale:2},(T,ce)=>{T&&g(T),l(ce);});})}async function st(e){if(!f())return !1;let[r]=await o.select().from(u).where(and(eq(u.userId,e.id),isNotNull(u.verifiedAt))).limit(1);return !!r}async function Je(e){f()&&await o.update(u).set({verifiedAt:new Date}).where(eq(u.userId,e));}async function He(e,r){if(!f())return !1;let[t]=await o.select().from(u).where(eq(u.userId,e)).limit(1);return t?authenticator.check(r,t.secret):!1}async function $e(e){if(!f())return !1;let r=await K();return !r||!await He(r.id,e.token)?!1:(await Je(r.id),E(r.id,"AUTHED"))}async function ot(e){return f()?await w({fn:$e,formData:e,request:V}):[null,new Error("MFA is not enabled")]}var Be=12,Z={min:8,upper:1,lower:1,number:1,symbol:1};function We(e,r){return e.length>=r}function Ke(e,r){return e.replace(/[^A-Z]/gu,"").length>=r}function Ge(e,r){return e.replace(/[^a-z]/gu,"").length>=r}function Qe(e,r){return e.replace(/[^0-9]/gu,"").length>=r}function Xe(e,r){return e.replace(/[^$]/gu,"").length>=r}var Ye={min:We,upper:Ke,lower:Ge,number:Qe,symbol:Xe};async function S(e){return await Y.hash(e,Be)}async function U(e,r){return !e||!r?!1:await Y.compare(e,r)}async function Ze(e,r=Z){let s=Object.entries(r).reduce((n,[l,g])=>(n[l]=Ye[l](e,g),n),{});return Promise.resolve(s)}async function at(e,r=Z){let t=await Ze(e,r);return Promise.resolve(Object.values(t).every(Boolean))}var er=m({email:C.string().email({minDomainSegments:2,tlds:!1}).required(),password:C.string().min(8).required()}),re=er;var tr=m({email:C.string().max(60).email({minDomainSegments:2,tlds:!1}).required().messages({"any.required":"Please provide your email address, so we can send you a reset link","string.empty":"Please provide your email address, so we can send you a reset link","string.email":"Please make sure your email address is valid","string.max":"Please make sure your email address is valid"})}),te=tr;var sr=m({token:C.string().pattern(/[a-z0-9]{40}/u).required(),password:C.string().required().messages({"any.required":"Please provide your new password","string.empty":"Please provide your new password"})}),oe=sr;var or=40,nr=36e5;async function Tt(){let e=v();e&&await B(e);}function F(){let e=process.env.AUTH_ALLOWED_ROLES;if(!e)throw new Error("AUTH_ALLOWED_ROLES is not defined. Authentication will not be possible.");return e.split(",").map(r=>Number(r)).filter(r=>!isNaN(r))}async function ae(e){return await E(e,f()?"MFA":"AUTHED"),(await Q("AUTHED"))?.redirectOnAuth||null}async function le(e){let[r]=await o.select().from(i).where(and(eq(i.email,e),inArray(i.role,F()))).limit(1);return r}async function ir({email:e,password:r}){let t=await le(e);if(!t?.password||!await U(r,t.password))throw new b({email:"",password:""});let s=await ae(t.id);if(!s)throw new b({email:"",password:""});return s}async function Dt(e,r){return console.log("handleLoginForm",r),await w({fn:ir,formData:e,onSuccess:async()=>{console.log("onSuccess",r),await r?.();},request:re})}async function Ot({email:e,password:r,role:t}){let s=r?await S(r):null,[n]=await o.insert(i).values({id:p(),email:e,password:s,role:t}).returning();return n}async function ar(e){let r=await le(e);if(!r)return null;await o.delete(c).where(eq(c.userId,r.id));let t=p("",or);return await o.insert(c).values({id:t,userId:r.id,expiresAt:new Date(new Date().getTime()+nr)}),t}async function Nt(e,r){async function t(n){let l=await ar(n.email);return l?r(n.email,l):!0}return await w({fn:t,formData:e,request:te})}async function lr(e,r){let[t]=await o.select().from(c).where(eq(c.id,r)).limit(1);return !t||(await o.delete(c).where(eq(c.id,r)),!t||t.expiresAt<new Date)?null:(await W(t.userId),await o.update(i).set({password:await S(e)}).where(and(eq(i.id,t.userId),inArray(i.role,F()))),t.userId)}async function jt(e){async function r(s){let n=await lr(s.password,s.token);return n?ae(n):null}return await w({fn:r,formData:e,request:oe})}var ur=16,cr=64;async function _t(e){let[r]=await o.select().from(h).where(eq(h.id,e)).limit(1);return r}async function Vt({alias:e,id:r,secret:t}){let[s]=await o.insert(h).values({alias:e,id:r||p("",ur),secret:await S(t||p("",cr))}).returning();return s}async function zt(e){let{headers:r}=e,t=r.get("authorization");if(!t)return null;let s=Buffer.from(t.replace("Basic ",""),"base64").toString("utf-8").replace(/:$/u,""),[n,...l]=s.split("-"),[g]=await o.select().from(h).where(eq(h.id,n)).limit(1);return g&&await U(l.join(""),g.secret)?g:null}
18
+
19
+ export { f as checkMFAEnabled, at as checkPasswordComplexity, Le as checkRouteAllowed, $r as checkSessionExists, st as checkUserHasMFA, E as createUserSession, p as generateID, tt as generateMFA, F as getAllowedRoles, _t as getClientByID, Ze as getPasswordComplexity, Q as getScopeByID, G as getScopes, v as getSessionID, K as getSessionUser, zt as handleClientAuth, Dt as handleLoginForm, Tt as handleLogout, ot as handleMFAForm, Nt as handlePasswordForm, jt as handlePasswordResetForm, Br as handleSession, S as hashPassword, B as invalidateSession, W as invalidateUserSessions, d as lucia, Vt as registerClient, Ot as registerUser, Ie as setScopes, U as verifyPassword };
20
+ //# sourceMappingURL=out.js.map
21
+ //# sourceMappingURL=index.js.map