@lunora/auth 1.0.0-alpha.80 → 1.0.0-alpha.82
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.
- package/dist/adapter.mjs +1 -1
- package/dist/audit.mjs +2 -2
- package/dist/email-guard.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/middleware.mjs +1 -1
- package/dist/packem_shared/AUTH_DO_AUDIT_PATH-WJbI0Bpc.mjs +1 -0
- package/dist/packem_shared/DEFAULT_AUTH_BASE_PATH-C7moXiBA.mjs +1 -0
- package/dist/packem_shared/LunoraAuthAdminError-C20JtTqI.mjs +1 -0
- package/dist/packem_shared/adapter-DA8DdALX.mjs +1 -0
- package/dist/packem_shared/authAuditHook-DOd5YOTL.mjs +1 -0
- package/dist/packem_shared/authDoColumnAdditions-BCSs7qaN.mjs +1 -0
- package/dist/packem_shared/compileMigrationsSql-Y1YB3maO.mjs +1 -0
- package/dist/packem_shared/createAuth-DYFhkI1U.mjs +1 -0
- package/dist/packem_shared/createDoAuthWiring-C7uxkces.mjs +1 -0
- package/dist/packem_shared/createLunoraAuthClient-DhjhCwmM.mjs +1 -0
- package/dist/packem_shared/emailGateDatabaseHooks-CAygR3iq.mjs +1 -0
- package/dist/packem_shared/sessionPresets-C867Mlo4.mjs +1 -0
- package/dist/packem_shared/uiConfig-B8pN6pks.mjs +1 -0
- package/dist/plugins-client.d.mts +97 -1
- package/dist/plugins-client.d.ts +97 -1
- package/dist/plugins-client.mjs +1 -1
- package/dist/plugins-enterprise-client.d.mts +7 -1
- package/dist/plugins-enterprise-client.d.ts +7 -1
- package/dist/plugins-enterprise.d.mts +23 -3
- package/dist/plugins-enterprise.d.ts +23 -3
- package/dist/plugins.d.mts +19 -1
- package/dist/plugins.d.ts +19 -1
- package/dist/plugins.mjs +1 -1
- package/dist/schema.mjs +1 -1
- package/dist/sql-store.mjs +1 -1
- package/dist/store.mjs +1 -1
- package/dist/turnstile-middleware.mjs +1 -1
- package/dist/turnstile.mjs +1 -1
- package/package.json +11 -11
- package/dist/packem_shared/AUTH_DO_AUDIT_PATH-C4897amZ.mjs +0 -1
- package/dist/packem_shared/DEFAULT_AUTH_BASE_PATH-kIwlEt8i.mjs +0 -1
- package/dist/packem_shared/LunoraAuthAdminError-CMUphsi_.mjs +0 -1
- package/dist/packem_shared/adapter-RvDcm0Zy.mjs +0 -1
- package/dist/packem_shared/authAuditHook-CP7kj4Ob.mjs +0 -1
- package/dist/packem_shared/authDoColumnAdditions-B8BRbdzn.mjs +0 -1
- package/dist/packem_shared/compileMigrationsSql-BcvcHAqo.mjs +0 -1
- package/dist/packem_shared/createAuth-DRtd4q6u.mjs +0 -1
- package/dist/packem_shared/createDoAuthWiring-acnXUGZr.mjs +0 -1
- package/dist/packem_shared/createLunoraAuthClient-gxLo2pKj.mjs +0 -1
- package/dist/packem_shared/emailGateDatabaseHooks-DzBD1Qoq.mjs +0 -1
- package/dist/packem_shared/sessionPresets-DpEFjXKV.mjs +0 -1
- package/dist/packem_shared/uiConfig-BrYEFK3O.mjs +0 -1
package/dist/adapter.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import{l as t,a as p,b as e}from"./packem_shared/adapter-DA8DdALX.mjs";import"./sql-store.mjs";export{t as lunoraAuthAdapter,p as lunoraD1Adapter,e as lunoraDoAdapter};
|
package/dist/audit.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{standardRules as A,piiRules as L,redact as f}from"@visulima/redact";const n="__lunora_auth_audit__",g=[...A,...L],R=1e3,N=1e4,s=null,u=e=>{if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="bigint"||typeof e=="boolean")return String(e)},l=async e=>{await e.run(`CREATE TABLE IF NOT EXISTS "${n}" (
|
|
2
2
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
3
3
|
ts REAL NOT NULL,
|
|
4
4
|
event TEXT NOT NULL,
|
|
@@ -9,4 +9,4 @@ import{redact as p,standardRules as A,piiRules as g}from"@visulima/redact";const
|
|
|
9
9
|
ip TEXT,
|
|
10
10
|
user_agent TEXT,
|
|
11
11
|
detail TEXT
|
|
12
|
-
)`,[]);try{await
|
|
12
|
+
)`,[]);try{await e.run(`ALTER TABLE "${n}" ADD COLUMN target_email TEXT`,[])}catch(t){if(!(t instanceof Error?t.message.toLowerCase():String(t).toLowerCase()).includes("duplicate column"))throw t}},S=async(e,t,i={})=>{await l(e);let a;return t.detail!==void 0&&(a=i.redactDetail===!1?t.detail:f(t.detail,g)),await e.run(`INSERT INTO "${n}" (ts, event, outcome, actor_id, actor_email, target_email, ip, user_agent, detail) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,[t.ts,t.event,t.outcome,t.actorId??s,t.actorEmail??s,t.targetEmail??s,t.ip??s,t.userAgent??s,a===void 0?s:JSON.stringify(a)]),typeof i.retention=="number"&&i.retention>0&&await e.run(`DELETE FROM "${n}" WHERE seq <= (SELECT MAX(seq) - ? FROM "${n}")`,[i.retention]),{...t,detail:a}},I=async(e,t={})=>{await l(e);const i=Number.isFinite(t.limit)?Math.max(1,Math.min(t.limit,N)):R,a=["seq > ?"],o=[t.sinceSeq??0];return t.actorId!==void 0&&(a.push("actor_id = ?"),o.push(t.actorId)),t.event!==void 0&&(a.push("event = ?"),o.push(t.event)),o.push(i),(await e.all(`SELECT seq, ts, event, outcome, actor_id, actor_email, target_email, ip, user_agent, detail FROM "${n}" WHERE ${a.join(" AND ")} ORDER BY seq DESC LIMIT ?`,o)).map(r=>{const c={event:u(r.event)??"",outcome:r.outcome==="failure"?"failure":"success",seq:Number(r.seq),ts:Number(r.ts)},d=[["actorId","actor_id"],["actorEmail","actor_email"],["targetEmail","target_email"],["ip","ip"],["userAgent","user_agent"]];for(const[m,_]of d){const T=u(r[_]);T!==void 0&&(c[m]=T)}const E=u(r.detail);return E!==void 0&&(c.detail=JSON.parse(E)),c})},D=e=>({read:t=>I(e,t)});export{n as AUTH_AUDIT_TABLE,S as appendAuthAuditEntry,D as createAuthAuditReader,l as ensureAuthAuditTable,I as readAuthAuditLog};
|
package/dist/email-guard.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as
|
|
1
|
+
import{LunoraError as o}from"@lunora/errors";import{isDisposableDomain as c,setDomains as d}from"@visulima/disposable-email-domains";import{extractDomain as u,isFreeDomain as w,setDomains as D}from"@visulima/free-email-domains";const r=a=>{const s=a.default??a;return Array.isArray(s)?s:[]};let n;const f=async()=>(n??=(async()=>{const[a,s]=await Promise.all([import("@visulima/disposable-email-domains/domains"),import("@visulima/free-email-domains/domains")]);d(r(a)),D(r(s))})(),n),m=a=>{try{return new URL(`https://${a}`).hostname}catch{return a.toLowerCase()}},l=a=>a&&a.length>0?new Set(a.map(s=>m(s))):void 0,h=(a,s={})=>{const t=u(a);if(t===void 0)return{domain:void 0,emailClass:"business"};const e=m(t),i=l(s.allowDomains);return c(e,{allowDomains:i,customDomains:l(s.denyDomains)})?{domain:e,emailClass:"disposable"}:w(e,{allowDomains:i})?{domain:e,emailClass:"free"}:{domain:e,emailClass:"business"}},p=async a=>{const{checkMxRecords:s}=await import("@visulima/email-verifier/checks/mx");return(await s(a)).valid},y=async(a,s={})=>{if(s.requireValidSyntax!==!1){const{validateSyntax:e}=await import("@visulima/email-verifier/checks/syntax");if(!e(a))throw new o("VALIDATION_ERROR",`@lunora/auth: "${a}" is not a valid email address.`)}await f();const t=h(a,s);if(t.emailClass==="disposable"&&s.blockDisposable!==!1)throw new o("EMAIL_DOMAIN_BLOCKED",`@lunora/auth: signups from the disposable/throwaway domain "${t.domain??a}" are not allowed.`);if(s.mx===!0&&t.domain!==void 0&&!await p(t.domain))throw new o("EMAIL_UNDELIVERABLE",`@lunora/auth: the domain "${t.domain}" publishes no MX records, so mail to it cannot be delivered.`);return t},b=a=>async({ctx:s,next:t})=>{const e=a.email(s);if(e===void 0||e==="")throw new o("VALIDATION_ERROR","@lunora/auth: emailGateMiddleware received no email to check.");const i=await y(e,a);return a.onClassify?.(i,s),t()};export{y as assertEmailAllowed,h as classifyEmail,b as emailGateMiddleware,f as loadEmailDomainLists};
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{l as r,a as o,b as a}from"./packem_shared/adapter-DA8DdALX.mjs";import{LunoraAuthAdminError as u,createAuthAdmin as i}from"./packem_shared/LunoraAuthAdminError-C20JtTqI.mjs";import{AUTH_AUDIT_TABLE as m,appendAuthAuditEntry as l,createAuthAuditReader as d,ensureAuthAuditTable as h,readAuthAuditLog as E}from"./audit.mjs";import{authAuditHook as p,buildAuditEntry as T,eventForPath as f,withAuthAudit as _}from"./packem_shared/authAuditHook-DOd5YOTL.mjs";import{READ_AUDIT_PATH as D,INTERNAL_SECRET_HEADER as S,RESOLVE_SESSION_PATH as H,LunoraAuthDO as c}from"./packem_shared/AUTH_DO_AUDIT_PATH-WJbI0Bpc.mjs";import{createAuth as L,resolveAuthOptions as P}from"./packem_shared/createAuth-DYFhkI1U.mjs";import{authDoColumnAdditions as I,authDoSchemaStatements as O}from"./packem_shared/authDoColumnAdditions-BCSs7qaN.mjs";import{createDoAuthWiring as N}from"./packem_shared/createDoAuthWiring-C7uxkces.mjs";import{emailGateDatabaseHooks as b,withEmailGate as g}from"./packem_shared/emailGateDatabaseHooks-CAygR3iq.mjs";import{assertEmailAllowed as M,classifyEmail as q,emailGateMiddleware as C,loadEmailDomainLists as F}from"./email-guard.mjs";import{DEFAULT_AUTH_BASE_PATH as k,handleAuthRequest as B}from"./packem_shared/DEFAULT_AUTH_BASE_PATH-C7moXiBA.mjs";import{LunoraAuthHeadersError as W,withAuthPlugins as Y}from"./middleware.mjs";import{compileMigrationsSql as z,ensureMigrated as J}from"./packem_shared/compileMigrationsSql-Y1YB3maO.mjs";import{default as Q}from"./schema.mjs";import{sessionPresets as Z,validateSessionPolicy as $}from"./packem_shared/sessionPresets-C867Mlo4.mjs";import{createSqlAuthStore as te,d1Executor as re}from"./sql-store.mjs";import{createMemoryAuthStore as ae,matchesWhere as Ae}from"./store.mjs";import{TURNSTILE_VERIFY_ENDPOINT as ie,verifyTurnstile as se}from"./turnstile.mjs";import{verifyTurnstileMiddleware as le}from"./turnstile-middleware.mjs";export{m as AUTH_AUDIT_TABLE,D as AUTH_DO_AUDIT_PATH,S as AUTH_DO_SECRET_HEADER,H as AUTH_DO_SESSION_PATH,k as DEFAULT_AUTH_BASE_PATH,u as LunoraAuthAdminError,c as LunoraAuthDO,W as LunoraAuthHeadersError,ie as TURNSTILE_VERIFY_ENDPOINT,l as appendAuthAuditEntry,M as assertEmailAllowed,p as authAuditHook,I as authDoColumnAdditions,O as authDoSchemaStatements,Q as authTables,T as buildAuditEntry,q as classifyEmail,z as compileMigrationsSql,L as createAuth,i as createAuthAdmin,d as createAuthAuditReader,N as createDoAuthWiring,ae as createMemoryAuthStore,te as createSqlAuthStore,re as d1Executor,b as emailGateDatabaseHooks,C as emailGateMiddleware,h as ensureAuthAuditTable,J as ensureMigrated,f as eventForPath,B as handleAuthRequest,F as loadEmailDomainLists,r as lunoraAuthAdapter,o as lunoraD1Adapter,a as lunoraDoAdapter,Ae as matchesWhere,E as readAuthAuditLog,P as resolveAuthOptions,Z as sessionPresets,$ as validateSessionPolicy,se as verifyTurnstile,le as verifyTurnstileMiddleware,_ as withAuthAudit,Y as withAuthPlugins,g as withEmailGate};
|
package/dist/middleware.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as i}from"@lunora/errors";const
|
|
1
|
+
import{LunoraError as i}from"@lunora/errors";const d=e=>{if(e===void 0)return!1;if(typeof e!="object"||e===null)return!0;const{headers:t}=e;return t!=null},c=e=>{const t=()=>e;return new Proxy(e,{get(s,r,a){if(r==="withoutHeaders"&&!(r in s))return t;const n=Reflect.get(s,r,a);if(typeof n!="function"||typeof r!="string")return n;const u=r;return(...o)=>d(o[0])?Reflect.apply(n,s,o):Promise.reject(new h(u))}})};class h extends i{method;constructor(t){super("AUTH_HEADERS_MISSING",`@lunora/auth: ctx.authApi.${t}(…) was called without \`headers\`. better-auth treats a header-less call as a trusted server-to-server invocation and skips session authorization entirely — an authorization bypass. Pass the inbound request headers: ctx.authApi.${t}({ body, headers: request.headers }). If you genuinely intend an unauthenticated server-to-server call, opt out explicitly via ctx.authApi.withoutHeaders().<method>(…), or disable the guard for the whole middleware with withAuthPlugins(auth, { enforceHeaders: false }).`,{name:"LunoraAuthHeadersError"}),this.method=t}}const f=(e,t={})=>{const r=t.enforceHeaders??!0?c(e.api):e.api;return async({next:a})=>await a({ctx:{authApi:r}})};export{h as LunoraAuthHeadersError,f as withAuthPlugins};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as i,d as u}from"./adapter-DA8DdALX.mjs";import{ensureAuthAuditTable as h,createAuthAuditReader as c}from"../audit.mjs";import{resolveAuthOptions as l,createAuth as d}from"./createAuth-DYFhkI1U.mjs";import{authDoSchemaStatements as f,authDoColumnAdditions as p}from"./authDoColumnAdditions-BCSs7qaN.mjs";import{handleAuthRequest as m}from"./DEFAULT_AUTH_BASE_PATH-C7moXiBA.mjs";const A=(o,t)=>{const e=Math.max(o.length,t.length);let s=o.length^t.length;for(let r=0;r<e;r+=1){const n=r<o.length?o.charCodeAt(r):0,a=r<t.length?t.charCodeAt(r):0;s|=n^a}return s===0},R="/__lunora/auth/session",y="/__lunora/auth/audit",g="x-lunora-auth-do-secret",S=o=>{if(o===null||typeof o!="object"||Array.isArray(o))return{error:"body must be a JSON object"};const t={};for(const[e,s]of Object.entries(o))if(e==="limit"||e==="sinceSeq"){if(typeof s!="number"||!Number.isFinite(s))return{error:`"${e}" must be a finite number`};t[e]=s}else if(e==="actorId"||e==="event"){if(typeof s!="string")return{error:`"${e}" must be a string`};t[e]=s}else return{error:`unknown audit read option "${e}"`};return{options:t}};class v{#s;#r;#t;#e;#o=!1;constructor(t,e,s={}){this.#t=t.storage,this.#r=e,this.#s=s}#n(){if(this.#e!==void 0)return this.#e;const t=this.#r();if(!this.#o){const e=l(t);for(const s of f(e))[...this.#t.sql.exec(s)];for(const s of p(e,r=>this.#i(r)))[...this.#t.sql.exec(s)];this.#o=!0}return this.#e=d({...t,database:i(this.#t)}),this.#e}#i(t){return[...this.#t.sql.exec("SELECT name FROM pragma_table_info(?)",t)].map(s=>String(s.name))}async#u(t){if(!this.#a(t))return Response.json({error:"unauthorized"},{status:401});let e;try{e=await t.json()}catch{return Response.json({error:"invalid body"},{status:400})}const s=S(e??{});if("error"in s)return Response.json({error:s.error},{status:400});const r=u(this.#t);await h(r);const n=await c(r).read(s.options);return Response.json({entries:n})}#a(t){const{internalSecret:e}=this.#s;if(e===void 0||e==="")return!1;const s=t.headers.get(g);return s!==null&&A(s,e)}async#h(t){if(!this.#a(t))return Response.json({error:"unauthorized"},{status:401});const r=(await this.#n().api.getSession({headers:t.headers}))?.user.id;return Response.json(r===void 0?{}:{userId:r})}async fetch(t){const e=new URL(t.url);if(e.pathname===R)return this.#h(t);if(e.pathname===y)return this.#u(t);const s=this.#n();return await m(s,t,this.#s.basePath)??Response.json({error:"not an auth route"},{status:404})}}export{g as INTERNAL_SECRET_HEADER,v as LunoraAuthDO,y as READ_AUDIT_PATH,R as RESOLVE_SESSION_PATH};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const a="/api/auth",c=async(s,t,n=a)=>{const e=new URL(t.url),r=n.endsWith("/")?n.slice(0,-1):n;if(!(e.pathname!==r&&!e.pathname.startsWith(`${r}/`)))return s.handler(t)};export{a as DEFAULT_AUTH_BASE_PATH,c as handleAuthRequest};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createLocalAccountIssuer as M}from"@better-auth/core/db";import{LunoraError as I}from"@lunora/errors";import{getAuthTables as S}from"better-auth/db";class f extends I{constructor(m,w){super(w,m,{name:"LunoraAuthAdminError"})}}const N=50,R=500,O=3600,U=O*24,D=100*365*24*60*60,E=2880*60*1e3,T=new Set(["accessToken","backupCodes","idToken","password","publicKey","refreshToken","secret","token"]),z=d=>Math.min(Math.max(Math.trunc(d??N),1),R),L=d=>Math.max(0,Math.trunc(d??0)),u=d=>{const m={};for(const[w,l]of Object.entries(d))T.has(w)||(m[w]=l instanceof Date?l.getTime():l);return m},A=d=>Array.isArray(d)?d.join(","):d,g=d=>d.toLowerCase().replaceAll(/[^\da-z]+/g,"-").replaceAll(/^-|-$/g,""),k=new Set(["banExpires","banned","banReason","createdAt","email","emailVerified","id","name","role","updatedAt"]),P={displayUsername:"username",phoneNumber:"phone-number",phoneNumberVerified:"phone-number",username:"username"},B=d=>d==="boolean"||d==="date"||d==="number"?d:"string",C=d=>{const m=[];for(const[w,l]of Object.entries(d))l.input===!1||l.references!==void 0||k.has(w)||m.push({name:w,plugin:P[w],required:l.required===!0,type:B(l.type),unique:l.unique===!0});return m},F=d=>{if(d instanceof f)return d;const m=d,w=m?.body?.code??m?.code??"AUTH_ADMIN_ERROR",l=m?.body?.message??m?.message??"auth admin operation failed";return new f(l,w)},j=(d,m={})=>{const w=d.$context,l=m.features??{},b=e=>{const a=new Set((e.plugins??[]).map(i=>i.id)),t=i=>a.has(i);return{accounts:l.accounts??!0,admin:l.admin??t("admin"),organization:l.organization??t("organization"),passkey:l.passkey??t("passkey"),twoFactor:l.twoFactor??t("two-factor")}},r=async e=>{try{return await e(await w)}catch(a){throw F(a)}},v=e=>u(e),h=async(e,a,t)=>{const i=t.where&&t.where.length>0?t.where:void 0,[n,s]=await Promise.all([e.adapter.findMany({limit:z(t.limit),model:a,offset:L(t.offset),sortBy:t.sortBy,where:i}),e.adapter.count({model:a,where:i})]);return{rows:n.map(o=>u(o)),total:s}};return{banUser:({expiresInSeconds:e,reason:a,userId:t})=>r(async i=>{let n=null;if(e!==void 0){if(!Number.isInteger(e)||e<=0)throw new f("expiresInSeconds must be a positive finite integer","INVALID_BAN_SECONDS");const o=Math.min(e,D);n=new Date(Date.now()+o*1e3)}const s=await i.internalAdapter.updateUser(t,{banExpires:n,banned:!0,banReason:a??"No reason"});return await i.internalAdapter.deleteUserSessions(t),v(s)}),cancelInvitation:({invitationId:e})=>r(async a=>{await a.adapter.delete({model:"invitation",where:[{field:"id",value:e}]})}),capabilities:()=>r(e=>Promise.resolve(b(e.options))),addMember:({organizationId:e,role:a,userId:t})=>r(async i=>{const n=await i.adapter.create({data:{createdAt:new Date,organizationId:e,role:a===void 0||a===""?"member":a,userId:t},model:"member"});return u(n)}),addTeamMember:({teamId:e,userId:a})=>r(async t=>{const i=await t.adapter.create({data:{createdAt:new Date,teamId:e,userId:a},model:"teamMember"});return u(i)}),config:()=>r(e=>{const a=e.options,t=b(a),i=new Set((a.plugins??[]).map(c=>c.id)),n=S(a),s=a.session??{},o=a.rateLimit??{};return Promise.resolve({capabilities:t,emailAndPassword:a.emailAndPassword?.enabled??!1,organization:{enabled:t.organization,roles:!!n.organizationRole,teams:!!n.team},plugins:[...i].toSorted((c,p)=>c.localeCompare(p)),rateLimit:{enabled:o.enabled??!1,max:o.max,window:o.window},session:{cookieCache:s.cookieCache?.enabled,expiresIn:s.expiresIn,freshAge:s.freshAge,updateAge:s.updateAge},socialProviders:Object.keys(a.socialProviders??{}).toSorted((c,p)=>c.localeCompare(p)),userFields:C(n.user?.fields??{})})}),createOrganization:({logo:e,metadata:a,name:t,ownerId:i,slug:n})=>r(async s=>{const o=g(n!==void 0&&n!==""?n:t);if(o==="")throw new f("could not derive a slug from the organization name","ORG_SLUG_INVALID");if(await s.adapter.findOne({model:"organization",where:[{field:"slug",value:o}]}))throw new f("an organization with this slug already exists","ORG_SLUG_TAKEN");const p=await s.adapter.create({data:{createdAt:new Date,logo:e===void 0||e===""?void 0:e,metadata:a===void 0?void 0:JSON.stringify(a),name:t,slug:o},model:"organization"});return i!==void 0&&i!==""&&await s.adapter.create({data:{createdAt:new Date,organizationId:p.id,role:"owner",userId:i},model:"member"}),u(p)}),createOrgRole:({organizationId:e,permission:a,role:t})=>r(async i=>{const n=await i.adapter.create({data:{createdAt:new Date,organizationId:e,permission:JSON.stringify(a),role:t},model:"organizationRole"});return u(n)}),createTeam:({name:e,organizationId:a})=>r(async t=>{const i=await t.adapter.create({data:{createdAt:new Date,name:e,organizationId:a},model:"team"});return u(i)}),deleteOrganization:({organizationId:e})=>r(async a=>{const t=S(a.options);if(await a.adapter.deleteMany({model:"member",where:[{field:"organizationId",value:e}]}),await a.adapter.deleteMany({model:"invitation",where:[{field:"organizationId",value:e}]}),t.team){const i=await a.adapter.findMany({model:"team",where:[{field:"organizationId",value:e}]});for(const n of i)await a.adapter.deleteMany({model:"teamMember",where:[{field:"teamId",value:n.id}]});await a.adapter.deleteMany({model:"team",where:[{field:"organizationId",value:e}]})}t.organizationRole&&await a.adapter.deleteMany({model:"organizationRole",where:[{field:"organizationId",value:e}]}),await a.adapter.delete({model:"organization",where:[{field:"id",value:e}]})}),deleteOrgRole:({roleId:e})=>r(async a=>{await a.adapter.delete({model:"organizationRole",where:[{field:"id",value:e}]})}),inviteMember:({email:e,inviterId:a,organizationId:t,role:i})=>r(async n=>{let s=a;if(s===void 0||s===""){const c=await n.adapter.findMany({model:"member",where:[{field:"organizationId",value:t}]});s=(c.find(y=>typeof y.role=="string"&&y.role.includes("owner"))??c[0])?.userId}if(s===void 0||s==="")throw new f("provide an inviter — the organization has no members to attribute the invitation to","INVITER_REQUIRED");const o=await n.adapter.create({data:{createdAt:new Date,email:e.toLowerCase(),expiresAt:new Date(Date.now()+E),inviterId:s,organizationId:t,role:i===void 0||i===""?"member":i,status:"pending"},model:"invitation"});return u(o)}),listOrgRoles:({limit:e,offset:a,organizationId:t})=>r(i=>h(i,"organizationRole",{limit:e,offset:a,sortBy:{direction:"desc",field:"createdAt"},where:[{field:"organizationId",value:t}]})),listTeamMembers:({limit:e,offset:a,teamId:t})=>r(i=>h(i,"teamMember",{limit:e,offset:a,where:[{field:"teamId",value:t}]})),listTeams:({limit:e,offset:a,organizationId:t})=>r(i=>h(i,"team",{limit:e,offset:a,sortBy:{direction:"desc",field:"createdAt"},where:[{field:"organizationId",value:t}]})),removeTeam:({teamId:e})=>r(async a=>{await a.adapter.deleteMany({model:"teamMember",where:[{field:"teamId",value:e}]}),await a.adapter.delete({model:"team",where:[{field:"id",value:e}]})}),removeTeamMember:({teamMemberId:e})=>r(async a=>{await a.adapter.delete({model:"teamMember",where:[{field:"id",value:e}]})}),updateMemberRole:({memberId:e,role:a})=>r(async t=>{const i=await t.adapter.update({model:"member",update:{role:A(a)},where:[{field:"id",value:e}]});return u(i??{id:e,role:A(a)})}),updateOrganization:({logo:e,metadata:a,name:t,organizationId:i,slug:n})=>r(async s=>{const o={};if(t!==void 0&&(o.name=t),n!==void 0&&n!==""&&(o.slug=g(n)),e!==void 0&&(o.logo=e===""?void 0:e),a!==void 0&&(o.metadata=JSON.stringify(a)),Object.keys(o).length===0)return u({id:i});const c=await s.adapter.update({model:"organization",update:o,where:[{field:"id",value:i}]});return u(c??{id:i})}),updateOrgRole:({permission:e,roleId:a})=>r(async t=>{const i=await t.adapter.update({model:"organizationRole",update:{permission:JSON.stringify(e),updatedAt:new Date},where:[{field:"id",value:a}]});return u(i??{id:a,permission:JSON.stringify(e)})}),updateTeam:({name:e,teamId:a})=>r(async t=>{const i=await t.adapter.update({model:"team",update:{name:e,updatedAt:new Date},where:[{field:"id",value:a}]});return u(i??{id:a,name:e})}),createUser:({data:e,email:a,name:t,password:i,role:n})=>r(async s=>{const o=a.toLowerCase();if(await s.internalAdapter.findUserByEmail(o))throw new f("a user with this email already exists","USER_ALREADY_EXISTS");const c=await s.internalAdapter.createUser({email:o,name:t,role:n===void 0?void 0:A(n),...e},{method:"admin"});if(i!==void 0&&i!==""){const p=await s.password.hash(i);await s.internalAdapter.linkAccount({issuer:M("credential"),password:p,providerAccountId:c.id,providerId:"credential",userId:c.id})}return v(c)}),deletePasskey:({passkeyId:e})=>r(async a=>{await a.adapter.delete({model:"passkey",where:[{field:"id",value:e}]})}),disableTwoFactor:({userId:e})=>r(async a=>{await a.adapter.deleteMany({model:"twoFactor",where:[{field:"userId",value:e}]}),await a.internalAdapter.updateUser(e,{twoFactorEnabled:!1})}),impersonateUser:({userId:e})=>r(async a=>{const t=await a.internalAdapter.findUserById(e);if(!t)throw new f("user not found","USER_NOT_FOUND");const i=m.impersonationSeconds;let n=O;if(i!==void 0){if(!Number.isInteger(i)||!Number.isFinite(i)||i<=0)throw new f("impersonationSeconds must be a positive finite integer","INVALID_IMPERSONATION_SECONDS");n=Math.min(i,U)}const s=new Date(Date.now()+n*1e3),o=await a.internalAdapter.createSession(e,!0,{expiresAt:s,impersonatedBy:m.impersonatedBy??e},!0);return{expiresAt:o.expiresAt instanceof Date?o.expiresAt.getTime():s.getTime(),token:o.token,user:v(t)}}),listAccounts:({userId:e})=>r(async a=>(await a.adapter.findMany({model:"account",where:[{field:"userId",value:e}]})).map(i=>u(i))),listInvitations:({limit:e,offset:a,organizationId:t})=>r(i=>h(i,"invitation",{limit:e,offset:a,where:[{field:"organizationId",value:t}]})),listMembers:({limit:e,offset:a,organizationId:t})=>r(i=>h(i,"member",{limit:e,offset:a,sortBy:{direction:"desc",field:"createdAt"},where:[{field:"organizationId",value:t}]})),listOrganizations:({limit:e,offset:a})=>r(t=>h(t,"organization",{limit:e,offset:a,sortBy:{direction:"desc",field:"createdAt"}})),listPasskeys:({userId:e})=>r(async a=>(await a.adapter.findMany({model:"passkey",where:[{field:"userId",value:e}]})).map(i=>u(i))),listSessions:({limit:e,offset:a,userId:t})=>r(i=>h(i,"session",{limit:e,offset:a,sortBy:{direction:"desc",field:"createdAt"},where:t===void 0||t===""?void 0:[{field:"userId",value:t}]})),listUsers:({filterField:e,filterValue:a,limit:t,offset:i,search:n,searchField:s,sortBy:o,sortDirection:c})=>r(p=>{const y=[];return n!==void 0&&n!==""&&y.push({field:s??"email",operator:"contains",value:n}),a!==void 0&&y.push({field:e??"email",operator:"eq",value:a}),h(p,"user",{limit:t,offset:i,sortBy:{direction:c??"desc",field:o??"createdAt"},where:y})}),removeMember:({memberId:e})=>r(async a=>{await a.adapter.delete({model:"member",where:[{field:"id",value:e}]})}),removeUser:({userId:e})=>r(async a=>{await a.internalAdapter.deleteUserSessions(e),await a.internalAdapter.deleteUser(e)}),revokeUserSession:({sessionId:e})=>r(async a=>{const t=await a.adapter.findOne({model:"session",where:[{field:"id",value:e}]});t?.token&&await a.internalAdapter.deleteSession(t.token)}),revokeUserSessions:({userId:e})=>r(async a=>{await a.internalAdapter.deleteUserSessions(e)}),setRole:({role:e,userId:a})=>r(async t=>{const i=await t.internalAdapter.updateUser(a,{role:A(e)});return v(i)}),setUserPassword:({newPassword:e,userId:a})=>r(async t=>{const i=t.password.config.minPasswordLength,n=t.password.config.maxPasswordLength;if(e.length<i)throw new f(`password must be at least ${i.toString()} characters`,"PASSWORD_TOO_SHORT");if(e.length>n)throw new f(`password must be at most ${n.toString()} characters`,"PASSWORD_TOO_LONG");const s=await t.password.hash(e);await t.internalAdapter.updatePassword(a,s)}),unbanUser:({userId:e})=>r(async a=>{const t=await a.internalAdapter.updateUser(e,{banExpires:null,banned:!1,banReason:null});return v(t)}),unlinkAccount:({accountId:e,userId:a})=>r(async t=>{await t.adapter.delete({model:"account",where:[{field:"id",value:e},{connector:"AND",field:"userId",value:a}]})}),updateUser:({data:e,userId:a})=>r(async t=>{const i=await t.internalAdapter.updateUser(a,e);return v(i)})}};export{f as LunoraAuthAdminError,j as createAuthAdmin};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createAdapterFactory as d}from"better-auth/adapters";import{createSqlAuthStore as o,d1Executor as l}from"../sql-store.mjs";const p=a=>({all:(e,n)=>Promise.resolve([...a.sql.exec(e,...n)]),run:(e,n)=>([...a.sql.exec(e,...n)],Promise.resolve())}),i=a=>async e=>a.transaction(e),y=a=>a,c=a=>a??null,m=a=>a,w=a=>({consumeOne:async({model:e,where:n})=>c(await a.consumeOne(e,n)),count:async({model:e,where:n})=>a.count(e,n??[]),create:async({data:e,model:n})=>y(await a.create(n,e)),delete:async({model:e,where:n})=>{await a.remove(e,n)},deleteMany:async({model:e,where:n})=>a.remove(e,n),findMany:async({limit:e,model:n,offset:t,sortBy:r,where:s})=>m(await a.read(n,{limit:e,offset:t,sortBy:r,where:s??[]})),findOne:async({model:e,where:n})=>{const[t]=await a.read(e,{limit:1,where:n});return c(t)},incrementOne:async({increment:e,model:n,set:t,where:r})=>c(await a.incrementOne(n,r,e,t)),update:async({model:e,update:n,where:t})=>{const[r]=await a.update(e,t,n);return c(r)},updateMany:async({model:e,update:n,where:t})=>(await a.update(e,t,n)).length}),u=(a,e)=>{const n=r=>s=>d({adapter:()=>w(a),config:{adapterId:"lunora",adapterName:"Lunora Adapter",transaction:r(s),supportsBooleans:!1,supportsDates:!1,supportsJSON:!1,supportsNumericIds:!1}})(s),t=()=>!1;return n(e?r=>async s=>e(async()=>s(n(t)(r))):t)},O=a=>u(o(l(a))),h=a=>u(o(p(a)),i(a));export{O as a,h as b,p as d,u as l};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createAuthMiddleware as d}from"better-auth/api";import{appendAuthAuditEntry as c}from"../audit.mjs";const l=r=>{const t=r.toLowerCase(),e=i=>t===i||t.endsWith(i);if(e("/sign-up/email")||e("/sign-up"))return"sign-up";if(e("/sign-in/social")||e("/sign-in/magic-link"))return"sign-in-initiated";if(t.includes("/sign-in/")||t.includes("/callback/")||e("/magic-link/verify")||t.includes("/two-factor/verify-"))return"sign-in";if(e("/sign-out"))return"sign-out";if(e("/change-password")||e("/set-password"))return"password-change";if(e("/reset-password")||e("/request-password-reset")||e("/forget-password"))return"password-reset";if(e("/verify-email"))return"email-verification";if(t.includes("/two-factor/enable")||t.includes("/totp/enable"))return"mfa-enable";if(t.includes("/two-factor/disable")||t.includes("/totp/disable"))return"mfa-disable";if(e("/refresh-token")||e("/token"))return"token-refresh";if(e("/revoke-session")||e("/revoke-sessions")||e("/revoke-other-sessions"))return"session-revoke";if(e("/link-social"))return"account-link";if(e("/unlink-account"))return"account-unlink"},o=(r,t)=>r.headers?.get(t)??r.request?.headers.get(t)??void 0,f=(r,t)=>{const e=o(r,"cf-connecting-ip");if(e!==void 0)return e;if(t===!0)return o(r,"x-forwarded-for")?.split(",")[0]?.trim()},v=r=>{const t=r.context?.newSession??r.context?.session,e=t?.user?.id??t?.session?.userId,i=t?.user?.email;return{...e===void 0?{}:{actorId:e},...i===void 0?{}:{actorEmail:i}}},h=r=>{const t=r.context?.returned;if(t instanceof Error)return"failure";if(typeof t=="object"&&t!==null&&"status"in t){const e=Number(t.status);if(Number.isFinite(e)&&e>=400)return"failure"}return"success"},u=320,p=(r,t)=>{if(t!=="sign-in"&&t!=="sign-in-initiated")return;const e=r.body?.email??r.body?.username;if(!(typeof e!="string"||e.length===0))return e.length>u?e.slice(0,u):e},g=(r,{now:t=Date.now(),trustProxyHeaders:e}={})=>{const i=r.path===void 0?void 0:l(r.path);if(i===void 0)return;const s=f(r,e),n=o(r,"user-agent"),a=p(r,i);return{...v(r),event:i,outcome:h(r),ts:t,...s===void 0?{}:{ip:s},...a===void 0?{}:{targetEmail:a},...n===void 0?{}:{userAgent:n},detail:{path:r.path}}},m=r=>d(async t=>{try{const e=g(t,{trustProxyHeaders:r.trustProxyHeaders});if(e!==void 0){const i=await c(r.executor,e,{redactDetail:r.redactDetail,retention:r.retention});r.onRecord!==void 0&&await r.onRecord(i)}}catch(e){console.error("@lunora/auth: audit hook failed to record event",e)}}),y=(r,t)=>{const e=m(t),i=r.hooks?.after,s=i?async n=>(await i(n),e(n)):e;return{...r,hooks:{...r.hooks,after:s}}};export{m as authAuditHook,g as buildAuditEntry,l as eventForPath,y as withAuthAudit};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getAuthTablesWithResolvedIndexes as l,getDatabaseFieldIndexName as N}from"@better-auth/core/db/internal";const r=n=>`"${n.replaceAll('"','""')}"`,d=(n,e)=>{if(n==="id"||e.references?.field==="id")return"text";const{type:t}=e;if(Array.isArray(t))return"text";switch(t){case"boolean":return"integer";case"date":return"date";case"number":return"bigint"in e&&e.bigint===!0?"bigint":"integer";default:return"text"}},b=n=>{const{defaultValue:e,type:t,unique:o}=n;if(!(o===!0&&n.required===!1)&&!(e==null||typeof e=="function")){if(t==="boolean")return e===!0?"1":"0";if(t==="number"&&typeof e=="number"&&Number.isFinite(e))return String(e);if(t==="string"&&typeof e=="string")return`'${e.replaceAll("'","''")}'`}},p=(n,e)=>{const t=[];for(const[o,i]of Object.entries(e)){const s=i.unique===!0;if(!s&&i.index!==!0)continue;const c=i.fieldName??o,u=N(n,c,s);t.push(`CREATE ${s?"UNIQUE ":""}INDEX IF NOT EXISTS ${r(u)} ON ${r(n)} (${r(c)})`)}return t},T=(n,e)=>{const t=[r(n),d(n,e)],o=b(e);return e.required!==!1&&o!==void 0&&t.push("NOT NULL"),o!==void 0&&t.push(`DEFAULT ${o}`),t.join(" ")},E=(n,e)=>{const t=[r(n),d(n,e)];return e.required!==!1&&t.push("NOT NULL"),t.join(" ")},h=n=>{const{indexesByTable:e,tables:t}=l(n),o=[],i=[];for(const s of Object.values(t)){if(s.disableMigrations===!0)continue;const c=[`${r("id")} text NOT NULL PRIMARY KEY`,...Object.entries(s.fields).map(([u,a])=>E(a.fieldName??u,a))];o.push(`CREATE TABLE IF NOT EXISTS ${r(s.modelName)} (${c.join(", ")})`),i.push(...p(s.modelName,s.fields));for(const u of e.get(s.modelName)??[]){const a=u.unique===!0?"UNIQUE ":"",f=u.columns.map(m=>r(m)).join(", ");i.push(`CREATE ${a}INDEX IF NOT EXISTS ${r(u.name)} ON ${r(s.modelName)} (${f})`)}}return[...o,...i]},A=(n,e)=>{const{tables:t}=l(n),o=[];for(const i of Object.values(t)){if(i.disableMigrations===!0)continue;const s=new Set(e(i.modelName));if(s.size!==0)for(const[c,u]of Object.entries(i.fields)){const a=u.fieldName??c;s.has(a)||o.push(`ALTER TABLE ${r(i.modelName)} ADD COLUMN ${T(a,u)}`)}}return o};export{A as authDoColumnAdditions,h as authDoSchemaStatements};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getMigrations as l}from"better-auth/db/migration";import{resolveAuthOptions as I}from"./createAuth-DYFhkI1U.mjs";const w=/pragma_index_list\s*\(/iu,b=/sqlite_master/iu,h=/^["`[]|["\]`]$/gu,y=/\s+/u,_=/create\s+unique\s+index/iu,x=/\bwhere\b/iu,M=t=>w.test(t)&&b.test(t),R=t=>(t.trim().split(y)[0]??"").replaceAll(h,"").trim(),D=t=>{const n=t.indexOf("(");if(n===-1)return{columns:[],tail:""};let s=0;for(let e=n;e<t.length;e+=1){const o=t[e];if(o==="(")s+=1;else if(o===")"&&(s-=1,s===0))return{columns:t.slice(n+1,e).split(",").map(i=>R(i)).filter(i=>i!==""),tail:t.slice(e+1)}}return{columns:[],tail:""}},T=async t=>{const n=t.prepare("SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index'"),{results:s}=await n.all(),e=[];for(const o of s??[]){const i=o.name,r=o.tbl_name,a=o.sql;if(typeof i!="string"||typeof r!="string")continue;if(typeof a!="string"){e.push({columnPosition:0,indexName:i,isPartial:0,isUnique:1,tableName:r});continue}const p=_.test(a)?1:0,{columns:m,tail:f}=D(a),d=x.test(f)?1:0;for(const[E,g]of m.entries())e.push({columnName:g,columnPosition:E,indexName:i,isPartial:d,isUnique:p,tableName:r})}return e},A=t=>{const n=async()=>{const e=await T(t);return{meta:{},results:e,success:!0}},s={all:n,bind:()=>s,run:n};return s},N=t=>new Proxy(t,{get(n,s,e){if(s==="prepare")return i=>M(i)?A(n):n.prepare(i);const o=Reflect.get(n,s,e);return typeof o=="function"?o.bind(n):o}}),P=t=>typeof t=="object"&&t!==null&&typeof t.prepare=="function"&&typeof t.batch=="function",u=t=>P(t.database)?{...t,database:N(t.database)}:t,c=new WeakMap,S=async t=>{const{options:n}=t,s=c.get(n);if(s){await s;return}const e=(async()=>{const{runMigrations:o}=await l(u(n));await o()})();c.set(n,e);try{await e}catch(o){throw c.delete(n),o}},U=async t=>{const{compileMigrations:n}=await l(u(I(t)));return n()};export{U as compileMigrationsSql,S as ensureMigrated};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as a}from"@lunora/errors";import{betterAuth as i}from"better-auth";import{validateSessionPolicy as c}from"./sessionPresets-C867Mlo4.mjs";const n=32,l=t=>{const e=typeof t=="string"?t.trim().length:0;return e>0&&e<n},s=t=>typeof t=="string"?t.toLowerCase().startsWith("http://"):t&&typeof t=="object"?t.protocol==="http"?!0:t.protocol==="https"?!1:typeof t.fallback=="string"&&t.fallback.toLowerCase().startsWith("http://"):!1,u=new Set(["127.0.0.1","localhost"]),d=t=>{if(typeof t!="string")return;let e;try{e=new URL(t)}catch{return}if(!(e.protocol!=="http:"||!u.has(e.hostname)))return{allowedHosts:[e.hostname,`${e.hostname}:*`],fallback:t,protocol:"http"}},h=t=>{const e=d(t.baseURL)??t.baseURL;if(l(t.secret)){const o=`@lunora/auth: AUTH_SECRET is only ${String(t.secret?.trim().length)} characters. Use at least ${String(n)} for a brute-force-resistant secret — generate one with \`openssl rand -hex 32\`.`;if(!s(e))throw new a("INTERNAL",o);console.warn(o)}const r=t.advanced??{};return{...t,advanced:{...r,defaultCookieAttributes:r.defaultCookieAttributes??{httpOnly:!0,path:"/",sameSite:"lax"},...r.useSecureCookies===void 0?{useSecureCookies:!s(e)}:{}},baseURL:e}},f=t=>{const e=h(t),r=e.rateLimit?.enabled===void 0,o=e.rateLimit?.storage===void 0&&e.rateLimit?.enabled!==!1;return{...e,...r||o?{rateLimit:{...e.rateLimit,...r?{enabled:!0}:{},...o?{storage:"database"}:{}}}:{},...e.session?.cookieCache===void 0?{session:{...e.session,cookieCache:{enabled:!0,maxAge:60}}}:{}}},g=t=>{if(!t.secret||t.secret.trim()==="")throw new a("INTERNAL",'@lunora/auth: `secret` is required. Set AUTH_SECRET locally in .dev.vars (`lunora env set AUTH_SECRET "$(openssl rand -hex 32)"`), and in production with `wrangler secret put AUTH_SECRET`.');return t.session&&c(t.session),i(f(t))};export{g as createAuth,f as resolveAuthOptions};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{INTERNAL_SECRET_HEADER as u,RESOLVE_SESSION_PATH as f,READ_AUDIT_PATH as p}from"./AUTH_DO_AUDIT_PATH-WJbI0Bpc.mjs";import{DEFAULT_AUTH_BASE_PATH as w}from"./DEFAULT_AUTH_BASE_PATH-C7moXiBA.mjs";const E=c=>{const{basePath:d=w,internalSecret:o,namespace:s,objectName:l="auth"}=c,i=()=>{if(s)return s.get(s.idFromName(l))},h=async(e,t,n)=>{if(!o)return;const r=i();if(!r)return;const a=await r.fetch(new Request(new URL(e,t),{body:JSON.stringify(n),headers:{"content-type":"application/json",[u]:o},method:"POST"}));return a.ok?a:void 0};return{auditReader:{read:async e=>{const t=await h(p,"https://auth-do.invalid",e);return t?(await t.json())?.entries??[]:[]}},authHandler:async e=>{if(new URL(e.url).pathname.startsWith(d))return i()?.fetch(e)},resolveIdentity:async e=>{if(!o)return null;const t=i();if(!t)return null;const n=new Headers(e.headers);n.set(u,o);const r=await t.fetch(new Request(new URL(f,e.url),{headers:n}));if(!r.ok)return null;const a=await r.json();return a?.userId?{userId:a.userId}:null}}};export{E as createDoAuthWiring};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{oauthProviderClient as u}from"@better-auth/oauth-provider/client";import{passkeyClient as m}from"@better-auth/passkey/client";import{oneTapClient as c,lastLoginMethodClient as C,deviceAuthorizationClient as p,anonymousClient as g,multiSessionClient as h,phoneNumberClient as d,usernameClient as L,adminClient as b,emailOTPClient as f,magicLinkClient as k,twoFactorClient as y,organizationClient as P}from"better-auth/client/plugins";const o={organization:P,twoFactor:y,passkey:m,magicLink:k,emailOtp:f,admin:b,username:L,phoneNumber:d,multiSession:h,anonymous:g,deviceAuthorization:p,lastLoginMethod:C,oauthProvider:u},T=(t={})=>{const n=[];for(const i of Object.keys(o))t[i]&&n.push(o[i]());return n},v=()=>globalThis.location?.origin,I=(t,n={})=>{const{baseURL:i,extraPlugins:a=[],oneTapClientId:e,plugins:r,...s}=n,l=e===void 0||e===""?[]:[c({clientId:e})];return t({...s,baseURL:i??v(),plugins:[...T(r),...l,...a]})};export{I as createLunoraAuthClient,T as lunoraAuthPlugins};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as n}from"@lunora/errors";import{APIError as u}from"better-auth/api";import{assertEmailAllowed as E}from"../email-guard.mjs";const m=e=>{switch(e){case 400:return"BAD_REQUEST";case 422:return"UNPROCESSABLE_ENTITY";case 429:return"TOO_MANY_REQUESTS";default:return"INTERNAL_SERVER_ERROR"}},i=e=>async(t,s)=>{const r=typeof t.email=="string"?t.email:void 0;if(r===void 0||r==="")return;let o;try{o=await E(r,e)}catch(a){throw a instanceof n?new u(m(a.status),{code:a.code,message:a.message}):a}e.onClassify?.(o,t,s)},b=(e={})=>({user:{create:{before:i(e)}}}),R=(e,t={})=>{const s=i(t),r=e.databaseHooks?.user?.create?.before,o=r?async(a,c)=>(await s(a,c),r(a,c)):s;return{...e,databaseHooks:{...e.databaseHooks,user:{...e.databaseHooks?.user,create:{...e.databaseHooks?.user?.create,before:o}}}}};export{b as emailGateDatabaseHooks,R as withEmailGate};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t=n=>{const o=["expiresIn","updateAge","freshAge"];for(const s of o){const e=n[s];if(e!==void 0&&(typeof e!="number"||!Number.isFinite(e)||e<0))throw new TypeError(`@lunora/auth: \`session.${s}\` must be a non-negative, finite number of seconds`)}return n},i={longLived:{cookieCache:{enabled:!0,maxAge:60},expiresIn:2592e3,freshAge:86400,updateAge:86400},rolling:{cookieCache:{enabled:!0,maxAge:60},expiresIn:604800,freshAge:86400,updateAge:86400},strict:{cookieCache:{enabled:!1},expiresIn:3600,freshAge:300,updateAge:900}};export{i as sessionPresets,t as validateSessionPolicy};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createAuthEndpoint as c}from"better-auth/api";import{getAuthTables as g}from"better-auth/db";const l=e=>[...e].toSorted((o,i)=>o.localeCompare(i)),m=(e,o={})=>{const i=o.expose??{},a=new Set((e.plugins??[]).map(t=>t.id)),r=e.emailAndPassword?.enabled??!1,s=g(e),n=(e.plugins??[]).find(t=>t.id==="organization")?.options??{},d={allowUserToCreate:typeof n.allowUserToCreateOrganization=="function"?!0:n.allowUserToCreateOrganization!==!1,enabled:a.has("organization"),invitationLimit:n.invitationLimit,limit:n.organizationLimit,membershipLimit:n.membershipLimit,roles:!!s.organizationRole,teams:!!s.team};return{emailAndPassword:r,signUp:r&&e.emailAndPassword?.disableSignUp!==!0,...i.organization===!1?{}:{organization:d},...i.plugins===!1?{}:{plugins:l(a)},...i.socialProviders===!1?{}:{socialProviders:l(new Set([...Object.keys(e.socialProviders??{}),...o.extraProviders??[]]))}}},p=e=>c(e.path??"/ui-config",{metadata:{openapi:{description:"Public description of the enabled auth plugins and social providers.",responses:{200:{description:"The UI configuration."}}}},method:"GET"},o=>{const i=o;return Promise.resolve(i.json(m(i.context.options,e)))}),h=(e={})=>({endpoints:{getUiConfig:p(e)},id:"lunora-ui-config"});export{m as deriveUiConfig,h as uiConfig};
|
|
@@ -66,4 +66,100 @@ interface CreateLunoraAuthClientOptions {
|
|
|
66
66
|
* `createAuthClient` call and only borrow the plugin array.
|
|
67
67
|
*/
|
|
68
68
|
declare const createLunoraAuthClient: <TClient>(createAuthClient: (options: Record<string, unknown>) => TClient, options?: CreateLunoraAuthClientOptions) => TClient;
|
|
69
|
-
export {
|
|
69
|
+
export {
|
|
70
|
+
/**
|
|
71
|
+
* Client-side counterparts to the server plugins re-exported from
|
|
72
|
+
* `@lunora/auth/plugins`, surfaced under `@lunora/auth/plugins/client`.
|
|
73
|
+
*
|
|
74
|
+
* better-auth plugins come in two halves: a **server** plugin you pass to
|
|
75
|
+
* `betterAuth()` (re-exported from `@lunora/auth/plugins`) and a **client**
|
|
76
|
+
* plugin you pass to `createAuthClient()`. Both halves must be registered for a
|
|
77
|
+
* plugin's endpoints, inferred types, and client actions (e.g.
|
|
78
|
+
* `authClient.twoFactor.*`, `authClient.organization.*`, the `twoFactorRedirect`
|
|
79
|
+
* sign-in flow) to exist. This module is the client half so consumers don't have
|
|
80
|
+
* to reach into better-auth's `better-auth/client/plugins` subpath directly.
|
|
81
|
+
*
|
|
82
|
+
* # Wiring
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* import { createAuthClient } from "better-auth/client"; // or a framework adapter
|
|
86
|
+
* import { organizationClient, twoFactorClient } from "@lunora/auth/plugins/client";
|
|
87
|
+
*
|
|
88
|
+
* export const authClient = createAuthClient({
|
|
89
|
+
* baseURL: import.meta.env.VITE_AUTH_URL,
|
|
90
|
+
* plugins: [
|
|
91
|
+
* organizationClient(),
|
|
92
|
+
* twoFactorClient({ onTwoFactorRedirect: () => location.assign("/2fa") }),
|
|
93
|
+
* ],
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* Keep the client plugin list in parity with the server `plugins` array passed
|
|
98
|
+
* to `createAuth`: a server plugin without its client counterpart leaves
|
|
99
|
+
* those actions untyped/unavailable, and vice-versa.
|
|
100
|
+
*/
|
|
101
|
+
type CreateLunoraAuthClientOptions,
|
|
102
|
+
/**
|
|
103
|
+
* Client-side counterparts to the server plugins re-exported from
|
|
104
|
+
* `@lunora/auth/plugins`, surfaced under `@lunora/auth/plugins/client`.
|
|
105
|
+
*
|
|
106
|
+
* better-auth plugins come in two halves: a **server** plugin you pass to
|
|
107
|
+
* `betterAuth()` (re-exported from `@lunora/auth/plugins`) and a **client**
|
|
108
|
+
* plugin you pass to `createAuthClient()`. Both halves must be registered for a
|
|
109
|
+
* plugin's endpoints, inferred types, and client actions (e.g.
|
|
110
|
+
* `authClient.twoFactor.*`, `authClient.organization.*`, the `twoFactorRedirect`
|
|
111
|
+
* sign-in flow) to exist. This module is the client half so consumers don't have
|
|
112
|
+
* to reach into better-auth's `better-auth/client/plugins` subpath directly.
|
|
113
|
+
*
|
|
114
|
+
* # Wiring
|
|
115
|
+
*
|
|
116
|
+
* ```ts
|
|
117
|
+
* import { createAuthClient } from "better-auth/client"; // or a framework adapter
|
|
118
|
+
* import { organizationClient, twoFactorClient } from "@lunora/auth/plugins/client";
|
|
119
|
+
*
|
|
120
|
+
* export const authClient = createAuthClient({
|
|
121
|
+
* baseURL: import.meta.env.VITE_AUTH_URL,
|
|
122
|
+
* plugins: [
|
|
123
|
+
* organizationClient(),
|
|
124
|
+
* twoFactorClient({ onTwoFactorRedirect: () => location.assign("/2fa") }),
|
|
125
|
+
* ],
|
|
126
|
+
* });
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* Keep the client plugin list in parity with the server `plugins` array passed
|
|
130
|
+
* to `createAuth`: a server plugin without its client counterpart leaves
|
|
131
|
+
* those actions untyped/unavailable, and vice-versa.
|
|
132
|
+
*/
|
|
133
|
+
type LunoraAuthClientPlugin,
|
|
134
|
+
/**
|
|
135
|
+
* Client-side counterparts to the server plugins re-exported from
|
|
136
|
+
* `@lunora/auth/plugins`, surfaced under `@lunora/auth/plugins/client`.
|
|
137
|
+
*
|
|
138
|
+
* better-auth plugins come in two halves: a **server** plugin you pass to
|
|
139
|
+
* `betterAuth()` (re-exported from `@lunora/auth/plugins`) and a **client**
|
|
140
|
+
* plugin you pass to `createAuthClient()`. Both halves must be registered for a
|
|
141
|
+
* plugin's endpoints, inferred types, and client actions (e.g.
|
|
142
|
+
* `authClient.twoFactor.*`, `authClient.organization.*`, the `twoFactorRedirect`
|
|
143
|
+
* sign-in flow) to exist. This module is the client half so consumers don't have
|
|
144
|
+
* to reach into better-auth's `better-auth/client/plugins` subpath directly.
|
|
145
|
+
*
|
|
146
|
+
* # Wiring
|
|
147
|
+
*
|
|
148
|
+
* ```ts
|
|
149
|
+
* import { createAuthClient } from "better-auth/client"; // or a framework adapter
|
|
150
|
+
* import { organizationClient, twoFactorClient } from "@lunora/auth/plugins/client";
|
|
151
|
+
*
|
|
152
|
+
* export const authClient = createAuthClient({
|
|
153
|
+
* baseURL: import.meta.env.VITE_AUTH_URL,
|
|
154
|
+
* plugins: [
|
|
155
|
+
* organizationClient(),
|
|
156
|
+
* twoFactorClient({ onTwoFactorRedirect: () => location.assign("/2fa") }),
|
|
157
|
+
* ],
|
|
158
|
+
* });
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* Keep the client plugin list in parity with the server `plugins` array passed
|
|
162
|
+
* to `createAuth`: a server plugin without its client counterpart leaves
|
|
163
|
+
* those actions untyped/unavailable, and vice-versa.
|
|
164
|
+
*/
|
|
165
|
+
type LunoraAuthPluginToggles, createLunoraAuthClient, lunoraAuthPlugins };
|
package/dist/plugins-client.d.ts
CHANGED
|
@@ -66,4 +66,100 @@ interface CreateLunoraAuthClientOptions {
|
|
|
66
66
|
* `createAuthClient` call and only borrow the plugin array.
|
|
67
67
|
*/
|
|
68
68
|
declare const createLunoraAuthClient: <TClient>(createAuthClient: (options: Record<string, unknown>) => TClient, options?: CreateLunoraAuthClientOptions) => TClient;
|
|
69
|
-
export {
|
|
69
|
+
export {
|
|
70
|
+
/**
|
|
71
|
+
* Client-side counterparts to the server plugins re-exported from
|
|
72
|
+
* `@lunora/auth/plugins`, surfaced under `@lunora/auth/plugins/client`.
|
|
73
|
+
*
|
|
74
|
+
* better-auth plugins come in two halves: a **server** plugin you pass to
|
|
75
|
+
* `betterAuth()` (re-exported from `@lunora/auth/plugins`) and a **client**
|
|
76
|
+
* plugin you pass to `createAuthClient()`. Both halves must be registered for a
|
|
77
|
+
* plugin's endpoints, inferred types, and client actions (e.g.
|
|
78
|
+
* `authClient.twoFactor.*`, `authClient.organization.*`, the `twoFactorRedirect`
|
|
79
|
+
* sign-in flow) to exist. This module is the client half so consumers don't have
|
|
80
|
+
* to reach into better-auth's `better-auth/client/plugins` subpath directly.
|
|
81
|
+
*
|
|
82
|
+
* # Wiring
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* import { createAuthClient } from "better-auth/client"; // or a framework adapter
|
|
86
|
+
* import { organizationClient, twoFactorClient } from "@lunora/auth/plugins/client";
|
|
87
|
+
*
|
|
88
|
+
* export const authClient = createAuthClient({
|
|
89
|
+
* baseURL: import.meta.env.VITE_AUTH_URL,
|
|
90
|
+
* plugins: [
|
|
91
|
+
* organizationClient(),
|
|
92
|
+
* twoFactorClient({ onTwoFactorRedirect: () => location.assign("/2fa") }),
|
|
93
|
+
* ],
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* Keep the client plugin list in parity with the server `plugins` array passed
|
|
98
|
+
* to `createAuth`: a server plugin without its client counterpart leaves
|
|
99
|
+
* those actions untyped/unavailable, and vice-versa.
|
|
100
|
+
*/
|
|
101
|
+
type CreateLunoraAuthClientOptions,
|
|
102
|
+
/**
|
|
103
|
+
* Client-side counterparts to the server plugins re-exported from
|
|
104
|
+
* `@lunora/auth/plugins`, surfaced under `@lunora/auth/plugins/client`.
|
|
105
|
+
*
|
|
106
|
+
* better-auth plugins come in two halves: a **server** plugin you pass to
|
|
107
|
+
* `betterAuth()` (re-exported from `@lunora/auth/plugins`) and a **client**
|
|
108
|
+
* plugin you pass to `createAuthClient()`. Both halves must be registered for a
|
|
109
|
+
* plugin's endpoints, inferred types, and client actions (e.g.
|
|
110
|
+
* `authClient.twoFactor.*`, `authClient.organization.*`, the `twoFactorRedirect`
|
|
111
|
+
* sign-in flow) to exist. This module is the client half so consumers don't have
|
|
112
|
+
* to reach into better-auth's `better-auth/client/plugins` subpath directly.
|
|
113
|
+
*
|
|
114
|
+
* # Wiring
|
|
115
|
+
*
|
|
116
|
+
* ```ts
|
|
117
|
+
* import { createAuthClient } from "better-auth/client"; // or a framework adapter
|
|
118
|
+
* import { organizationClient, twoFactorClient } from "@lunora/auth/plugins/client";
|
|
119
|
+
*
|
|
120
|
+
* export const authClient = createAuthClient({
|
|
121
|
+
* baseURL: import.meta.env.VITE_AUTH_URL,
|
|
122
|
+
* plugins: [
|
|
123
|
+
* organizationClient(),
|
|
124
|
+
* twoFactorClient({ onTwoFactorRedirect: () => location.assign("/2fa") }),
|
|
125
|
+
* ],
|
|
126
|
+
* });
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* Keep the client plugin list in parity with the server `plugins` array passed
|
|
130
|
+
* to `createAuth`: a server plugin without its client counterpart leaves
|
|
131
|
+
* those actions untyped/unavailable, and vice-versa.
|
|
132
|
+
*/
|
|
133
|
+
type LunoraAuthClientPlugin,
|
|
134
|
+
/**
|
|
135
|
+
* Client-side counterparts to the server plugins re-exported from
|
|
136
|
+
* `@lunora/auth/plugins`, surfaced under `@lunora/auth/plugins/client`.
|
|
137
|
+
*
|
|
138
|
+
* better-auth plugins come in two halves: a **server** plugin you pass to
|
|
139
|
+
* `betterAuth()` (re-exported from `@lunora/auth/plugins`) and a **client**
|
|
140
|
+
* plugin you pass to `createAuthClient()`. Both halves must be registered for a
|
|
141
|
+
* plugin's endpoints, inferred types, and client actions (e.g.
|
|
142
|
+
* `authClient.twoFactor.*`, `authClient.organization.*`, the `twoFactorRedirect`
|
|
143
|
+
* sign-in flow) to exist. This module is the client half so consumers don't have
|
|
144
|
+
* to reach into better-auth's `better-auth/client/plugins` subpath directly.
|
|
145
|
+
*
|
|
146
|
+
* # Wiring
|
|
147
|
+
*
|
|
148
|
+
* ```ts
|
|
149
|
+
* import { createAuthClient } from "better-auth/client"; // or a framework adapter
|
|
150
|
+
* import { organizationClient, twoFactorClient } from "@lunora/auth/plugins/client";
|
|
151
|
+
*
|
|
152
|
+
* export const authClient = createAuthClient({
|
|
153
|
+
* baseURL: import.meta.env.VITE_AUTH_URL,
|
|
154
|
+
* plugins: [
|
|
155
|
+
* organizationClient(),
|
|
156
|
+
* twoFactorClient({ onTwoFactorRedirect: () => location.assign("/2fa") }),
|
|
157
|
+
* ],
|
|
158
|
+
* });
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* Keep the client plugin list in parity with the server `plugins` array passed
|
|
162
|
+
* to `createAuth`: a server plugin without its client counterpart leaves
|
|
163
|
+
* those actions untyped/unavailable, and vice-versa.
|
|
164
|
+
*/
|
|
165
|
+
type LunoraAuthPluginToggles, createLunoraAuthClient, lunoraAuthPlugins };
|
package/dist/plugins-client.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLunoraAuthClient as n,lunoraAuthPlugins as t}from"./packem_shared/createLunoraAuthClient-
|
|
1
|
+
import{createLunoraAuthClient as n,lunoraAuthPlugins as t}from"./packem_shared/createLunoraAuthClient-DhjhCwmM.mjs";import{oauthProviderClient as l}from"@better-auth/oauth-provider/client";import{passkeyClient as C}from"@better-auth/passkey/client";import{adminClient as s,anonymousClient as m,customSessionClient as u,deviceAuthorizationClient as d,emailOTPClient as p,inferAdditionalFields as f,inferOrgAdditionalFields as h,jwtClient as c,lastLoginMethodClient as g,magicLinkClient as A,multiSessionClient as x,oneTapClient as T,oneTimeTokenClient as k,organizationClient as w,phoneNumberClient as F,siweClient as L,twoFactorClient as P,usernameClient as v}from"better-auth/client/plugins";export{s as adminClient,m as anonymousClient,n as createLunoraAuthClient,u as customSessionClient,d as deviceAuthorizationClient,p as emailOTPClient,f as inferAdditionalFields,h as inferOrgAdditionalFields,c as jwtClient,g as lastLoginMethodClient,t as lunoraAuthPlugins,A as magicLinkClient,x as multiSessionClient,l as oauthProviderClient,T as oneTapClient,k as oneTimeTokenClient,w as organizationClient,C as passkeyClient,F as phoneNumberClient,L as siweClient,P as twoFactorClient,v as usernameClient};
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { ssoClient } from '@better-auth/sso/client';
|
|
2
|
-
export type {
|
|
2
|
+
export type {
|
|
3
|
+
/**
|
|
4
|
+
* Adds `authClient.signIn.sso({ email | domain | providerId })`. Registering the
|
|
5
|
+
* server `sso` plugin without this leaves those actions unavailable and untyped.
|
|
6
|
+
* @experimental
|
|
7
|
+
*/
|
|
8
|
+
ssoClient } from '@better-auth/sso/client';
|
|
3
9
|
/**
|
|
4
10
|
* The plugin instance `ssoClient()` returns. Exported so an app can type the array it
|
|
5
11
|
* passes to `createAuthClient` when it assembles plugins conditionally.
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { ssoClient } from '@better-auth/sso/client';
|
|
2
|
-
export type {
|
|
2
|
+
export type {
|
|
3
|
+
/**
|
|
4
|
+
* Adds `authClient.signIn.sso({ email | domain | providerId })`. Registering the
|
|
5
|
+
* server `sso` plugin without this leaves those actions unavailable and untyped.
|
|
6
|
+
* @experimental
|
|
7
|
+
*/
|
|
8
|
+
ssoClient } from '@better-auth/sso/client';
|
|
3
9
|
/**
|
|
4
10
|
* The plugin instance `ssoClient()` returns. Exported so an app can type the array it
|
|
5
11
|
* passes to `createAuthClient` when it assembles plugins conditionally.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import 'better-auth/api';
|
|
2
1
|
import * as z from 'zod';
|
|
3
2
|
import { DBFieldAttribute, RemoveFieldsWithReturnedFalse, FieldAttributeToObject, InferAdditionalFieldsFromPluginOptions } from 'better-auth/db';
|
|
4
3
|
import { DBTransactionAdapter, Awaitable, User, OAuth2Tokens } from 'better-auth';
|
|
5
|
-
import '@better-auth/core';
|
|
6
4
|
type DeprecatedAlgorithmBehavior = "reject" | "warn" | "allow";
|
|
7
5
|
interface AlgorithmValidationOptions {
|
|
8
6
|
onDeprecated?: DeprecatedAlgorithmBehavior;
|
|
@@ -1573,4 +1571,26 @@ declare function sso<O extends SSOOptions>(options?: O | undefined): {
|
|
|
1573
1571
|
};
|
|
1574
1572
|
options: NoInfer<O>;
|
|
1575
1573
|
};
|
|
1576
|
-
export {
|
|
1574
|
+
export {
|
|
1575
|
+
/**
|
|
1576
|
+
* The OIDC provider configuration accepted by `registerSSOProvider` — exported so a
|
|
1577
|
+
* caller can type the config it builds (from env, a tenant record, …) before handing it
|
|
1578
|
+
* over.
|
|
1579
|
+
* @experimental
|
|
1580
|
+
*/
|
|
1581
|
+
type OIDCConfig,
|
|
1582
|
+
/**
|
|
1583
|
+
* Enterprise SSO — OIDC / OAuth2 / SAML 2.0 providers registered per email domain or
|
|
1584
|
+
* per organization, with `provisionUser` / `organizationProvisioning` hooks for
|
|
1585
|
+
* just-in-time account creation.
|
|
1586
|
+
*
|
|
1587
|
+
* Two defaults are worth setting deliberately, both documented on the package's docs
|
|
1588
|
+
* page: `domainVerification` is **off**, so an unverified `domain` still routes
|
|
1589
|
+
* sign-in; and `/sso/register` is session-only, so any signed-in user may register a
|
|
1590
|
+
* provider unless you narrow `providersLimit`.
|
|
1591
|
+
*
|
|
1592
|
+
* SAML loads on workerd but its assertion-verify path (pure-JS RSA) has not been
|
|
1593
|
+
* measured against a Worker CPU budget — treat OIDC/OAuth2 as the supported mode.
|
|
1594
|
+
* @experimental
|
|
1595
|
+
*/
|
|
1596
|
+
sso };
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import 'better-auth/api';
|
|
2
1
|
import * as z from 'zod';
|
|
3
2
|
import { DBFieldAttribute, RemoveFieldsWithReturnedFalse, FieldAttributeToObject, InferAdditionalFieldsFromPluginOptions } from 'better-auth/db';
|
|
4
3
|
import { DBTransactionAdapter, Awaitable, User, OAuth2Tokens } from 'better-auth';
|
|
5
|
-
import '@better-auth/core';
|
|
6
4
|
type DeprecatedAlgorithmBehavior = "reject" | "warn" | "allow";
|
|
7
5
|
interface AlgorithmValidationOptions {
|
|
8
6
|
onDeprecated?: DeprecatedAlgorithmBehavior;
|
|
@@ -1573,4 +1571,26 @@ declare function sso<O extends SSOOptions>(options?: O | undefined): {
|
|
|
1573
1571
|
};
|
|
1574
1572
|
options: NoInfer<O>;
|
|
1575
1573
|
};
|
|
1576
|
-
export {
|
|
1574
|
+
export {
|
|
1575
|
+
/**
|
|
1576
|
+
* The OIDC provider configuration accepted by `registerSSOProvider` — exported so a
|
|
1577
|
+
* caller can type the config it builds (from env, a tenant record, …) before handing it
|
|
1578
|
+
* over.
|
|
1579
|
+
* @experimental
|
|
1580
|
+
*/
|
|
1581
|
+
type OIDCConfig,
|
|
1582
|
+
/**
|
|
1583
|
+
* Enterprise SSO — OIDC / OAuth2 / SAML 2.0 providers registered per email domain or
|
|
1584
|
+
* per organization, with `provisionUser` / `organizationProvisioning` hooks for
|
|
1585
|
+
* just-in-time account creation.
|
|
1586
|
+
*
|
|
1587
|
+
* Two defaults are worth setting deliberately, both documented on the package's docs
|
|
1588
|
+
* page: `domainVerification` is **off**, so an unverified `domain` still routes
|
|
1589
|
+
* sign-in; and `/sso/register` is session-only, so any signed-in user may register a
|
|
1590
|
+
* provider unless you narrow `providersLimit`.
|
|
1591
|
+
*
|
|
1592
|
+
* SAML loads on workerd but its assertion-verify path (pure-JS RSA) has not been
|
|
1593
|
+
* measured against a Worker CPU budget — treat OIDC/OAuth2 as the supported mode.
|
|
1594
|
+
* @experimental
|
|
1595
|
+
*/
|
|
1596
|
+
sso };
|
package/dist/plugins.d.mts
CHANGED
|
@@ -100,4 +100,22 @@ interface UiConfigOptions {
|
|
|
100
100
|
* consumes anyway.
|
|
101
101
|
*/
|
|
102
102
|
declare const uiConfig: (options?: UiConfigOptions) => BetterAuthPlugin;
|
|
103
|
-
export {
|
|
103
|
+
export {
|
|
104
|
+
/**
|
|
105
|
+
* Publish which plugins and social providers this deployment enabled, at
|
|
106
|
+
* `GET {basePath}/ui-config`, so an auth UI configures itself instead of making
|
|
107
|
+
* you restate the list client-side. Lunora's own, not a better-auth re-export.
|
|
108
|
+
*/
|
|
109
|
+
type UiConfigOptions,
|
|
110
|
+
/**
|
|
111
|
+
* Publish which plugins and social providers this deployment enabled, at
|
|
112
|
+
* `GET {basePath}/ui-config`, so an auth UI configures itself instead of making
|
|
113
|
+
* you restate the list client-side. Lunora's own, not a better-auth re-export.
|
|
114
|
+
*/
|
|
115
|
+
type UiConfigOrganization,
|
|
116
|
+
/**
|
|
117
|
+
* Publish which plugins and social providers this deployment enabled, at
|
|
118
|
+
* `GET {basePath}/ui-config`, so an auth UI configures itself instead of making
|
|
119
|
+
* you restate the list client-side. Lunora's own, not a better-auth re-export.
|
|
120
|
+
*/
|
|
121
|
+
type UiConfigPayload, uiConfig };
|
package/dist/plugins.d.ts
CHANGED
|
@@ -100,4 +100,22 @@ interface UiConfigOptions {
|
|
|
100
100
|
* consumes anyway.
|
|
101
101
|
*/
|
|
102
102
|
declare const uiConfig: (options?: UiConfigOptions) => BetterAuthPlugin;
|
|
103
|
-
export {
|
|
103
|
+
export {
|
|
104
|
+
/**
|
|
105
|
+
* Publish which plugins and social providers this deployment enabled, at
|
|
106
|
+
* `GET {basePath}/ui-config`, so an auth UI configures itself instead of making
|
|
107
|
+
* you restate the list client-side. Lunora's own, not a better-auth re-export.
|
|
108
|
+
*/
|
|
109
|
+
type UiConfigOptions,
|
|
110
|
+
/**
|
|
111
|
+
* Publish which plugins and social providers this deployment enabled, at
|
|
112
|
+
* `GET {basePath}/ui-config`, so an auth UI configures itself instead of making
|
|
113
|
+
* you restate the list client-side. Lunora's own, not a better-auth re-export.
|
|
114
|
+
*/
|
|
115
|
+
type UiConfigOrganization,
|
|
116
|
+
/**
|
|
117
|
+
* Publish which plugins and social providers this deployment enabled, at
|
|
118
|
+
* `GET {basePath}/ui-config`, so an auth UI configures itself instead of making
|
|
119
|
+
* you restate the list client-side. Lunora's own, not a better-auth re-export.
|
|
120
|
+
*/
|
|
121
|
+
type UiConfigPayload, uiConfig };
|
package/dist/plugins.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{uiConfig as e}from"./packem_shared/uiConfig-
|
|
1
|
+
import{uiConfig as e}from"./packem_shared/uiConfig-B8pN6pks.mjs";import{mcp as m,mcpHandler as p,requireMcpAuth as f}from"@better-auth/mcp";import{oauthProvider as i}from"@better-auth/oauth-provider";import{passkey as a}from"@better-auth/passkey";import{scim as s}from"@better-auth/scim";import{captcha as h}from"better-auth/plugins";import{createAccessControl as A}from"better-auth/plugins/access";import{admin as l}from"better-auth/plugins/admin";import{anonymous as P}from"better-auth/plugins/anonymous";import{bearer as v}from"better-auth/plugins/bearer";import{customSession as T}from"better-auth/plugins/custom-session";import{deviceAuthorization as z}from"better-auth/plugins/device-authorization";import{emailOTP as O}from"better-auth/plugins/email-otp";import{genericOAuth as j}from"better-auth/plugins/generic-oauth";import{haveIBeenPwned as B}from"better-auth/plugins/haveibeenpwned";import{jwt as H}from"better-auth/plugins/jwt";import{magicLink as L}from"better-auth/plugins/magic-link";import{multiSession as N}from"better-auth/plugins/multi-session";import{oAuthProxy as E}from"better-auth/plugins/oauth-proxy";import{oneTimeToken as J}from"better-auth/plugins/one-time-token";import{organization as Q}from"better-auth/plugins/organization";import{phoneNumber as U}from"better-auth/plugins/phone-number";import{siwe as W}from"better-auth/plugins/siwe";import{twoFactor as Y}from"better-auth/plugins/two-factor";import{username as _}from"better-auth/plugins/username";export{l as admin,P as anonymous,v as bearer,h as captcha,A as createAccessControl,T as customSession,z as deviceAuthorization,O as emailOTP,j as genericOAuth,B as haveIBeenPwned,H as jwt,L as magicLink,m as mcp,p as mcpHandler,N as multiSession,E as oAuthProxy,i as oauthProvider,J as oneTimeToken,Q as organization,a as passkey,U as phoneNumber,f as requireMcpAuth,s as scim,W as siwe,Y as twoFactor,e as uiConfig,_ as username};
|
package/dist/schema.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineTable as i}from"@lunora/server";import{v as e}from"@lunora/values";import{getAuthTables as
|
|
1
|
+
import{defineTable as i}from"@lunora/server";import{v as e}from"@lunora/values";import{getAuthTables as u}from"better-auth/db";const c=n=>{if(n.references)return e.id(n.references.model);const{type:r}=n;if(Array.isArray(r))return e.string();switch(r){case"boolean":return e.boolean();case"date":return e.date();case"number":return e.number();case"number[]":return e.array(e.number());case"string":return e.string();case"string[]":return e.array(e.string());default:return e.any()}},f=n=>{let r=c(n);return n.required===!1&&(r=r.nullable()),n.unique===!0&&(r=r.unique()),r},y=n=>{const r=u(n),a={};for(const t of Object.values(r)){const s={};for(const[l,o]of Object.entries(t.fields))s[o.fieldName??l]=f(o);a[t.modelName]=i(s).externallyManaged()}return a};export{y as default};
|
package/dist/sql-store.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const o=n=>`"${n.replaceAll('"','""')}"`,O={params:[],sql:"0"},E=(n,r)=>r?{column:`LOWER(${n})`,placeholder:"LOWER(?)"}:{column:n,placeholder:"?"},I=(n,r,a,t)=>{if(typeof r!="string")return O;const s=E(n,t);return a==="contains"?{params:[r],sql:`instr(${s.column}, ${s.placeholder}) > 0`}:a==="starts_with"?{params:[r],sql:`instr(${s.column}, ${s.placeholder}) = 1`}:{params:[r,r],sql:`substr(${s.column}, -length(?)) = ${s.placeholder}`}},f=(n,r,a,t)=>{if(r===null)return{params:[],sql:`${n} IS ${t?"NOT ":""}NULL`};const s=E(n,a),e=t?"<>":"=";return{params:[r],sql:`${s.column} ${e} ${s.placeholder}`}},d=(n,r,a,t)=>{const s=Array.isArray(r)?r:[];if(s.length===0)return{params:[],sql:a?"1":"0"};const e=E(n,t),c=s.map(()=>e.placeholder).join(", ");return{params:[...s],sql:`${e.column} ${a?"NOT IN":"IN"} (${c})`}},R=n=>{const r=o(n.field),{mode:a,operator:t,value:s}=n,e=a==="insensitive";switch(t){case"contains":case"ends_with":case"starts_with":return I(r,s,t,e);case"gt":return{params:[s],sql:`${r} > ?`};case"gte":return{params:[s],sql:`${r} >= ?`};case"in":return d(r,s,!1,e);case"lt":return{params:[s],sql:`${r} < ?`};case"lte":return{params:[s],sql:`${r} <= ?`};case"ne":return f(r,s,e,!0);case"not_in":return d(r,s,!0,e);default:return f(r,s,e,!1)}},$=n=>{if(n.length===0)return{params:[],sql:""};let r=R(n[0]);for(const a of n.slice(1)){const t=R(a),s=a.connector==="OR"?"OR":"AND";r={params:[...r.params,...t.params],sql:`(${r.sql} ${s} ${t.sql})`}}return r},m=n=>n.sql?` WHERE ${n.sql}`:"",N=n=>{const r=(a,t)=>{const s=$(t);return n.all(`SELECT * FROM ${o(a)}${m(s)}`,s.params)};return{consumeOne:async(a,t)=>{const s=$(t),e=o(a),[c]=await n.all(`DELETE FROM ${e} WHERE rowid IN (SELECT rowid FROM ${e}${m(s)} LIMIT 1) RETURNING *`,s.params);return c},count:async(a,t)=>{const s=$(t),[e]=await n.all(`SELECT COUNT(*) AS __count FROM ${o(a)}${m(s)}`,s.params);return Number(e?.__count??0)},create:async(a,t)=>{const s=Object.keys(t),e=s.map(()=>"?").join(", "),c=`INSERT INTO ${o(a)} (${s.map(l=>o(l)).join(", ")}) VALUES (${e})`;return await n.run(c,s.map(l=>t[l])),{...t}},incrementOne:async(a,t,s,e)=>{const c=o(a),l=Object.keys(s),p=e?Object.keys(e):[],u=$(t);if(l.length===0&&p.length===0){const[i]=await n.all(`SELECT * FROM ${c}${m(u)} LIMIT 1`,u.params);return i}const T=[...l.map(i=>`${o(i)} = COALESCE(${o(i)}, 0) + ?`),...p.map(i=>`${o(i)} = ?`)].join(", "),[h]=await n.all(`UPDATE ${c} SET ${T} WHERE rowid IN (SELECT rowid FROM ${c}${m(u)} LIMIT 1) RETURNING *`,[...l.map(i=>s[i]),...p.map(i=>e[i]),...u.params]);return h},read:async(a,t)=>{const s=$(t.where),e=[...s.params];let c=`SELECT * FROM ${o(a)}${m(s)}`;return t.sortBy&&(c+=` ORDER BY ${o(t.sortBy.field)} ${t.sortBy.direction==="asc"?"ASC":"DESC"}`),t.limit!==void 0&&(c+=" LIMIT ?",e.push(Math.trunc(t.limit))),t.offset&&(c+=`${t.limit===void 0?" LIMIT -1":""} OFFSET ?`,e.push(Math.trunc(t.offset))),n.all(c,e)},remove:async(a,t)=>{const s=$(t);return(await n.all(`DELETE FROM ${o(a)}${m(s)} RETURNING *`,s.params)).length},update:async(a,t,s)=>{const e=Object.keys(s);if(e.length===0)return r(a,t);const c=e.map(p=>`${o(p)} = ?`).join(", "),l=$(t);return n.all(`UPDATE ${o(a)} SET ${c}${m(l)} RETURNING *`,[...e.map(p=>s[p]),...l.params])}}},g=n=>({all:async(r,a)=>(await n.prepare(r).bind(...a).all()).results??[],run:async(r,a)=>{await n.prepare(r).bind(...a).run()}});export{N as createSqlAuthStore,g as d1Executor};
|
package/dist/store.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const d=i=>typeof i=="string"?i:void 0,a=i=>i==null,f=(i,o,r)=>{if(r){const s=d(i),t=d(o);if(s!==void 0&&t!==void 0)return s.toLowerCase()===t.toLowerCase()}return i===o},m=(i,o,r,s)=>{const t=d(i),e=d(o);if(t===void 0||e===void 0)return!1;const[n,c]=s?[t.toLowerCase(),e.toLowerCase()]:[t,e];return r==="contains"?n.includes(c):r==="starts_with"?n.startsWith(c):n.endsWith(c)},v=(i,o)=>{const{field:r,mode:s,operator:t,value:e}=o,n=i[r],c=s==="insensitive";switch(t){case"contains":case"ends_with":case"starts_with":return m(n,e,t,c);case"gt":return!a(e)&&n>e;case"gte":return!a(e)&&n>=e;case"in":return Array.isArray(e)&&e.some(u=>f(n,u,c));case"lt":return!a(e)&&n<e;case"lte":return!a(e)&&n<=e;case"ne":return!f(n,e,c);case"not_in":return Array.isArray(e)&&!e.some(u=>f(n,u,c));default:return a(e)?a(n):f(n,e,c)}},h=(i,o)=>{if(typeof i=="string"&&typeof o=="string")return i.localeCompare(o);const r=Number(i),s=Number(o);return r<s?-1:r>s?1:0},w=(i,o)=>{const r=o.direction==="asc"?1:-1;return i.toSorted((s,t)=>{const e=s[o.field],n=t[o.field];return a(e)&&a(n)?0:a(e)?-r:a(n)?r:h(e,n)*r})},l=(i,o)=>{let r=!0;for(const[s,t]of o.entries()){const e=v(i,t);s===0?r=e:t.connector==="OR"?r=r||e:r=r&&e}return r},p=()=>{const i=new Map,o=r=>{const s=i.get(r);if(s)return s;const t=[];return i.set(r,t),t};return{consumeOne:(r,s)=>{const t=o(r),e=t.findIndex(c=>l(c,s));if(e===-1)return Promise.resolve(void 0);const[n]=t.splice(e,1);return Promise.resolve(n?{...n}:void 0)},count:(r,s)=>Promise.resolve(o(r).filter(t=>l(t,s)).length),create:(r,s)=>{const t={...s};return o(r).push(t),Promise.resolve({...t})},incrementOne:(r,s,t,e)=>{const n=o(r).find(c=>l(c,s));if(!n)return Promise.resolve(void 0);for(const[c,u]of Object.entries(t))n[c]=(typeof n[c]=="number"?n[c]:0)+u;return e&&Object.assign(n,e),Promise.resolve({...n})},read:(r,s)=>{let t=o(r).filter(e=>l(e,s.where));return s.sortBy&&(t=w(t,s.sortBy)),s.offset&&(t=t.slice(s.offset)),s.limit!==void 0&&(t=t.slice(0,s.limit)),Promise.resolve(t.map(e=>({...e})))},remove:(r,s)=>{const t=o(r),e=t.filter(c=>!l(c,s)),n=t.length-e.length;return t.length=0,t.push(...e),Promise.resolve(n)},update:(r,s,t)=>{const e=o(r).filter(n=>l(n,s));for(const n of e)Object.assign(n,t);return Promise.resolve(e.map(n=>({...n})))}}};export{p as createMemoryAuthStore,l as matchesWhere};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as i}from"@lunora/errors";import{verifyTurnstile as
|
|
1
|
+
import{LunoraError as i}from"@lunora/errors";import{verifyTurnstile as n}from"./turnstile.mjs";const f=e=>async({ctx:a,next:l})=>{const t=e.token(a);if(t===void 0||t==="")throw new i("FORBIDDEN",e.message??"turnstile token missing");let r;try{r=await n({expectedAction:e.expectedAction,expectedHostname:e.expectedHostname,fetch:e.fetch,remoteip:e.remoteip?.(a),secret:e.secret,token:t})}catch(c){if(console.error(`@lunora/auth: verifyTurnstileMiddleware siteverify threw; ${e.failOpen?"failing open":"failing closed"}`,c),e.failOpen)return l();throw new i("FORBIDDEN",e.message??"turnstile verification unavailable",{cause:c})}if(!r.success||e.validate!==void 0&&!e.validate(r))throw new i("FORBIDDEN",e.message??"turnstile verification failed",{data:r.errorCodes.length>0?{errorCodes:r.errorCodes}:void 0});return l()};export{f as verifyTurnstileMiddleware};
|
package/dist/turnstile.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as l}from"@lunora/errors";const
|
|
1
|
+
import{LunoraError as l}from"@lunora/errors";const E="https://challenges.cloudflare.com/turnstile/v0/siteverify",a=t=>typeof t=="string"?t:void 0,m=t=>Array.isArray(t)?t.filter(e=>typeof e=="string"):[],S=async({expectedAction:t,expectedHostname:e,fetch:d=globalThis.fetch,remoteip:n,secret:y,token:w})=>{const h=new URLSearchParams({response:w,secret:y});n!==void 0&&n!==""&&h.set("remoteip",n);let o;try{o=await d(E,{body:h.toString(),headers:{"content-type":"application/x-www-form-urlencoded"},method:"POST"})}catch(c){throw new l("SERVICE_UNAVAILABLE","turnstile siteverify request failed",{cause:c,status:503})}if(!o.ok)throw new l("SERVICE_UNAVAILABLE",`turnstile siteverify returned ${String(o.status)}`,{status:503});let r;try{r=await o.json()}catch(c){throw new l("SERVICE_UNAVAILABLE","turnstile siteverify returned a non-JSON body",{cause:c,status:503})}const u=a(r.action),f=a(r.hostname),i=m(r["error-codes"]);let s=r.success===!0;return s&&e!==void 0&&f!==e&&(s=!1,i.push("hostname-mismatch")),s&&t!==void 0&&u!==t&&(s=!1,i.push("action-mismatch")),{action:u,cdata:a(r.cdata),challengeTs:a(r.challenge_ts),errorCodes:i,hostname:f,success:s}};export{E as TURNSTILE_VERIFY_ENDPOINT,S as verifyTurnstile};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/auth",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.82",
|
|
4
4
|
"description": "Auth for Lunora — a thin better-auth wrapper: email/password, OAuth, plugins, D1-backed",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -102,18 +102,18 @@
|
|
|
102
102
|
"@better-auth/oauth-provider": "1.7.0-rc.2",
|
|
103
103
|
"@better-auth/passkey": "1.7.0-rc.2",
|
|
104
104
|
"@better-auth/scim": "1.7.0-rc.2",
|
|
105
|
-
"@lunora/errors": "1.0.0-alpha.
|
|
106
|
-
"@lunora/server": "1.0.0-alpha.
|
|
107
|
-
"@lunora/values": "1.0.0-alpha.
|
|
108
|
-
"@visulima/disposable-email-domains": "1.0
|
|
109
|
-
"@visulima/email-verifier": "1.0.
|
|
110
|
-
"@visulima/free-email-domains": "1.0.
|
|
111
|
-
"@visulima/redact": "
|
|
105
|
+
"@lunora/errors": "1.0.0-alpha.22",
|
|
106
|
+
"@lunora/server": "1.0.0-alpha.74",
|
|
107
|
+
"@lunora/values": "1.0.0-alpha.27",
|
|
108
|
+
"@visulima/disposable-email-domains": "1.1.0",
|
|
109
|
+
"@visulima/email-verifier": "1.0.2",
|
|
110
|
+
"@visulima/free-email-domains": "1.0.1",
|
|
111
|
+
"@visulima/redact": "4.0.0"
|
|
112
112
|
},
|
|
113
113
|
"peerDependencies": {
|
|
114
|
-
"@better-auth/core": "1.7.0-rc.2",
|
|
115
|
-
"@better-auth/sso": "1.7.0-rc.2",
|
|
116
|
-
"better-auth": "1.7.0-rc.2"
|
|
114
|
+
"@better-auth/core": ">=1.7.0-rc.2 <2.0.0-0",
|
|
115
|
+
"@better-auth/sso": ">=1.7.0-rc.2 <2.0.0-0",
|
|
116
|
+
"better-auth": ">=1.7.0-rc.2 <2.0.0-0"
|
|
117
117
|
},
|
|
118
118
|
"peerDependenciesMeta": {
|
|
119
119
|
"@better-auth/sso": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{w as i,d as u}from"./adapter-RvDcm0Zy.mjs";import{ensureAuthAuditTable as h,createAuthAuditReader as c}from"../audit.mjs";import{resolveAuthOptions as l,createAuth as f}from"./createAuth-DRtd4q6u.mjs";import{authDoSchemaStatements as d,authDoColumnAdditions as m}from"./authDoColumnAdditions-B8BRbdzn.mjs";import{handleAuthRequest as p}from"./DEFAULT_AUTH_BASE_PATH-kIwlEt8i.mjs";const A=(o,e)=>{const t=Math.max(o.length,e.length);let r=o.length^e.length;for(let s=0;s<t;s+=1){const n=s<o.length?o.charCodeAt(s):0,a=s<e.length?e.charCodeAt(s):0;r|=n^a}return r===0},b="/__lunora/auth/session",R="/__lunora/auth/audit",S="x-lunora-auth-do-secret",g=o=>{if(o===null||typeof o!="object"||Array.isArray(o))return{error:"body must be a JSON object"};const e={};for(const[t,r]of Object.entries(o))if(t==="limit"||t==="sinceSeq"){if(typeof r!="number"||!Number.isFinite(r))return{error:`"${t}" must be a finite number`};e[t]=r}else if(t==="actorId"||t==="event"){if(typeof r!="string")return{error:`"${t}" must be a string`};e[t]=r}else return{error:`unknown audit read option "${t}"`};return{options:e}};class T{#r;#s;#t;#e;#o=!1;constructor(e,t,r={}){this.#t=e.storage,this.#s=t,this.#r=r}#n(){if(this.#e!==void 0)return this.#e;const e=this.#s();if(!this.#o){const t=l(e);for(const r of d(t))[...this.#t.sql.exec(r)];for(const r of m(t,s=>this.#i(s)))[...this.#t.sql.exec(r)];this.#o=!0}return this.#e=f({...e,database:i(this.#t)}),this.#e}#i(e){return[...this.#t.sql.exec("SELECT name FROM pragma_table_info(?)",e)].map(t=>String(t.name))}async#u(e){if(!this.#a(e))return Response.json({error:"unauthorized"},{status:401});let t;try{t=await e.json()}catch{return Response.json({error:"invalid body"},{status:400})}const r=g(t??{});if("error"in r)return Response.json({error:r.error},{status:400});const s=u(this.#t);await h(s);const n=await c(s).read(r.options);return Response.json({entries:n})}#a(e){const{internalSecret:t}=this.#r;if(t===void 0||t==="")return!1;const r=e.headers.get(S);return r!==null&&A(r,t)}async#h(e){if(!this.#a(e))return Response.json({error:"unauthorized"},{status:401});const t=(await this.#n().api.getSession({headers:e.headers}))?.user.id;return Response.json(t===void 0?{}:{userId:t})}async fetch(e){const t=new URL(e.url);if(t.pathname===b)return this.#h(e);if(t.pathname===R)return this.#u(e);const r=this.#n();return await p(r,e,this.#r.basePath)??Response.json({error:"not an auth route"},{status:404})}}export{S as INTERNAL_SECRET_HEADER,T as LunoraAuthDO,R as READ_AUDIT_PATH,b as RESOLVE_SESSION_PATH};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const s="/api/auth",r=async(h,t,n=s)=>{const a=new URL(t.url),e=n.endsWith("/")?n.slice(0,-1):n;if(!(a.pathname!==e&&!a.pathname.startsWith(`${e}/`)))return h.handler(t)};export{s as DEFAULT_AUTH_BASE_PATH,r as handleAuthRequest};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createLocalAccountIssuer as S}from"@better-auth/core/db";import{LunoraError as O}from"@lunora/errors";import{getAuthTables as A}from"better-auth/db";class p extends O{constructor(c,w){super(w,c,{name:"LunoraAuthAdminError"})}}const M=50,D=500,z=3600,R=z*24,U=100*365*24*60*60,N=2880*60*1e3,k=new Set(["accessToken","backupCodes","idToken","password","publicKey","refreshToken","secret","token"]),T=s=>Math.min(Math.max(Math.trunc(s??M),1),D),x=s=>Math.max(0,Math.trunc(s??0)),u=s=>{const c={};for(const[w,m]of Object.entries(s))k.has(w)||(c[w]=m instanceof Date?m.getTime():m);return c},I=s=>Array.isArray(s)?s.join(","):s,b=s=>s.toLowerCase().replaceAll(/[^\da-z]+/g,"-").replaceAll(/^-|-$/g,""),E=new Set(["banExpires","banned","banReason","createdAt","email","emailVerified","id","name","role","updatedAt"]),L={displayUsername:"username",phoneNumber:"phone-number",phoneNumberVerified:"phone-number",username:"username"},_=s=>s==="boolean"||s==="date"||s==="number"?s:"string",P=s=>{const c=[];for(const[w,m]of Object.entries(s))m.input===!1||m.references!==void 0||E.has(w)||c.push({name:w,plugin:L[w],required:m.required===!0,type:_(m.type),unique:m.unique===!0});return c},B=s=>{if(s instanceof p)return s;const c=s,w=c?.body?.code??c?.code??"AUTH_ADMIN_ERROR",m=c?.body?.message??c?.message??"auth admin operation failed";return new p(m,w)},j=(s,c={})=>{const w=s.$context,m=c.features??{},y=e=>{const a=new Set((e.plugins??[]).map(i=>i.id)),t=i=>a.has(i);return{accounts:m.accounts??!0,admin:m.admin??t("admin"),organization:m.organization??t("organization"),passkey:m.passkey??t("passkey"),twoFactor:m.twoFactor??t("two-factor")}},r=async e=>{try{return await e(await w)}catch(a){throw B(a)}},h=e=>u(e),g=async(e,a,t)=>{const i=t.where&&t.where.length>0?t.where:void 0,[n,o]=await Promise.all([e.adapter.findMany({limit:T(t.limit),model:a,offset:x(t.offset),sortBy:t.sortBy,where:i}),e.adapter.count({model:a,where:i})]);return{rows:n.map(d=>u(d)),total:o}};return{banUser:({expiresInSeconds:e,reason:a,userId:t})=>r(async i=>{let n=null;if(e!==void 0){if(!Number.isInteger(e)||e<=0)throw new p("expiresInSeconds must be a positive finite integer","INVALID_BAN_SECONDS");const d=Math.min(e,U);n=new Date(Date.now()+d*1e3)}const o=await i.internalAdapter.updateUser(t,{banExpires:n,banned:!0,banReason:a??"No reason"});return await i.internalAdapter.deleteUserSessions(t),h(o)}),cancelInvitation:({invitationId:e})=>r(async a=>{await a.adapter.delete({model:"invitation",where:[{field:"id",value:e}]})}),capabilities:()=>r(e=>Promise.resolve(y(e.options))),addMember:({organizationId:e,role:a,userId:t})=>r(async i=>{const n=await i.adapter.create({data:{createdAt:new Date,organizationId:e,role:a===void 0||a===""?"member":a,userId:t},model:"member"});return u(n)}),addTeamMember:({teamId:e,userId:a})=>r(async t=>{const i=await t.adapter.create({data:{createdAt:new Date,teamId:e,userId:a},model:"teamMember"});return u(i)}),config:()=>r(e=>{const a=e.options,t=y(a),i=new Set((a.plugins??[]).map(l=>l.id)),n=A(a),o=a.session??{},d=a.rateLimit??{};return Promise.resolve({capabilities:t,emailAndPassword:a.emailAndPassword?.enabled??!1,organization:{enabled:t.organization,roles:!!n.organizationRole,teams:!!n.team},plugins:[...i].toSorted((l,f)=>l.localeCompare(f)),rateLimit:{enabled:d.enabled??!1,max:d.max,window:d.window},session:{cookieCache:o.cookieCache?.enabled,expiresIn:o.expiresIn,freshAge:o.freshAge,updateAge:o.updateAge},socialProviders:Object.keys(a.socialProviders??{}).toSorted((l,f)=>l.localeCompare(f)),userFields:P(n.user?.fields??{})})}),createOrganization:({logo:e,metadata:a,name:t,ownerId:i,slug:n})=>r(async o=>{const d=b(n!==void 0&&n!==""?n:t);if(d==="")throw new p("could not derive a slug from the organization name","ORG_SLUG_INVALID");if(await o.adapter.findOne({model:"organization",where:[{field:"slug",value:d}]}))throw new p("an organization with this slug already exists","ORG_SLUG_TAKEN");const l=await o.adapter.create({data:{createdAt:new Date,logo:e===void 0||e===""?void 0:e,metadata:a===void 0?void 0:JSON.stringify(a),name:t,slug:d},model:"organization"});return i!==void 0&&i!==""&&await o.adapter.create({data:{createdAt:new Date,organizationId:l.id,role:"owner",userId:i},model:"member"}),u(l)}),createOrgRole:({organizationId:e,permission:a,role:t})=>r(async i=>{const n=await i.adapter.create({data:{createdAt:new Date,organizationId:e,permission:JSON.stringify(a),role:t},model:"organizationRole"});return u(n)}),createTeam:({name:e,organizationId:a})=>r(async t=>{const i=await t.adapter.create({data:{createdAt:new Date,name:e,organizationId:a},model:"team"});return u(i)}),deleteOrganization:({organizationId:e})=>r(async a=>{const t=A(a.options);if(await a.adapter.deleteMany({model:"member",where:[{field:"organizationId",value:e}]}),await a.adapter.deleteMany({model:"invitation",where:[{field:"organizationId",value:e}]}),t.team){const i=await a.adapter.findMany({model:"team",where:[{field:"organizationId",value:e}]});for(const n of i)await a.adapter.deleteMany({model:"teamMember",where:[{field:"teamId",value:n.id}]});await a.adapter.deleteMany({model:"team",where:[{field:"organizationId",value:e}]})}t.organizationRole&&await a.adapter.deleteMany({model:"organizationRole",where:[{field:"organizationId",value:e}]}),await a.adapter.delete({model:"organization",where:[{field:"id",value:e}]})}),deleteOrgRole:({roleId:e})=>r(async a=>{await a.adapter.delete({model:"organizationRole",where:[{field:"id",value:e}]})}),inviteMember:({email:e,inviterId:a,organizationId:t,role:i})=>r(async n=>{let o=a;if(o===void 0||o===""){const l=await n.adapter.findMany({model:"member",where:[{field:"organizationId",value:t}]});o=(l.find(f=>typeof f.role=="string"&&f.role.includes("owner"))??l[0])?.userId}if(o===void 0||o==="")throw new p("provide an inviter — the organization has no members to attribute the invitation to","INVITER_REQUIRED");const d=await n.adapter.create({data:{createdAt:new Date,email:e.toLowerCase(),expiresAt:new Date(Date.now()+N),inviterId:o,organizationId:t,role:i===void 0||i===""?"member":i,status:"pending"},model:"invitation"});return u(d)}),listOrgRoles:({limit:e,offset:a,organizationId:t})=>r(i=>g(i,"organizationRole",{limit:e,offset:a,sortBy:{direction:"desc",field:"createdAt"},where:[{field:"organizationId",value:t}]})),listTeamMembers:({limit:e,offset:a,teamId:t})=>r(i=>g(i,"teamMember",{limit:e,offset:a,where:[{field:"teamId",value:t}]})),listTeams:({limit:e,offset:a,organizationId:t})=>r(i=>g(i,"team",{limit:e,offset:a,sortBy:{direction:"desc",field:"createdAt"},where:[{field:"organizationId",value:t}]})),removeTeam:({teamId:e})=>r(async a=>{await a.adapter.deleteMany({model:"teamMember",where:[{field:"teamId",value:e}]}),await a.adapter.delete({model:"team",where:[{field:"id",value:e}]})}),removeTeamMember:({teamMemberId:e})=>r(async a=>{await a.adapter.delete({model:"teamMember",where:[{field:"id",value:e}]})}),updateMemberRole:({memberId:e,role:a})=>r(async t=>{const i=await t.adapter.update({model:"member",update:{role:I(a)},where:[{field:"id",value:e}]});return u(i??{id:e,role:I(a)})}),updateOrganization:({logo:e,metadata:a,name:t,organizationId:i,slug:n})=>r(async o=>{const d={};if(t!==void 0&&(d.name=t),n!==void 0&&n!==""&&(d.slug=b(n)),e!==void 0&&(d.logo=e===""?void 0:e),a!==void 0&&(d.metadata=JSON.stringify(a)),Object.keys(d).length===0)return u({id:i});const l=await o.adapter.update({model:"organization",update:d,where:[{field:"id",value:i}]});return u(l??{id:i})}),updateOrgRole:({permission:e,roleId:a})=>r(async t=>{const i=await t.adapter.update({model:"organizationRole",update:{permission:JSON.stringify(e),updatedAt:new Date},where:[{field:"id",value:a}]});return u(i??{id:a,permission:JSON.stringify(e)})}),updateTeam:({name:e,teamId:a})=>r(async t=>{const i=await t.adapter.update({model:"team",update:{name:e,updatedAt:new Date},where:[{field:"id",value:a}]});return u(i??{id:a,name:e})}),createUser:({data:e,email:a,name:t,password:i,role:n})=>r(async o=>{const d=a.toLowerCase();if(await o.internalAdapter.findUserByEmail(d))throw new p("a user with this email already exists","USER_ALREADY_EXISTS");const l=await o.internalAdapter.createUser({email:d,name:t,role:n===void 0?void 0:I(n),...e},{method:"admin"});if(i!==void 0&&i!==""){const f=await o.password.hash(i);await o.internalAdapter.linkAccount({issuer:S("credential"),password:f,providerAccountId:l.id,providerId:"credential",userId:l.id})}return h(l)}),deletePasskey:({passkeyId:e})=>r(async a=>{await a.adapter.delete({model:"passkey",where:[{field:"id",value:e}]})}),disableTwoFactor:({userId:e})=>r(async a=>{await a.adapter.deleteMany({model:"twoFactor",where:[{field:"userId",value:e}]}),await a.internalAdapter.updateUser(e,{twoFactorEnabled:!1})}),impersonateUser:({userId:e})=>r(async a=>{const t=await a.internalAdapter.findUserById(e);if(!t)throw new p("user not found","USER_NOT_FOUND");const i=c.impersonationSeconds;let n=z;if(i!==void 0){if(!Number.isInteger(i)||!Number.isFinite(i)||i<=0)throw new p("impersonationSeconds must be a positive finite integer","INVALID_IMPERSONATION_SECONDS");n=Math.min(i,R)}const o=new Date(Date.now()+n*1e3),d=await a.internalAdapter.createSession(e,!0,{expiresAt:o,impersonatedBy:c.impersonatedBy??e},!0);return{expiresAt:d.expiresAt instanceof Date?d.expiresAt.getTime():o.getTime(),token:d.token,user:h(t)}}),listAccounts:({userId:e})=>r(async a=>(await a.adapter.findMany({model:"account",where:[{field:"userId",value:e}]})).map(t=>u(t))),listInvitations:({limit:e,offset:a,organizationId:t})=>r(i=>g(i,"invitation",{limit:e,offset:a,where:[{field:"organizationId",value:t}]})),listMembers:({limit:e,offset:a,organizationId:t})=>r(i=>g(i,"member",{limit:e,offset:a,sortBy:{direction:"desc",field:"createdAt"},where:[{field:"organizationId",value:t}]})),listOrganizations:({limit:e,offset:a})=>r(t=>g(t,"organization",{limit:e,offset:a,sortBy:{direction:"desc",field:"createdAt"}})),listPasskeys:({userId:e})=>r(async a=>(await a.adapter.findMany({model:"passkey",where:[{field:"userId",value:e}]})).map(t=>u(t))),listSessions:({limit:e,offset:a,userId:t})=>r(i=>g(i,"session",{limit:e,offset:a,sortBy:{direction:"desc",field:"createdAt"},where:t===void 0||t===""?void 0:[{field:"userId",value:t}]})),listUsers:({filterField:e,filterValue:a,limit:t,offset:i,search:n,searchField:o,sortBy:d,sortDirection:l})=>r(f=>{const v=[];return n!==void 0&&n!==""&&v.push({field:o??"email",operator:"contains",value:n}),a!==void 0&&v.push({field:e??"email",operator:"eq",value:a}),g(f,"user",{limit:t,offset:i,sortBy:{direction:l??"desc",field:d??"createdAt"},where:v})}),removeMember:({memberId:e})=>r(async a=>{await a.adapter.delete({model:"member",where:[{field:"id",value:e}]})}),removeUser:({userId:e})=>r(async a=>{await a.internalAdapter.deleteUserSessions(e),await a.internalAdapter.deleteUser(e)}),revokeUserSession:({sessionId:e})=>r(async a=>{const t=await a.adapter.findOne({model:"session",where:[{field:"id",value:e}]});t?.token&&await a.internalAdapter.deleteSession(t.token)}),revokeUserSessions:({userId:e})=>r(async a=>{await a.internalAdapter.deleteUserSessions(e)}),setRole:({role:e,userId:a})=>r(async t=>{const i=await t.internalAdapter.updateUser(a,{role:I(e)});return h(i)}),setUserPassword:({newPassword:e,userId:a})=>r(async t=>{const i=t.password.config.minPasswordLength,n=t.password.config.maxPasswordLength;if(e.length<i)throw new p(`password must be at least ${i.toString()} characters`,"PASSWORD_TOO_SHORT");if(e.length>n)throw new p(`password must be at most ${n.toString()} characters`,"PASSWORD_TOO_LONG");const o=await t.password.hash(e);await t.internalAdapter.updatePassword(a,o)}),unbanUser:({userId:e})=>r(async a=>{const t=await a.internalAdapter.updateUser(e,{banExpires:null,banned:!1,banReason:null});return h(t)}),unlinkAccount:({accountId:e,userId:a})=>r(async t=>{await t.adapter.delete({model:"account",where:[{field:"id",value:e},{connector:"AND",field:"userId",value:a}]})}),updateUser:({data:e,userId:a})=>r(async t=>{const i=await t.internalAdapter.updateUser(a,e);return h(i)})}};export{p as LunoraAuthAdminError,j as createAuthAdmin};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createAdapterFactory as l}from"better-auth/adapters";import{createSqlAuthStore as c,d1Executor as m}from"../sql-store.mjs";const i=e=>({all:(a,r)=>Promise.resolve([...e.sql.exec(a,...r)]),run:(a,r)=>([...e.sql.exec(a,...r)],Promise.resolve())}),u=e=>async a=>e.transaction(a),p=e=>e,o=e=>e??null,w=e=>e,y=e=>({consumeOne:async({model:a,where:r})=>o(await e.consumeOne(a,r)),count:async({model:a,where:r})=>e.count(a,r??[]),create:async({data:a,model:r})=>p(await e.create(r,a)),delete:async({model:a,where:r})=>{await e.remove(a,r)},deleteMany:async({model:a,where:r})=>e.remove(a,r),findMany:async({limit:a,model:r,offset:n,sortBy:t,where:s})=>w(await e.read(r,{limit:a,offset:n,sortBy:t,where:s??[]})),findOne:async({model:a,where:r})=>{const[n]=await e.read(a,{limit:1,where:r});return o(n)},incrementOne:async({increment:a,model:r,set:n,where:t})=>o(await e.incrementOne(r,t,a,n)),update:async({model:a,update:r,where:n})=>{const[t]=await e.update(a,n,r);return o(t)},updateMany:async({model:a,update:r,where:n})=>(await e.update(a,n,r)).length}),d=(e,a)=>{const r=t=>s=>l({adapter:()=>y(e),config:{adapterId:"lunora",adapterName:"Lunora Adapter",transaction:t(s),supportsBooleans:!1,supportsDates:!1,supportsJSON:!1,supportsNumericIds:!1}})(s),n=()=>!1;return r(a?t=>async s=>a(async()=>s(r(n)(t))):n)},O=e=>d(c(m(e))),x=e=>d(c(i(e)),u(e));export{i as d,O as l,d as s,x as w};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createAuthMiddleware as c}from"better-auth/api";import{appendAuthAuditEntry as d}from"../audit.mjs";const f=t=>{const r=t.toLowerCase(),e=i=>r===i||r.endsWith(i);if(e("/sign-up/email")||e("/sign-up"))return"sign-up";if(e("/sign-in/social")||e("/sign-in/magic-link"))return"sign-in-initiated";if(r.includes("/sign-in/")||r.includes("/callback/")||e("/magic-link/verify")||r.includes("/two-factor/verify-"))return"sign-in";if(e("/sign-out"))return"sign-out";if(e("/change-password")||e("/set-password"))return"password-change";if(e("/reset-password")||e("/request-password-reset")||e("/forget-password"))return"password-reset";if(e("/verify-email"))return"email-verification";if(r.includes("/two-factor/enable")||r.includes("/totp/enable"))return"mfa-enable";if(r.includes("/two-factor/disable")||r.includes("/totp/disable"))return"mfa-disable";if(e("/refresh-token")||e("/token"))return"token-refresh";if(e("/revoke-session")||e("/revoke-sessions")||e("/revoke-other-sessions"))return"session-revoke";if(e("/link-social"))return"account-link";if(e("/unlink-account"))return"account-unlink"},n=(t,r)=>t.headers?.get(r)??t.request?.headers.get(r)??void 0,l=(t,r)=>{const e=n(t,"cf-connecting-ip");if(e!==void 0)return e;if(r===!0)return n(t,"x-forwarded-for")?.split(",")[0]?.trim()},g=t=>{const r=t.context?.newSession??t.context?.session,e=r?.user?.id??r?.session?.userId,i=r?.user?.email;return{...e===void 0?{}:{actorId:e},...i===void 0?{}:{actorEmail:i}}},h=t=>{const r=t.context?.returned;if(r instanceof Error)return"failure";if(typeof r=="object"&&r!==null&&"status"in r){const e=Number(r.status);if(Number.isFinite(e)&&e>=400)return"failure"}return"success"},u=320,p=(t,r)=>{if(r!=="sign-in"&&r!=="sign-in-initiated")return;const e=t.body?.email??t.body?.username;if(!(typeof e!="string"||e.length===0))return e.length>u?e.slice(0,u):e},v=(t,{now:r=Date.now(),trustProxyHeaders:e}={})=>{const i=t.path===void 0?void 0:f(t.path);if(i===void 0)return;const o=l(t,e),s=n(t,"user-agent"),a=p(t,i);return{...g(t),event:i,outcome:h(t),ts:r,...o===void 0?{}:{ip:o},...a===void 0?{}:{targetEmail:a},...s===void 0?{}:{userAgent:s},detail:{path:t.path}}},m=t=>c(async r=>{try{const e=v(r,{trustProxyHeaders:t.trustProxyHeaders});if(e!==void 0){const i=await d(t.executor,e,{redactDetail:t.redactDetail,retention:t.retention});t.onRecord!==void 0&&await t.onRecord(i)}}catch(e){console.error("@lunora/auth: audit hook failed to record event",e)}}),y=(t,r)=>{const e=m(r),i=t.hooks?.after,o=i?async s=>(await i(s),e(s)):e;return{...t,hooks:{...t.hooks,after:o}}};export{m as authAuditHook,v as buildAuditEntry,f as eventForPath,y as withAuthAudit};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{getAuthTablesWithResolvedIndexes as f,getDatabaseFieldIndexName as N}from"@better-auth/core/db/internal";const s=n=>`"${n.replaceAll('"','""')}"`,c=(n,e)=>{if(n==="id"||e.references?.field==="id")return"text";const{type:t}=e;if(Array.isArray(t))return"text";switch(t){case"boolean":return"integer";case"date":return"date";case"number":return"bigint"in e&&e.bigint===!0?"bigint":"integer";default:return"text"}},b=n=>{const{defaultValue:e,type:t,unique:r}=n;if(!(r===!0&&n.required===!1)&&!(e==null||typeof e=="function")){if(t==="boolean")return e===!0?"1":"0";if(t==="number"&&typeof e=="number"&&Number.isFinite(e))return String(e);if(t==="string"&&typeof e=="string")return`'${e.replaceAll("'","''")}'`}},p=(n,e)=>{const t=[];for(const[r,o]of Object.entries(e)){const i=o.unique===!0;if(!i&&o.index!==!0)continue;const l=o.fieldName??r,u=N(n,l,i);t.push(`CREATE ${i?"UNIQUE ":""}INDEX IF NOT EXISTS ${s(u)} ON ${s(n)} (${s(l)})`)}return t},E=(n,e)=>{const t=[s(n),c(n,e)],r=b(e);return e.required!==!1&&r!==void 0&&t.push("NOT NULL"),r!==void 0&&t.push(`DEFAULT ${r}`),t.join(" ")},T=(n,e)=>{const t=[s(n),c(n,e)];return e.required!==!1&&t.push("NOT NULL"),t.join(" ")},A=n=>{const{indexesByTable:e,tables:t}=f(n),r=[],o=[];for(const i of Object.values(t)){if(i.disableMigrations===!0)continue;const l=[`${s("id")} text NOT NULL PRIMARY KEY`,...Object.entries(i.fields).map(([u,a])=>T(a.fieldName??u,a))];r.push(`CREATE TABLE IF NOT EXISTS ${s(i.modelName)} (${l.join(", ")})`),o.push(...p(i.modelName,i.fields));for(const u of e.get(i.modelName)??[]){const a=u.unique===!0?"UNIQUE ":"",d=u.columns.map(m=>s(m)).join(", ");o.push(`CREATE ${a}INDEX IF NOT EXISTS ${s(u.name)} ON ${s(i.modelName)} (${d})`)}}return[...r,...o]},$=(n,e)=>{const{tables:t}=f(n),r=[];for(const o of Object.values(t)){if(o.disableMigrations===!0)continue;const i=new Set(e(o.modelName));if(i.size!==0)for(const[l,u]of Object.entries(o.fields)){const a=u.fieldName??l;i.has(a)||r.push(`ALTER TABLE ${s(o.modelName)} ADD COLUMN ${E(a,u)}`)}}return r};export{$ as authDoColumnAdditions,A as authDoSchemaStatements};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{getMigrations as l}from"better-auth/db/migration";import{resolveAuthOptions as d}from"./createAuth-DRtd4q6u.mjs";const w=/pragma_index_list\s*\(/iu,x=/sqlite_master/iu,h=/^["`[]|["\]`]$/gu,q=/\s+/u,M=/create\s+unique\s+index/iu,_=/\bwhere\b/iu,E=t=>w.test(t)&&x.test(t),N=t=>(t.trim().split(q)[0]??"").replaceAll(h,"").trim(),P=t=>{const e=t.indexOf("(");if(e===-1)return{columns:[],tail:""};let i=0;for(let s=e;s<t.length;s+=1){const n=t[s];if(n==="(")i+=1;else if(n===")"&&(i-=1,i===0))return{columns:t.slice(e+1,s).split(",").map(a=>N(a)).filter(a=>a!==""),tail:t.slice(s+1)}}return{columns:[],tail:""}},R=async t=>{const e=t.prepare("SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index'"),{results:i}=await e.all(),s=[];for(const n of i??[]){const a=n.name,o=n.tbl_name,r=n.sql;if(typeof a!="string"||typeof o!="string")continue;if(typeof r!="string"){s.push({columnPosition:0,indexName:a,isPartial:0,isUnique:1,tableName:o});continue}const p=M.test(r)?1:0,{columns:m,tail:f}=P(r),g=_.test(f)?1:0;for(const[y,b]of m.entries())s.push({columnName:b,columnPosition:y,indexName:a,isPartial:g,isUnique:p,tableName:o})}return s},D=t=>{const e=async()=>{const s=await R(t);return{meta:{},results:s,success:!0}},i={all:e,bind:()=>i,run:e};return i},I=t=>new Proxy(t,{get(e,i,s){if(i==="prepare")return a=>E(a)?D(e):e.prepare(a);const n=Reflect.get(e,i,s);return typeof n=="function"?n.bind(e):n}}),O=t=>typeof t=="object"&&t!==null&&typeof t.prepare=="function"&&typeof t.batch=="function",u=t=>O(t.database)?{...t,database:I(t.database)}:t,c=new WeakMap,S=async t=>{const{options:e}=t,i=c.get(e);if(i){await i;return}const s=(async()=>{const{runMigrations:n}=await l(u(e));await n()})();c.set(e,s);try{await s}catch(n){throw c.delete(e),n}},U=async t=>{const{compileMigrations:e}=await l(u(d(t)));return e()};export{U as compileMigrationsSql,S as ensureMigrated};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as a}from"@lunora/errors";import{betterAuth as i}from"better-auth";import{validateSessionPolicy as l}from"./sessionPresets-DpEFjXKV.mjs";const n=32,c=e=>{const t=typeof e=="string"?e.trim().length:0;return t>0&&t<n},s=e=>typeof e=="string"?e.toLowerCase().startsWith("http://"):e&&typeof e=="object"?e.protocol==="http"?!0:e.protocol==="https"?!1:typeof e.fallback=="string"&&e.fallback.toLowerCase().startsWith("http://"):!1,h=new Set(["127.0.0.1","localhost"]),u=e=>{if(typeof e!="string")return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="http:"||!h.has(t.hostname)))return{allowedHosts:[t.hostname,`${t.hostname}:*`],fallback:e,protocol:"http"}},p=e=>{const t=u(e.baseURL)??e.baseURL;if(c(e.secret)){const o=`@lunora/auth: AUTH_SECRET is only ${String(e.secret?.trim().length)} characters. Use at least ${String(n)} for a brute-force-resistant secret — generate one with \`openssl rand -hex 32\`.`;if(!s(t))throw new a("INTERNAL",o);console.warn(o)}const r=e.advanced??{};return{...e,advanced:{...r,defaultCookieAttributes:r.defaultCookieAttributes??{httpOnly:!0,path:"/",sameSite:"lax"},...r.useSecureCookies===void 0?{useSecureCookies:!s(t)}:{}},baseURL:t}},d=e=>{const t=p(e),r=t.rateLimit?.enabled===void 0,o=t.rateLimit?.storage===void 0&&t.rateLimit?.enabled!==!1;return{...t,...r||o?{rateLimit:{...t.rateLimit,...r?{enabled:!0}:{},...o?{storage:"database"}:{}}}:{},...t.session?.cookieCache===void 0?{session:{...t.session,cookieCache:{enabled:!0,maxAge:60}}}:{}}},L=e=>{if(!e.secret||e.secret.trim()==="")throw new a("INTERNAL",'@lunora/auth: `secret` is required. Set AUTH_SECRET locally in .dev.vars (`lunora env set AUTH_SECRET "$(openssl rand -hex 32)"`), and in production with `wrangler secret put AUTH_SECRET`.');return e.session&&l(e.session),i(d(e))};export{L as createAuth,d as resolveAuthOptions};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{INTERNAL_SECRET_HEADER as u,RESOLVE_SESSION_PATH as f,READ_AUDIT_PATH as w}from"./AUTH_DO_AUDIT_PATH-C4897amZ.mjs";import{DEFAULT_AUTH_BASE_PATH as A}from"./DEFAULT_AUTH_BASE_PATH-kIwlEt8i.mjs";const m=c=>{const{basePath:d=A,internalSecret:a,namespace:i,objectName:h="auth"}=c,o=()=>{if(i)return i.get(i.idFromName(h))},l=async(t,e,s)=>{if(!a)return;const n=o();if(!n)return;const r=await n.fetch(new Request(new URL(t,e),{body:JSON.stringify(s),headers:{"content-type":"application/json",[u]:a},method:"POST"}));return r.ok?r:void 0};return{auditReader:{read:async t=>{const e=await l(w,"https://auth-do.invalid",t);return e?(await e.json())?.entries??[]:[]}},authHandler:async t=>{if(new URL(t.url).pathname.startsWith(d))return o()?.fetch(t)},resolveIdentity:async t=>{if(!a)return null;const e=o();if(!e)return null;const s=new Headers(t.headers);s.set(u,a);const n=await e.fetch(new Request(new URL(f,t.url),{headers:s}));if(!n.ok)return null;const r=await n.json();return r?.userId?{userId:r.userId}:null}}};export{m as createDoAuthWiring};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{oauthProviderClient as u}from"@better-auth/oauth-provider/client";import{passkeyClient as m}from"@better-auth/passkey/client";import{oneTapClient as C,lastLoginMethodClient as c,deviceAuthorizationClient as p,anonymousClient as g,multiSessionClient as h,phoneNumberClient as d,usernameClient as L,adminClient as b,emailOTPClient as k,magicLinkClient as f,twoFactorClient as v,organizationClient as y}from"better-auth/client/plugins";const e={organization:y,twoFactor:v,passkey:m,magicLink:f,emailOtp:k,admin:b,username:L,phoneNumber:d,multiSession:h,anonymous:g,deviceAuthorization:p,lastLoginMethod:c,oauthProvider:u},P=(t={})=>{const i=[];for(const n of Object.keys(e))t[n]&&i.push(e[n]());return i},z=()=>globalThis.location?.origin,w=(t,i={})=>{const{baseURL:n,extraPlugins:a=[],oneTapClientId:o,plugins:l,...r}=i,s=o===void 0||o===""?[]:[C({clientId:o})];return t({...r,baseURL:n??z(),plugins:[...P(l),...s,...a]})};export{w as createLunoraAuthClient,P as lunoraAuthPlugins};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as i}from"@lunora/errors";import{APIError as u}from"better-auth/api";import{assertEmailAllowed as E}from"../email-guard.mjs";const m=a=>{switch(a){case 400:return"BAD_REQUEST";case 422:return"UNPROCESSABLE_ENTITY";case 429:return"TOO_MANY_REQUESTS";default:return"INTERNAL_SERVER_ERROR"}},n=a=>async(s,t)=>{const r=typeof s.email=="string"?s.email:void 0;if(r===void 0||r==="")return;let o;try{o=await E(r,a)}catch(e){throw e instanceof i?new u(m(e.status),{code:e.code,message:e.message}):e}a.onClassify?.(o,s,t)},b=(a={})=>({user:{create:{before:n(a)}}}),R=(a,s={})=>{const t=n(s),r=a.databaseHooks?.user?.create?.before,o=r?async(e,c)=>(await t(e,c),r(e,c)):t;return{...a,databaseHooks:{...a.databaseHooks,user:{...a.databaseHooks?.user,create:{...a.databaseHooks?.user?.create,before:o}}}}};export{b as emailGateDatabaseHooks,R as withEmailGate};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const r=s=>{const o=["expiresIn","updateAge","freshAge"];for(const n of o){const e=s[n];if(e!==void 0&&(typeof e!="number"||!Number.isFinite(e)||e<0))throw new TypeError(`@lunora/auth: \`session.${n}\` must be a non-negative, finite number of seconds`)}return s},i={longLived:{cookieCache:{enabled:!0,maxAge:60},expiresIn:2592e3,freshAge:86400,updateAge:86400},rolling:{cookieCache:{enabled:!0,maxAge:60},expiresIn:604800,freshAge:86400,updateAge:86400},strict:{cookieCache:{enabled:!1},expiresIn:3600,freshAge:300,updateAge:900}};export{i as sessionPresets,r as validateSessionPolicy};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createAuthEndpoint as p}from"better-auth/api";import{getAuthTables as g}from"better-auth/db";const l=i=>[...i].toSorted((o,e)=>o.localeCompare(e)),m=(i,o={})=>{const e=o.expose??{},t=new Set((i.plugins??[]).map(n=>n.id)),s=i.emailAndPassword?.enabled??!1,r=g(i),a=(i.plugins??[]).find(n=>n.id==="organization")?.options??{},d={allowUserToCreate:typeof a.allowUserToCreateOrganization=="function"?!0:a.allowUserToCreateOrganization!==!1,enabled:t.has("organization"),invitationLimit:a.invitationLimit,limit:a.organizationLimit,membershipLimit:a.membershipLimit,roles:!!r.organizationRole,teams:!!r.team};return{emailAndPassword:s,signUp:s&&i.emailAndPassword?.disableSignUp!==!0,...e.organization===!1?{}:{organization:d},...e.plugins===!1?{}:{plugins:l(t)},...e.socialProviders===!1?{}:{socialProviders:l(new Set([...Object.keys(i.socialProviders??{}),...o.extraProviders??[]]))}}},c=i=>p(i.path??"/ui-config",{metadata:{openapi:{description:"Public description of the enabled auth plugins and social providers.",responses:{200:{description:"The UI configuration."}}}},method:"GET"},o=>{const e=o;return Promise.resolve(e.json(m(e.context.options,i)))}),h=(i={})=>({endpoints:{getUiConfig:c(i)},id:"lunora-ui-config"});export{m as deriveUiConfig,h as uiConfig};
|