@lunora/auth 1.0.0-alpha.242 → 1.0.0-alpha.244
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/README.md +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.mjs +1 -1
- package/dist/middleware.d.mts +1 -1
- package/dist/middleware.d.ts +1 -1
- package/dist/migrate.d.mts +1 -1
- package/dist/migrate.d.ts +1 -1
- package/dist/migrate.mjs +1 -1
- package/dist/packem_shared/AUTH_DO_AUDIT_PATH-CRtEqPlF.mjs +1 -0
- package/dist/packem_shared/auth-do-Dkk1jOb6.mjs +1 -0
- package/dist/packem_shared/{create-auth.d-D1CKOFWc.d.mts → create-auth.d-DNGo48je.d.mts} +4 -0
- package/dist/packem_shared/{create-auth.d-D1CKOFWc.d.ts → create-auth.d-DNGo48je.d.ts} +4 -0
- package/dist/packem_shared/createAuth-CLeBjzDR.mjs +1 -0
- package/dist/packem_shared/createDoAuthWiring-DIxLcjKQ.mjs +1 -0
- package/dist/packem_shared/{invite-only.d-CsPJWAwM.d.ts → invite-only.d-BAKOiLTW.d.ts} +1 -1
- package/dist/packem_shared/{invite-only.d-DKyHHkkQ.d.mts → invite-only.d-BDErbVxw.d.mts} +1 -1
- package/dist/packem_shared/resolveSessionIdentity-ChaUVZSy.mjs +1 -0
- package/dist/packem_shared/{worker.d-B0mER8nz.d.mts → worker.d-C5xDdT5g.d.mts} +67 -14
- package/dist/packem_shared/{worker.d-CGYer33D.d.ts → worker.d-CqKoBSlW.d.ts} +67 -14
- package/dist/plugins.d.mts +2 -2
- package/dist/plugins.d.ts +2 -2
- package/dist/schema.d.mts +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/worker.d.mts +2 -2
- package/dist/worker.d.ts +2 -2
- package/dist/worker.mjs +1 -1
- package/package.json +1 -1
- package/dist/packem_shared/AUTH_DO_AUDIT_PATH-DNM3G5GI.mjs +0 -1
- package/dist/packem_shared/auth-do-nKx0bUTe.mjs +0 -1
- package/dist/packem_shared/createAuth-B-Pq7nmN.mjs +0 -1
- package/dist/packem_shared/createDoAuthWiring-az8FcbYw.mjs +0 -1
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ export default {
|
|
|
86
86
|
};
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
The runtime resolves the inbound session and stamps `ctx.auth` on every `query` / `mutation` / `action`: `ctx.auth.userId` is the signed-in user's id (or `null` when anonymous), and `ctx.auth.getIdentity()` resolves the
|
|
89
|
+
The runtime resolves the inbound session and stamps `ctx.auth` on every `query` / `mutation` / `action`: `ctx.auth.userId` is the signed-in user's id (or `null` when anonymous), and `ctx.auth.getIdentity()` resolves the claims: `email`, `name`, `role`, `expiresAtMs`, and, with the `organization()` plugin, `orgId` / `teamId` for the session's active organization and team.
|
|
90
90
|
|
|
91
91
|
### Plugins & CAPTCHA
|
|
92
92
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { D as DoStorageLike } from "./packem_shared/adapter.d-CaPwF2v3.mjs";
|
|
2
2
|
export { l as lunoraAuthAdapter, a as lunoraD1Adapter, b as lunoraDoAdapter } from "./packem_shared/adapter.d-CaPwF2v3.mjs";
|
|
3
|
-
export { type A as AuthAccount, type a as AuthAdmin, type b as AuthAdminSession, type c as AuthAdminUser, type d as AuthCapabilities, type e as AuthConfigInfo, type f as AuthInvitation, type g as AuthJurisdiction, type h as AuthJurisdictionMove, type i as AuthMember, type j as AuthMoveResult, type k as AuthMoveTableReport, type l as AuthNamespaceLike, type m as AuthOrgRole, type n as AuthOrganization, type o as AuthPage, type p as AuthPasskey, type q as AuthSignUpInvitation, type r as AuthTeam, type s as AuthTeamMember, type t as AuthTimestamp, type u as AuthUserFieldSpec, type C as CreateAuthAdminOptions, D as DEFAULT_AUTH_BASE_PATH, type v as DoAuthWiring, type w as DoAuthWiringOptions, type I as ImpersonationResult, type L as ListUsersOptions, x as LunoraAuthAdminError, y as authDiscoveryPaths, z as authDiscoveryPathsFor, B as createAuthAdmin, E as createDoAuthWiring, F as handleAuthDiscoveryRequest, G as handleAuthRequest } from "./packem_shared/worker.d-
|
|
3
|
+
export { type A as AuthAccount, type a as AuthAdmin, type b as AuthAdminSession, type c as AuthAdminUser, type d as AuthCapabilities, type e as AuthConfigInfo, type f as AuthInvitation, type g as AuthJurisdiction, type h as AuthJurisdictionMove, type i as AuthMember, type j as AuthMoveResult, type k as AuthMoveTableReport, type l as AuthNamespaceLike, type m as AuthOrgRole, type n as AuthOrganization, type o as AuthPage, type p as AuthPasskey, type q as AuthSignUpInvitation, type r as AuthTeam, type s as AuthTeamMember, type t as AuthTimestamp, type u as AuthUserFieldSpec, type C as CreateAuthAdminOptions, D as DEFAULT_AUTH_BASE_PATH, type v as DoAuthWiring, type w as DoAuthWiringOptions, type I as ImpersonationResult, type L as ListUsersOptions, x as LunoraAuthAdminError, type S as SessionIdentity, y as authDiscoveryPaths, z as authDiscoveryPathsFor, B as createAuthAdmin, E as createDoAuthWiring, F as handleAuthDiscoveryRequest, G as handleAuthRequest, H as resolveSessionIdentity } from "./packem_shared/worker.d-C5xDdT5g.mjs";
|
|
4
4
|
import { AppendAuthAuditEntry, AppendAuthAuditOptions, AuthAuditEvent } from "./audit.mjs";
|
|
5
5
|
export { AUTH_AUDIT_TABLE, type AuthAuditEntry, type AuthAuditOutcome, type AuthAuditReader, type ReadAuthAuditOptions, appendAuthAuditEntry, createAuthAuditReader, ensureAuthAuditTable, readAuthAuditLog } from "./audit.mjs";
|
|
6
6
|
import { createAuthMiddleware } from 'better-auth/api';
|
|
7
7
|
import { SqlExecutor } from "./sql-store.mjs";
|
|
8
8
|
export { createSqlAuthStore, d1Executor } from "./sql-store.mjs";
|
|
9
|
-
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-
|
|
10
|
-
export { type L as LunoraAuth, c as createAuth, r as resolveAuthOptions } from "./packem_shared/create-auth.d-
|
|
9
|
+
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-DNGo48je.mjs";
|
|
10
|
+
export { type L as LunoraAuth, c as createAuth, r as resolveAuthOptions } from "./packem_shared/create-auth.d-DNGo48je.mjs";
|
|
11
11
|
import { BetterAuthOptions } from 'better-auth';
|
|
12
12
|
import { EmailClassification, EmailGateConfig } from "./email-guard.mjs";
|
|
13
13
|
export { type EmailClass, type EmailGateMiddlewareOptions, assertEmailAllowed, classifyEmail, emailGateMiddleware, loadEmailDomainLists } from "./email-guard.mjs";
|
|
14
|
-
export { type I as InviteOnlyOptions, type a as IssuedSignUpInvitation, type S as SignUpInvitation, c as createSignUpInvitation, l as listSignUpInvitations, p as pruneSignUpInvitations, r as revokeSignUpInvitation } from "./packem_shared/invite-only.d-
|
|
14
|
+
export { type I as InviteOnlyOptions, type a as IssuedSignUpInvitation, type S as SignUpInvitation, c as createSignUpInvitation, l as listSignUpInvitations, p as pruneSignUpInvitations, r as revokeSignUpInvitation } from "./packem_shared/invite-only.d-BDErbVxw.mjs";
|
|
15
15
|
export { type LunoraAuthApiContext, LunoraAuthHeadersError, type WithAuthPluginsMiddleware, type WithAuthPluginsOptions, withAuthPlugins } from "./middleware.mjs";
|
|
16
16
|
export { default as authTables } from "./schema.mjs";
|
|
17
17
|
export { type AuthQuery, type AuthRow, type AuthStore, type AuthWhereClause, createMemoryAuthStore, matchesWhere } from "./store.mjs";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { D as DoStorageLike } from "./packem_shared/adapter.d-CElAPJ1U.js";
|
|
2
2
|
export { l as lunoraAuthAdapter, a as lunoraD1Adapter, b as lunoraDoAdapter } from "./packem_shared/adapter.d-CElAPJ1U.js";
|
|
3
|
-
export { type A as AuthAccount, type a as AuthAdmin, type b as AuthAdminSession, type c as AuthAdminUser, type d as AuthCapabilities, type e as AuthConfigInfo, type f as AuthInvitation, type g as AuthJurisdiction, type h as AuthJurisdictionMove, type i as AuthMember, type j as AuthMoveResult, type k as AuthMoveTableReport, type l as AuthNamespaceLike, type m as AuthOrgRole, type n as AuthOrganization, type o as AuthPage, type p as AuthPasskey, type q as AuthSignUpInvitation, type r as AuthTeam, type s as AuthTeamMember, type t as AuthTimestamp, type u as AuthUserFieldSpec, type C as CreateAuthAdminOptions, D as DEFAULT_AUTH_BASE_PATH, type v as DoAuthWiring, type w as DoAuthWiringOptions, type I as ImpersonationResult, type L as ListUsersOptions, x as LunoraAuthAdminError, y as authDiscoveryPaths, z as authDiscoveryPathsFor, B as createAuthAdmin, E as createDoAuthWiring, F as handleAuthDiscoveryRequest, G as handleAuthRequest } from "./packem_shared/worker.d-
|
|
3
|
+
export { type A as AuthAccount, type a as AuthAdmin, type b as AuthAdminSession, type c as AuthAdminUser, type d as AuthCapabilities, type e as AuthConfigInfo, type f as AuthInvitation, type g as AuthJurisdiction, type h as AuthJurisdictionMove, type i as AuthMember, type j as AuthMoveResult, type k as AuthMoveTableReport, type l as AuthNamespaceLike, type m as AuthOrgRole, type n as AuthOrganization, type o as AuthPage, type p as AuthPasskey, type q as AuthSignUpInvitation, type r as AuthTeam, type s as AuthTeamMember, type t as AuthTimestamp, type u as AuthUserFieldSpec, type C as CreateAuthAdminOptions, D as DEFAULT_AUTH_BASE_PATH, type v as DoAuthWiring, type w as DoAuthWiringOptions, type I as ImpersonationResult, type L as ListUsersOptions, x as LunoraAuthAdminError, type S as SessionIdentity, y as authDiscoveryPaths, z as authDiscoveryPathsFor, B as createAuthAdmin, E as createDoAuthWiring, F as handleAuthDiscoveryRequest, G as handleAuthRequest, H as resolveSessionIdentity } from "./packem_shared/worker.d-CqKoBSlW.js";
|
|
4
4
|
import { AppendAuthAuditEntry, AppendAuthAuditOptions, AuthAuditEvent } from "./audit.js";
|
|
5
5
|
export { AUTH_AUDIT_TABLE, type AuthAuditEntry, type AuthAuditOutcome, type AuthAuditReader, type ReadAuthAuditOptions, appendAuthAuditEntry, createAuthAuditReader, ensureAuthAuditTable, readAuthAuditLog } from "./audit.js";
|
|
6
6
|
import { createAuthMiddleware } from 'better-auth/api';
|
|
7
7
|
import { SqlExecutor } from "./sql-store.js";
|
|
8
8
|
export { createSqlAuthStore, d1Executor } from "./sql-store.js";
|
|
9
|
-
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-
|
|
10
|
-
export { type L as LunoraAuth, c as createAuth, r as resolveAuthOptions } from "./packem_shared/create-auth.d-
|
|
9
|
+
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-DNGo48je.js";
|
|
10
|
+
export { type L as LunoraAuth, c as createAuth, r as resolveAuthOptions } from "./packem_shared/create-auth.d-DNGo48je.js";
|
|
11
11
|
import { BetterAuthOptions } from 'better-auth';
|
|
12
12
|
import { EmailClassification, EmailGateConfig } from "./email-guard.js";
|
|
13
13
|
export { type EmailClass, type EmailGateMiddlewareOptions, assertEmailAllowed, classifyEmail, emailGateMiddleware, loadEmailDomainLists } from "./email-guard.js";
|
|
14
|
-
export { type I as InviteOnlyOptions, type a as IssuedSignUpInvitation, type S as SignUpInvitation, c as createSignUpInvitation, l as listSignUpInvitations, p as pruneSignUpInvitations, r as revokeSignUpInvitation } from "./packem_shared/invite-only.d-
|
|
14
|
+
export { type I as InviteOnlyOptions, type a as IssuedSignUpInvitation, type S as SignUpInvitation, c as createSignUpInvitation, l as listSignUpInvitations, p as pruneSignUpInvitations, r as revokeSignUpInvitation } from "./packem_shared/invite-only.d-BAKOiLTW.js";
|
|
15
15
|
export { type LunoraAuthApiContext, LunoraAuthHeadersError, type WithAuthPluginsMiddleware, type WithAuthPluginsOptions, withAuthPlugins } from "./middleware.js";
|
|
16
16
|
export { default as authTables } from "./schema.js";
|
|
17
17
|
export { type AuthQuery, type AuthRow, type AuthStore, type AuthWhereClause, createMemoryAuthStore, matchesWhere } from "./store.js";
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as r,a as o,b as a}from"./packem_shared/adapter-CBZ17jKi.mjs";import{LunoraAuthAdminError as s,createAuthAdmin as u}from"./packem_shared/LunoraAuthAdminError-uhKG1MgQ.mjs";import{AUTH_AUDIT_TABLE as
|
|
1
|
+
import{l as r,a as o,b as a}from"./packem_shared/adapter-CBZ17jKi.mjs";import{LunoraAuthAdminError as s,createAuthAdmin as u}from"./packem_shared/LunoraAuthAdminError-uhKG1MgQ.mjs";import{AUTH_AUDIT_TABLE as A,appendAuthAuditEntry as m,createAuthAuditReader as h,ensureAuthAuditTable as l,readAuthAuditLog as p}from"./audit.mjs";import{authAuditHook as f,buildAuditEntry as x,eventForPath as E,withAuthAudit as D}from"./packem_shared/authAuditHook-DPTKdjBe.mjs";import{R as S,I as c,a as v,L as _}from"./packem_shared/auth-do-Dkk1jOb6.mjs";import{createAuth as U,resolveAuthOptions as y}from"./packem_shared/createAuth-CLeBjzDR.mjs";import{authDiscoveryPaths as P,authDiscoveryPathsFor as L,handleAuthDiscoveryRequest as g}from"./packem_shared/authDiscoveryPaths-B14lM14u.mjs";import{authDoColumnAdditions as O,authDoSchemaStatements as w}from"./packem_shared/authDoColumnAdditions-UwF3Rl41.mjs";import{createDoAuthWiring as F}from"./packem_shared/createDoAuthWiring-DIxLcjKQ.mjs";import{emailGateDatabaseHooks as k,withEmailGate as q}from"./packem_shared/emailGateDatabaseHooks-BYLUpiN_.mjs";import{assertEmailAllowed as G,classifyEmail as M,emailGateMiddleware as B,loadEmailDomainLists as W}from"./email-guard.mjs";import{default as Y}from"./packem_shared/isDevEnvironment-yLbPngwn.mjs";import{DEFAULT_AUTH_BASE_PATH as z,handleAuthRequest as J}from"./packem_shared/DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import{createSignUpInvitation as Q,listSignUpInvitations as X,pruneSignUpInvitations as Z,revokeSignUpInvitation as $}from"./packem_shared/createSignUpInvitation-DHZL9fNl.mjs";import{legacyIssuerCleanupStatements as et}from"./packem_shared/legacyIssuerCleanupStatements-D9Kf8OD1.mjs";import{LunoraAuthHeadersError as ot,withAuthPlugins as at}from"./middleware.mjs";import{default as st}from"./schema.mjs";import{sessionPresets as nt,validateSessionPolicy as At}from"./packem_shared/sessionPresets-C867Mlo4.mjs";import{resolveSessionIdentity as ht}from"./packem_shared/resolveSessionIdentity-ChaUVZSy.mjs";import{c as pt,d as dt}from"./packem_shared/sql-store-uMubPYI9.mjs";import{createMemoryAuthStore as xt,matchesWhere as Et}from"./store.mjs";import{TURNSTILE_VERIFY_ENDPOINT as Tt,verifyTurnstile as St}from"./turnstile.mjs";import{verifyTurnstileMiddleware as vt}from"./turnstile-middleware.mjs";export{A as AUTH_AUDIT_TABLE,S as AUTH_DO_AUDIT_PATH,c as AUTH_DO_SECRET_HEADER,v as AUTH_DO_SESSION_PATH,z as DEFAULT_AUTH_BASE_PATH,s as LunoraAuthAdminError,_ as LunoraAuthDO,ot as LunoraAuthHeadersError,Tt as TURNSTILE_VERIFY_ENDPOINT,m as appendAuthAuditEntry,G as assertEmailAllowed,f as authAuditHook,P as authDiscoveryPaths,L as authDiscoveryPathsFor,O as authDoColumnAdditions,w as authDoSchemaStatements,st as authTables,x as buildAuditEntry,M as classifyEmail,U as createAuth,u as createAuthAdmin,h as createAuthAuditReader,F as createDoAuthWiring,xt as createMemoryAuthStore,Q as createSignUpInvitation,pt as createSqlAuthStore,dt as d1Executor,k as emailGateDatabaseHooks,B as emailGateMiddleware,l as ensureAuthAuditTable,E as eventForPath,g as handleAuthDiscoveryRequest,J as handleAuthRequest,Y as isDevEnvironment,et as legacyIssuerCleanupStatements,X as listSignUpInvitations,W as loadEmailDomainLists,r as lunoraAuthAdapter,o as lunoraD1Adapter,a as lunoraDoAdapter,Et as matchesWhere,Z as pruneSignUpInvitations,p as readAuthAuditLog,y as resolveAuthOptions,ht as resolveSessionIdentity,$ as revokeSignUpInvitation,nt as sessionPresets,At as validateSessionPolicy,St as verifyTurnstile,vt as verifyTurnstileMiddleware,D as withAuthAudit,at as withAuthPlugins,q as withEmailGate};
|
package/dist/middleware.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LunoraError } from '@lunora/errors';
|
|
2
|
-
import { L as LunoraAuth } from "./packem_shared/create-auth.d-
|
|
2
|
+
import { L as LunoraAuth } from "./packem_shared/create-auth.d-DNGo48je.mjs";
|
|
3
3
|
import 'better-auth';
|
|
4
4
|
/**
|
|
5
5
|
* Structural mirror of `@lunora/server`'s `MiddlewareNext` — the continuation
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LunoraError } from '@lunora/errors';
|
|
2
|
-
import { L as LunoraAuth } from "./packem_shared/create-auth.d-
|
|
2
|
+
import { L as LunoraAuth } from "./packem_shared/create-auth.d-DNGo48je.js";
|
|
3
3
|
import 'better-auth';
|
|
4
4
|
/**
|
|
5
5
|
* Structural mirror of `@lunora/server`'s `MiddlewareNext` — the continuation
|
package/dist/migrate.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as LunoraAuthOptions, L as LunoraAuth } from "./packem_shared/create-auth.d-
|
|
1
|
+
import { a as LunoraAuthOptions, L as LunoraAuth } from "./packem_shared/create-auth.d-DNGo48je.mjs";
|
|
2
2
|
import 'better-auth';
|
|
3
3
|
/**
|
|
4
4
|
* Apply better-auth's required schema (`user`, `session`, `account`,
|
package/dist/migrate.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as LunoraAuthOptions, L as LunoraAuth } from "./packem_shared/create-auth.d-
|
|
1
|
+
import { a as LunoraAuthOptions, L as LunoraAuth } from "./packem_shared/create-auth.d-DNGo48je.js";
|
|
2
2
|
import 'better-auth';
|
|
3
3
|
/**
|
|
4
4
|
* Apply better-auth's required schema (`user`, `session`, `account`,
|
package/dist/migrate.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getAuthTablesWithResolvedIndexes as w,invalidateSchemaChecks as y}from"@better-auth/core/db/internal";import{LunoraError as R}from"@lunora/errors";import{betterAuth as _}from"better-auth";import{getMigrations as l}from"better-auth/db/migration";import{q as c}from"./packem_shared/quote-identifier-CGiYFBvY.mjs";import{resolveAuthOptions as A,prepareAuthOptions as M}from"./packem_shared/createAuth-
|
|
1
|
+
import{getAuthTablesWithResolvedIndexes as w,invalidateSchemaChecks as y}from"@better-auth/core/db/internal";import{LunoraError as R}from"@lunora/errors";import{betterAuth as _}from"better-auth";import{getMigrations as l}from"better-auth/db/migration";import{q as c}from"./packem_shared/quote-identifier-CGiYFBvY.mjs";import{resolveAuthOptions as A,prepareAuthOptions as M}from"./packem_shared/createAuth-CLeBjzDR.mjs";import{schemaDeclaresIssuer as x,legacyIssuerCleanupStatements as T,indexesReferencingIssuer as N}from"./packem_shared/legacyIssuerCleanupStatements-D9Kf8OD1.mjs";const C=/pragma_index_list\s*\(/iu,D=/sqlite_master/iu,S=/^["`[]|["\]`]$/gu,O=/\s+/u,P=/create\s+unique\s+index/iu,L=/\bwhere\b/iu,U=t=>C.test(t)&&D.test(t),q=t=>(t.trim().split(O)[0]??"").replaceAll(S,"").trim(),v=t=>{const e=t.indexOf("(");if(e===-1)return{columns:[],tail:""};let s=0;for(let n=e;n<t.length;n+=1){const a=t[n];if(a==="(")s+=1;else if(a===")"&&(s-=1,s===0))return{columns:t.slice(e+1,n).split(",").map(o=>q(o)).filter(o=>o!==""),tail:t.slice(n+1)}}return{columns:[],tail:""}},W=async t=>{const e=t.prepare("SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index'"),{results:s}=await e.all(),n=[];for(const a of s??[]){const o=a.name,r=a.tbl_name,i=a.sql;if(typeof o!="string"||typeof r!="string")continue;if(typeof i!="string"){n.push({columnPosition:0,indexName:o,isPartial:0,isUnique:1,tableName:r});continue}const d=P.test(i)?1:0,{columns:h,tail:b}=v(i),E=L.test(b)?1:0;for(const[g,I]of h.entries())n.push({columnName:I,columnPosition:g,indexName:o,isPartial:E,isUnique:d,tableName:r})}return n},F=t=>{const e=async()=>{const n=await W(t);return{meta:{},results:n,success:!0}},s={all:e,bind:()=>s,run:e};return s},H=t=>new Proxy(t,{get(e,s,n){if(s==="prepare")return o=>U(o)?F(e):e.prepare(o);const a=Reflect.get(e,s,n);return typeof a=="function"?a.bind(e):a}}),m=t=>typeof t=="object"&&t!==null&&typeof t.prepare=="function"&&typeof t.batch=="function",p=t=>{const{database:e}=t;if(!(e&&typeof e!="function"))throw new R("AUTH_MIGRATOR_UNSUPPORTED",e?"@lunora/auth: this auth instance's `database` is a custom adapter, which better-auth's migrator cannot drive.":"@lunora/auth: this auth instance has no `database`, so better-auth's migrator has nothing to introspect.")},f=t=>m(t.database)?{...t,database:H(t.database)}:t,$=async(t,e)=>{const s=c("t");try{return await t.prepare(`SELECT ${s}.${c("issuer")} FROM ${c(e)} AS ${s} LIMIT 0`).run(),!0}catch{return!1}},Q=async t=>{const{database:e}=t,{tables:s}=w(t),{account:n}=s;if(n!==void 0&&!x(Object.entries(n.fields).map(([a,o])=>o.fieldName??a))&&m(e)&&await $(e,n.modelName))try{const{results:a}=await e.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ?").bind(n.modelName).all();await e.batch(T(n.modelName,N(a)).map(o=>e.prepare(o)))}catch(a){console.error("@lunora/auth: could not drop the reverted `account.issuer` column; if it is still present, sign-ups will fail until it is removed.",a)}},j=t=>{const{database:e}=t;typeof e=="object"&&e!==null&&y(e)},u=new WeakMap,J=async t=>{const{options:e}=t;p(e);const s=u.get(e);if(s){await s;return}const n=(async()=>{const{runMigrations:a}=await l(f(e));await a(),await Q(e),j(e)})();u.set(e,n);try{await n}catch(a){throw u.delete(e),a}},V=async t=>{const e=A(t);p(e);const{compileMigrations:s}=await l(f(e));return s()},Z=t=>_(M(t));export{V as compileMigrationsSql,Z as createMigrationAuth,J as ensureMigrated};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{I as _,L as S,R as T,a as s}from"./auth-do-Dkk1jOb6.mjs";import"./adapter-CBZ17jKi.mjs";import"../audit.mjs";import"./createAuth-CLeBjzDR.mjs";import"./authDiscoveryPaths-B14lM14u.mjs";import"./authDoColumnAdditions-UwF3Rl41.mjs";import"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import"./legacyIssuerCleanupStatements-D9Kf8OD1.mjs";import"./resolveSessionIdentity-ChaUVZSy.mjs";export{_ as INTERNAL_SECRET_HEADER,S as LunoraAuthDO,T as READ_AUDIT_PATH,s as RESOLVE_SESSION_PATH};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getAuthTablesWithResolvedIndexes as U}from"@better-auth/core/db/internal";import{LunoraError as w,toErrorBody as Y}from"@lunora/errors";import{d as k,b as K}from"./adapter-CBZ17jKi.mjs";import{AUTH_AUDIT_TABLE as Q,ensureAuthAuditTable as z,createAuthAuditReader as Z}from"../audit.mjs";import{resolveAuthOptions as ee,createAuth as te}from"./createAuth-CLeBjzDR.mjs";import{handleAuthDiscoveryRequest as ne}from"./authDiscoveryPaths-B14lM14u.mjs";import{authDoSchemaStatements as re,authDoColumnAdditions as se}from"./authDoColumnAdditions-UwF3Rl41.mjs";import{handleAuthRequest as oe}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import{decodeWire as I,encodeWire as v}from"@lunora/wire";import{q as a}from"./quote-identifier-CGiYFBvY.mjs";import{schemaDeclaresIssuer as ie,legacyIssuerCleanupStatements as ae,indexesReferencingIssuer as ce}from"./legacyIssuerCleanupStatements-D9Kf8OD1.mjs";import{resolveSessionIdentity as ue}from"./resolveSessionIdentity-ChaUVZSy.mjs";const le=(t,e)=>{const n=Math.max(t.length,e.length);let r=t.length^e.length;for(let s=0;s<n;s+=1){const o=s<t.length?t.charCodeAt(s):0,i=s<e.length?e.charCodeAt(s):0;r|=o^i}return r===0},j=2166136261,F=16777619,C=(t,e=j)=>{let n=e;for(let r=0;r<t.length;r+=1)n^=t.charCodeAt(r),n=Math.imul(n,F);return(n>>>0).toString(16).padStart(8,"0")},fe=t=>`${C(t,j)}${C(t,F)}`,Ee="/__lunora/auth/move",T="__lunora_auth_move__",R="__lunora_auth_move_rows__",y="__lunora_auth_purged__",x=100,de=16,me=100,pe=2n**64n,H=t=>t.startsWith("sqlite_")||t.startsWith("_cf_")||t===T||t===R||t===y,P=async(t,e)=>t.transaction(async()=>Promise.resolve().then(e)),E=(t,e,...n)=>[...t.sql.exec(e,...n)],u=(t,e,...n)=>{[...t.sql.exec(e,...n)]},_=t=>E(t,"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name").map(e=>String(e.name)).filter(e=>!H(e)),q=(t,e)=>E(t,"SELECT name, pk FROM pragma_table_info(?)",e).map(n=>({name:String(n.name),pk:Number(n.pk)})),g=(t,e)=>q(t,e).map(({name:n})=>n),W=(t,e)=>{const n=q(t,e),r=n.filter(({pk:s})=>s>0).toSorted((s,o)=>s.pk-o.pk).map(({name:s})=>s);return r.length>0?r:n.map(({name:s})=>s)},B=(t,e)=>Number(E(t,`SELECT count(*) AS n FROM ${a(e)}`)[0]?.n??0),V=t=>JSON.stringify(v(t)),b=(t,e)=>BigInt(`0x${fe(V(e.map(n=>t[n]??null)))}`),G=(t,e)=>{let n=0,r=0n;for(const s of t)n+=1,r=(r+e(s))%pe;return`${String(n)}:${r.toString(16)}`},X=(t,e)=>{const n=g(t,e).toSorted((r,s)=>r.localeCompare(s));return G(t.sql.exec(`SELECT * FROM ${a(e)}`),r=>b(r,n))},he=t=>_(t).map(e=>{const n=E(t,"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?",e)[0]?.sql,r=E(t,"SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ? AND sql IS NOT NULL",e).map(s=>({name:String(s.name),sql:String(s.sql)}));return{columns:g(t,e),indexes:r,name:e,rows:B(t,e),sql:typeof n=="string"?n:""}}),M=(t,e)=>{if(typeof e!="string"||!_(t).includes(e))throw new TypeError(`unknown table ${JSON.stringify(e)}`);return e},Te=(t,e,n)=>{const r=E(t,`SELECT rowid AS "__lunora_rowid__", * FROM ${a(e)} WHERE rowid > ? ORDER BY rowid LIMIT ?`,n,x),s=r.at(-1)?.__lunora_rowid__;return{last:typeof s=="number"?s:void 0,rows:r.map(({__lunora_rowid__:o,...i})=>i)}},Se=t=>g(t,T).length>0,O=t=>Se(t)?Object.fromEntries(E(t,`SELECT tbl, after, done, pass, cols, watermark FROM ${a(T)}`).map(e=>[String(e.tbl),{after:Number(e.after),columns:JSON.parse(String(e.cols)),done:e.done===1,pass:Number(e.pass),watermark:Number(e.watermark)}])):{},we=(t,e,n)=>G(t.sql.exec(`SELECT h FROM ${a(R)} WHERE tbl = ? AND pass = ?`,e,n),r=>BigInt(`0x${String(r.h)}`)),Re=t=>{u(t,`CREATE TABLE IF NOT EXISTS ${a(T)} (tbl TEXT NOT NULL PRIMARY KEY, after INTEGER NOT NULL, done INTEGER NOT NULL, pass INTEGER NOT NULL, cols TEXT NOT NULL, watermark INTEGER NOT NULL DEFAULT 0)`),u(t,`CREATE TABLE IF NOT EXISTS ${a(R)} (tbl TEXT NOT NULL, k TEXT NOT NULL, h TEXT NOT NULL, pass INTEGER NOT NULL, PRIMARY KEY (tbl, k))`)},Ae=(t,e,n)=>{if(!_(t).includes(e))return 0;const r=W(t,e).map(s=>`u.${a(s)}`).join(", ");return Number(E(t,`SELECT count(*) AS n FROM ${a(e)} u WHERE u.rowid > ? AND NOT EXISTS (SELECT 1 FROM ${a(R)} c WHERE c.tbl = ? AND c.k = json_array(${r}))`,n,e)[0]?.n??0)},Ne=/^CREATE TABLE /iu,_e=/^CREATE (?:UNIQUE )?INDEX /iu,Oe=(t,e,n)=>{if(H(e.name))throw new TypeError(`refusing reserved table ${JSON.stringify(e.name)}`);if(!Ne.test(e.sql)||e.indexes.some(({sql:s})=>!_e.test(s)))throw new TypeError(`refusing unexpected DDL for ${JSON.stringify(e.name)}`);if(n.has(e.name)){const s=new Set(g(t,e.name));for(const o of e.columns.filter(i=>!s.has(i)))u(t,`ALTER TABLE ${a(e.name)} ADD COLUMN ${a(o)}`)}else u(t,e.sql);const r=new Set(E(t,"SELECT name FROM sqlite_master WHERE type = 'index'").map(s=>String(s.name)));for(const s of e.indexes.filter(({name:o})=>!r.has(o)))u(t,s.sql)},ge=(t,e,n)=>{Re(t);const r=Ae(t,n.userTable,O(t)[n.userTable]?.watermark??0);if(r>0&&!n.force)throw new w("AUTH_MOVE_TARGET_NOT_EMPTY",`the pinned auth object has ${String(r)} user(s) the copy did not write; copying into it would merge two user bases — pass force to copy anyway`,{data:{users:r}});const s=new Set(_(t)),o=O(t);for(const c of e){Oe(t,c,s);const f=o[c.name],d=JSON.stringify(c.columns.toSorted((m,p)=>m.localeCompare(p)));f===void 0?u(t,`INSERT INTO ${a(T)} (tbl, after, done, pass, cols) VALUES (?, 0, 0, 1, ?)`,c.name,d):u(t,`UPDATE ${a(T)} SET cols = ? WHERE tbl = ?`,d,c.name)}u(t,`UPDATE ${a(T)} SET watermark = (SELECT coalesce(max(rowid), 0) FROM ${a(n.userTable)}) WHERE tbl = ?`,n.userTable);const i=O(t);return{cursors:Object.fromEntries(e.map(({name:c})=>[c,i[c]?.after??0])),done:Object.fromEntries(e.map(({name:c})=>[c,i[c]?.done??!1])),targetRows:Object.fromEntries(e.map(({name:c})=>[c,B(t,c)]))}},J=(t,e,n,r,s)=>E(t,`SELECT ${s.map(o=>a(o)).join(", ")} FROM ${a(e)} WHERE ${n.map(o=>`${a(o)} IS ?`).join(" AND ")} LIMIT 1`,...r)[0],$=(t,e)=>{if(!e)throw new w("AUTH_MOVE_CONFLICT",`a row copied into ${t} collides with a row the pinned auth object holds on its own — nothing from this page was written; pass force to keep the pinned object's rows`,{data:{table:t}});return"conflicts"},Le=t=>t instanceof Error&&t.message.includes("UNIQUE constraint failed"),ye=(t,e)=>{if(e.prior!==void 0)return $(e.table,e.force);try{u(t,`INSERT INTO ${a(e.table)} (${e.columns.map(n=>a(n)).join(", ")}) VALUES (${e.columns.map(()=>"?").join(", ")})`,...e.columns.map(n=>e.row[n]))}catch(n){if(!Le(n))throw n;return $(e.table,e.force)}return"copied"},$e=(t,e,n)=>{const r=b(n,e.columns).toString(16);return r===e.h?"unchanged":e.prior===void 0||r!==e.prior?$(e.table,e.force):(u(t,`UPDATE ${a(e.table)} SET ${e.columns.map(s=>`${a(s)} = ?`).join(", ")} WHERE ${e.keys.map(s=>`${a(s)} IS ?`).join(" AND ")}`,...e.columns.map(s=>e.row[s]),...e.key),"updated")},be=(t,e,n,r)=>{const{force:s,keys:o,pass:i}=r,c=Object.keys(n).toSorted((S,L)=>S.localeCompare(L));if(c.length+o.length>me)throw new TypeError(`${e} has too many columns to copy`);const f=o.map(S=>n[S]??null),d=V(f),m=b(n,c).toString(16),p=E(t,`SELECT h FROM ${a(R)} WHERE tbl = ? AND k = ?`,e,d)[0]?.h,l={columns:c,force:s,h:m,key:f,keys:o,prior:typeof p=="string"?p:void 0,row:n,table:e},h=l.prior===m?void 0:J(t,e,o,f,c);let A="unchanged";return l.prior!==m&&(A=h===void 0?ye(t,l):$e(t,l,h)),u(t,`INSERT INTO ${a(R)} (tbl, k, h, pass) VALUES (?, ?, ?, ?) ON CONFLICT (tbl, k) DO UPDATE SET h = excluded.h, pass = excluded.pass`,e,d,m,i),A},Ie=(t,e,n)=>{const{counts:r,force:s,keys:o,marker:i}=n,c=E(t,`SELECT k, h FROM ${a(R)} WHERE tbl = ? AND pass < ?`,e,i.pass);for(const f of c){const d=I(JSON.parse(String(f.k))),m=J(t,e,o,d,i.columns);m!==void 0&&(b(m,i.columns).toString(16)===String(f.h)?(u(t,`DELETE FROM ${a(e)} WHERE ${o.map(p=>`${a(p)} IS ?`).join(" AND ")}`,...d),r.deleted+=1):r[$(e,s)]+=1),u(t,`DELETE FROM ${a(R)} WHERE tbl = ? AND k = ?`,e,String(f.k))}},De=async(t,e,n)=>{const r=O(t)[e];if(r===void 0)throw new TypeError(`table ${JSON.stringify(e)} was not part of this move`);return P(t,()=>{const s={conflicts:0,copied:0,deleted:0,unchanged:0,updated:0},o=W(t,e);for(const i of n.rows)s[be(t,e,i,{force:n.force,keys:o,pass:r.pass})]+=1;return n.final&&Ie(t,e,{counts:s,force:n.force,keys:o,marker:r}),u(t,`UPDATE ${a(T)} SET after = ?, done = ? WHERE tbl = ?`,n.last,n.final?1:0,e),s})},Ue=async(t,e)=>{const n=_(t);return await P(t,()=>{const r=n.filter(o=>e[o]===void 0);if(r.length>0)throw new w("AUTH_MOVE_INCOMPLETE",`refusing to purge the un-pinned auth object: ${r.join(", ")} not fully copied — run the copy to done: true first`,{data:{unfinished:r}});const s=n.filter(o=>X(t,o)!==e[o]);if(s.length>0)throw new w("AUTH_MOVE_SOURCE_CHANGED",`refusing to purge the un-pinned auth object: ${s.join(", ")} changed since it was copied — run the copy again to carry the changes, then purge`,{data:{changed:s}});for(const o of n)u(t,`DROP TABLE ${a(o)}`);u(t,`CREATE TABLE IF NOT EXISTS ${a(y)} (at INTEGER NOT NULL)`),u(t,`INSERT INTO ${a(y)} (at) VALUES (?)`,Date.now())}),{dropped:n}},ke=t=>{if(g(t,y).length>0)throw new w("AUTH_MOVE_SOURCE_PURGED","the un-pinned auth object was purged after its tables were copied; anything in it now is new, and copying it would delete the pinned object's users")},Ce=new Set(["fingerprints","manifest","page","purge"]),Me=(t,e,n)=>{e.prepare();const r=O(t);return{copied:n?Object.fromEntries(Object.entries(r).map(([s,o])=>[s,we(t,s,o.pass)])):{},markers:r,order:e.order()}},ve=(t,e)=>{for(const n of e)u(t,`UPDATE ${a(T)} SET after = 0, done = 0, pass = pass + 1 WHERE tbl = ?`,n);return{}},je=[/(?:UNIQUE|NOT NULL|CHECK|FOREIGN KEY|PRIMARY KEY) constraint failed/u,/no such (?:table|column)/u,/datatype mismatch/u,/SQLITE_[A-Z_]+/u],Fe=t=>{const e=t instanceof Error?t.message:String(t);for(const n of je){const r=n.exec(e);if(r)return r[0]}return t instanceof Error?t.name:"unknown"},xe=async(t,e,n)=>{switch(Ce.has(String(e.op))&&ke(t),e.op){case"begin":return n.prepare(),ge(t,e.tables,{force:e.force===!0,userTable:n.userTable()});case"fingerprints":return{fingerprints:Object.fromEntries(_(t).map(r=>[r,X(t,r)]))};case"manifest":return{tables:he(t)};case"page":{const r=Te(t,M(t,e.table),Number(e.after??0));return{last:r.last,rows:v(r.rows)}}case"purge":{const r=await Ue(t,e.expected??{});return n.onPurge(),r}case"rescan":return ve(t,e.tables??[]);case"status":return Me(t,n,e.fingerprints===!0);case"write":return De(t,M(t,e.table),{final:e.final===!0,force:e.force===!0,last:Number(e.last),rows:I(e.rows)});default:throw new TypeError(`unknown move op ${JSON.stringify(e.op)}`)}},He=async(t,e,n)=>{try{return await xe(t,e,n)}catch(r){if(r instanceof w)throw r;console.error("@lunora/auth: auth move step failed",r);const s=String(e.op),o=typeof e.table=="string"?e.table:void 0,i=Fe(r);throw new w("AUTH_MOVE_FAILED",`auth move ${s}${o===void 0?"":` on ${o}`} failed: ${i}`,{data:{cause:i,op:s,...o===void 0?{}:{table:o}}})}},Pe=(t,e)=>{const n=r=>{const s=e.first.indexOf(r);if(s!==-1)return s;const o=e.last.indexOf(r);return o===-1?e.first.length:e.first.length+1+o};return t.toSorted((r,s)=>n(r.name)-n(s.name)||r.name.localeCompare(s.name))},qe=(t,e)=>({conflicts:0,copied:0,deleted:0,sourceRows:t.rows,table:t.name,targetRows:e,unchanged:0,updated:0}),ot=t=>{const e=async(n,r)=>{const s=await t(n,r);if(!s.ok){let o={};try{const i=await s.json();typeof i.error=="object"&&i.error!==null&&(o=i.error)}catch{}throw new w(o.code??"AUTH_MOVE_FAILED",o.message??`auth move ${String(r.op)} on the ${n} object failed (${String(s.status)})`,{data:o.data,status:s.status})}return s.json()};return{copy:async(n={})=>{const r=n.force===!0,s=await e("target",{op:"status"}),{tables:o}=await e("source",{op:"manifest"}),i=await e("target",{force:r,op:"begin",tables:o}),c=[];let f=0;for(const l of Pe(o,s.order)){const h=qe(l,i.targetRows[l.name]??0);let A=i.cursors[l.name]??0;for(c.push(h);;){if(f>=de)return{done:!1,tables:c};f+=1;const S=await e("source",{after:A,op:"page",table:l.name}),L=I(S.rows),D=L.length<x;if(L.length===0&&i.done[l.name]===!0)break;const N=await e("target",{final:D,force:r,last:S.last??A,op:"write",rows:S.rows,table:l.name});if(h.conflicts+=N.conflicts,h.copied+=N.copied,h.deleted+=N.deleted,h.unchanged+=N.unchanged,h.updated+=N.updated,h.targetRows+=N.copied-N.deleted,A=S.last??A,D)break}}const{fingerprints:d}=await e("source",{op:"fingerprints"}),m=await e("target",{fingerprints:!0,op:"status"}),p=Object.keys(d).filter(l=>m.copied[l]!==d[l]);return p.length>0&&await e("target",{op:"rescan",tables:p}),{done:p.length===0,tables:c}},purge:async()=>{const{copied:n,markers:r}=await e("target",{fingerprints:!0,op:"status"}),s=Object.fromEntries(Object.entries(r).filter(([,o])=>o.done).map(([o])=>[o,n[o]]));return e("source",{expected:s,op:"purge"})}}},We="/__lunora/auth/session",Be="/__lunora/auth/audit",Ve="x-lunora-auth-do-secret",Ge=t=>{if(t===null||typeof t!="object"||Array.isArray(t))return{error:"body must be a JSON object"};const e={};for(const[n,r]of Object.entries(t))if(n==="limit"||n==="sinceSeq"){if(typeof r!="number"||!Number.isFinite(r))return{error:`"${n}" must be a finite number`};e[n]=r}else if(n==="actorId"||n==="event"){if(typeof r!="string")return{error:`"${n}" must be a string`};e[n]=r}else return{error:`unknown audit read option "${n}"`};return{options:e}};class it{#n;#a;#c;#e;#t;#r=!1;#s={first:["user"],last:[]};constructor(e,n,r={}){this.#e=e.storage,this.#n=k(e.storage),this.#c=n,this.#a=r}#o(){if(this.#t!==void 0)return this.#t;const e=this.#c(),n=ee(e),{tables:r}=U(n);if(this.#s={first:[r.user?.modelName??"user",r.account?.modelName??"account",r.session?.modelName??"session"],last:[Q,r.rateLimit?.modelName??"rateLimit"]},!this.#r){for(const s of re(n))[...this.#e.sql.exec(s)];for(const s of se(n,o=>this.#u(o)))[...this.#e.sql.exec(s)];this.#l(n),this.#r=!0}return this.#t=te({...e,database:K(this.#e)}),this.#t}#u(e){return[...this.#e.sql.exec("SELECT name FROM pragma_table_info(?)",e)].map(r=>String(r.name))}#l(e){const{tables:n}=U(e),{account:r}=n;if(r===void 0)return;const s=Object.entries(r.fields).map(([o,i])=>i.fieldName??o);if(!(ie(s)||!this.#u(r.modelName).includes("issuer")))try{const o=[...this.#e.sql.exec("SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ?",r.modelName)].map(i=>({name:String(i.name),sql:typeof i.sql=="string"?i.sql:void 0}));for(const i of ae(r.modelName,ce(o)))[...this.#e.sql.exec(i)]}catch(o){console.error("@lunora/auth: could not drop the reverted `account.issuer` column; if it is still present, sign-ups will fail until it is removed.",o)}}async#f(e){if(!this.#i(e))return Response.json({error:"unauthorized"},{status:401});let n;try{n=await e.json()}catch{return Response.json({error:"invalid body"},{status:400})}const r=Ge(n??{});if("error"in r)return Response.json({error:r.error},{status:400});await z(this.#n);const s=await Z(this.#n).read(r.options);return Response.json({entries:s})}async#E(e){if(e.method!=="POST"||!this.#i(e))return Response.json({error:"unauthorized"},{status:401});try{const n=await e.json(),r=await He(this.#e,n,{onPurge:()=>{this.#r=!1,this.#t=void 0,this.#n=k(this.#e)},order:()=>this.#s,prepare:()=>{this.#o()},userTable:()=>this.#s.first[0]??"user"});return Response.json(r)}catch(n){const{body:r,redacted:s,status:o}=Y(n,{encodeData:i=>i,fallbackCode:"AUTH_MOVE_FAILED",redactedMessage:"auth move failed"});return s&&console.error("@lunora/auth: auth move failed",n),Response.json({error:r},{status:o})}}#i(e){const{internalSecret:n}=this.#a;if(n===void 0||n==="")return!1;const r=e.headers.get(Ve);return r!==null&&le(r,n)}async#d(e){return this.#i(e)?Response.json(await ue(this.#o(),e.headers)??{}):Response.json({error:"unauthorized"},{status:401})}async fetch(e){const n=new URL(e.url);if(n.pathname===We)return this.#d(e);if(n.pathname===Be)return this.#f(e);if(n.pathname===Ee)return this.#E(e);const r=this.#o();return await oe(r,e)??await ne(r,e)??Response.json({error:"not an auth route"},{status:404})}}export{Ve as I,it as L,Ee as M,Be as R,We as a,ot as c};
|
|
@@ -126,6 +126,10 @@ type LunoraAuth = ReturnType<typeof betterAuth>;
|
|
|
126
126
|
* The one tradeoff — a revoked session stays valid until the cache expires —
|
|
127
127
|
* is bounded by that TTL; callers who need immediate revocation opt out with
|
|
128
128
|
* `session: { cookieCache: { enabled: false } }` (or the `strict` preset).
|
|
129
|
+
* With the organization plugin on, a small plugin re-issues that cache cookie
|
|
130
|
+
* whenever an `/organization/*` endpoint moved the caller's active organization
|
|
131
|
+
* or team (see `org-session-refresh.ts`), so a membership change is not hidden
|
|
132
|
+
* behind the cache window.
|
|
129
133
|
*
|
|
130
134
|
* Every explicit caller value is forwarded verbatim. The `rateLimit` fills all
|
|
131
135
|
* merge into a single `rateLimit` object so none clobbers the others.
|
|
@@ -126,6 +126,10 @@ type LunoraAuth = ReturnType<typeof betterAuth>;
|
|
|
126
126
|
* The one tradeoff — a revoked session stays valid until the cache expires —
|
|
127
127
|
* is bounded by that TTL; callers who need immediate revocation opt out with
|
|
128
128
|
* `session: { cookieCache: { enabled: false } }` (or the `strict` preset).
|
|
129
|
+
* With the organization plugin on, a small plugin re-issues that cache cookie
|
|
130
|
+
* whenever an `/organization/*` endpoint moved the caller's active organization
|
|
131
|
+
* or team (see `org-session-refresh.ts`), so a membership change is not hidden
|
|
132
|
+
* behind the cache window.
|
|
129
133
|
*
|
|
130
134
|
* Every explicit caller value is forwarded verbatim. The `rateLimit` fills all
|
|
131
135
|
* merge into a single `rateLimit` object so none clobbers the others.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as a}from"@lunora/errors";import{isAPIError as u,createAuthMiddleware as h,getIP as f}from"better-auth/api";import{betterAuth as m}from"better-auth/minimal";import{o as g}from"./on-cloudflare-edge-Dx6gFjBO.mjs";import{authDiscoveryPaths as p}from"./authDiscoveryPaths-B14lM14u.mjs";import{setCookieCache as A}from"better-auth/cookies";import{validateSessionPolicy as E}from"./sessionPresets-C867Mlo4.mjs";const d="lunora-org-session-refresh",w=new Set(["/organization/accept-invitation","/organization/create","/organization/delete","/organization/leave","/organization/remove-member"]),S=new Set(["/organization/get-full-organization","/organization/get-organization"]),v=()=>({hooks:{after:[{handler:h(async e=>{const t=await e.getSignedCookie(e.context.authCookies.sessionToken.name,e.context.secret);if(!(typeof t!="string"||t===""))try{const r=await e.context.internalAdapter.findSession(t);if(!r||r.session.expiresAt.getTime()<=Date.now())return;const o=await e.getSignedCookie(e.context.authCookies.dontRememberToken.name,e.context.secret);await A(e,r,!!o)}catch(r){console.error("@lunora/auth: organization session cache refresh failed",r)}}),matcher:e=>{if(e.context.options.session?.cookieCache?.enabled!==!0||e.path===void 0)return!1;const t=u(e.context.returned);return w.has(e.path)&&!t||S.has(e.path)&&t}}]},id:d}),C=e=>{const t=e.plugins??[];return!t.some(r=>r.id==="organization")||t.some(r=>r.id===d)?e:{...e,plugins:[...t,v()]}},i="cf-connecting-ip",L=e=>{const t=e!==void 0&&e.length>0;return g()?t?[i,"x-forwarded-for"]:[i]:t?["x-forwarded-for"]:[]},s="/**",T=100,_=e=>{const t=e.rateLimit?.customRules??{};return t[s]!==void 0?t:{...t,[s]:(r,o)=>f(r,e)===null?{max:o.max*T,window:o.window}:o}},l=32,R=e=>{const t=typeof e=="string"?e.trim().length:0;return t>0&&t<l},c=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,k=new Set(["127.0.0.1","localhost"]),b=e=>{if(typeof e!="string")return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="http:"||!k.has(t.hostname)))return{allowedHosts:[t.hostname,`${t.hostname}:*`],fallback:e,protocol:"http"}},O=e=>{const t=b(e.baseURL)??e.baseURL;if(R(e.secret)){const n=`@lunora/auth: AUTH_SECRET is only ${String(e.secret?.trim().length)} characters. Use at least ${String(l)} for a brute-force-resistant secret — generate one with \`openssl rand -hex 32\`.`;if(!c(t))throw new a("INTERNAL",n);console.warn(n)}const r=e.advanced??{},o=r.ipAddress??{};return{...e,advanced:{...r,defaultCookieAttributes:r.defaultCookieAttributes??{httpOnly:!0,path:"/",sameSite:"lax"},ipAddress:{...o,...o.ipAddressHeaders===void 0?{ipAddressHeaders:L(o.trustedProxies)}:{}},...r.useSecureCookies===void 0?{useSecureCookies:!c(t)}:{}},baseURL:t}},y=e=>{const t=O(e),r=t.rateLimit?.enabled===void 0,o=t.rateLimit?.storage===void 0&&t.rateLimit?.enabled!==!1,n=t.rateLimit?.enabled===!1?{}:{rateLimit:{...t.rateLimit,...r?{enabled:!0}:{},...o?{storage:"database"}:{},customRules:_(t)}};return C({...t,...n,...t.session?.cookieCache===void 0?{session:{...t.session,cookieCache:{enabled:!0,maxAge:60}}}:{}})},I=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&&E(e.session),p(e),y(e)},B=e=>{const t=I(e);if(typeof t.database!="function")throw new a("INTERNAL","@lunora/auth: `createAuth` needs an adapter factory as `database` (`lunoraD1Adapter(env.DB)` or the Durable Object wiring). A raw D1 binding is for the migration-only instance: build it with `createMigrationAuth` from `@lunora/auth/migrate`.");return m(t)};export{B as createAuth,I as prepareAuthOptions,y as resolveAuthOptions};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as E}from"@lunora/errors";import{I as h,a as j,c as T,R as b,M as g}from"./auth-do-Dkk1jOb6.mjs";import{isDiscoveryRequest as S,unlessNotFound as _}from"./authDiscoveryPaths-B14lM14u.mjs";import{isAuthRoutePath as N,DEFAULT_AUTH_BASE_PATH as I}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";const U=a=>{const{internalSecret:i,jurisdiction:c,objectName:p="auth"}=a,A=new Set(a.discoveryPaths),y=a.namespace;let{namespace:r}=a;if(r&&c!==void 0){if(typeof r.jurisdiction!="function")throw new TypeError(`@lunora/auth: Durable Object namespace does not support jurisdiction("${c}") — update @cloudflare/workers-types or remove the jurisdiction option`);r=r.jurisdiction(c)}const u=()=>{if(r)return r.get(r.idFromName(p))},R=async(e,t,o)=>{if(!i)return;const s=u();if(!s)return;const n=await s.fetch(new Request(new URL(e,t),{body:JSON.stringify(o),headers:{"content-type":"application/json",[h]:i},method:"POST"}));return n.ok?n:void 0},m=r,v=y&&m&&c!==void 0?T(async(e,t)=>{if(!i)throw new E("AUTH_MOVE_NOT_CONFIGURED","copying the auth object needs its internal secret; set the auth object's `internalSecret`");const o=e==="source"?y:m;return o.get(o.idFromName(p)).fetch(new Request(new URL(g,"https://auth-do.invalid"),{body:JSON.stringify(t),headers:{"content-type":"application/json",[h]:i},method:"POST"}))}):void 0;return{...v===void 0?{}:{jurisdictionMove:v},auditReader:{read:async e=>{const t=await R(b,"https://auth-do.invalid",e);return t?(await t.json())?.entries??[]:[]}},authHandler:async e=>{if(N(new URL(e.url).pathname,I))return u()?.fetch(e)},discoveryHandler:async e=>{if(S(e,A))return _(await u()?.fetch(e))},resolveIdentity:async e=>{if(!i)return null;const t=u();if(!t)return null;const o=new Headers(e.headers);o.set(h,i);const s=await t.fetch(new Request(new URL(j,e.url),{headers:o}));if(!s.ok)return null;const n=await s.json(),d=n?.userId;if(!n||typeof d!="string"||d.length===0)return null;const f={userId:d};for(const w of["email","name","orgId","role","teamId"]){const l=n[w];typeof l=="string"&&l.length>0&&(f[w]=l)}return typeof n.expiresAtMs=="number"&&Number.isFinite(n.expiresAtMs)&&(f.expiresAtMs=n.expiresAtMs),f}}};export{U as createDoAuthWiring};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BetterAuthPlugin } from 'better-auth';
|
|
2
|
-
import { L as LunoraAuth } from "./create-auth.d-
|
|
2
|
+
import { L as LunoraAuth } from "./create-auth.d-DNGo48je.js";
|
|
3
3
|
/** One pending or spent sign-up invitation. */
|
|
4
4
|
interface SignUpInvitation {
|
|
5
5
|
/** When an account was created for this address; `null` while the invitation is unspent. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BetterAuthPlugin } from 'better-auth';
|
|
2
|
-
import { L as LunoraAuth } from "./create-auth.d-
|
|
2
|
+
import { L as LunoraAuth } from "./create-auth.d-DNGo48je.mjs";
|
|
3
3
|
/** One pending or spent sign-up invitation. */
|
|
4
4
|
interface SignUpInvitation {
|
|
5
5
|
/** When an account was created for this address; `null` while the invitation is unspent. */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const r=e=>typeof e=="string"&&e.length>0?e:void 0,c=e=>{const l=r(e?.user.id);if(e==null||l===void 0)return null;const{activeOrganizationId:t,activeTeamId:d,expiresAt:i}=e.session,o=r(e.user.email),n=r(e.user.name),a=r(t),m=r(e.user.role),v=r(d);return{...o===void 0?{}:{email:o},...i instanceof Date?{expiresAtMs:i.getTime()}:{},...n===void 0?{}:{name:n},...a===void 0?{}:{orgId:a},...m===void 0?{}:{role:m},...v===void 0?{}:{teamId:v},userId:l}},u=async(e,l)=>{const t=c(await e.api.getSession({headers:l}));if(t===null||t.orgId===void 0&&t.teamId===void 0)return t;const{orgId:d,teamId:i,...o}=t;if(d===void 0)return o;const{adapter:n}=await e.$context,{userId:a}=t,[m,v,s]=await Promise.all([n.findOne({model:"member",where:[{field:"organizationId",value:d},{field:"userId",value:a}]}),i===void 0?void 0:n.findOne({model:"team",where:[{field:"id",value:i},{field:"organizationId",value:d}]}),i===void 0?void 0:n.findOne({model:"teamMember",where:[{field:"teamId",value:i},{field:"userId",value:a}]})]);return m?{...o,orgId:d,...v&&s?{teamId:i}:{}}:o};export{c as identityFromSession,u as resolveSessionIdentity};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./adapter.d-CaPwF2v3.mjs";
|
|
2
2
|
import { LunoraError } from '@lunora/errors';
|
|
3
|
-
import { L as LunoraAuth, a as LunoraAuthOptions } from "./create-auth.d-
|
|
3
|
+
import { L as LunoraAuth, a as LunoraAuthOptions } from "./create-auth.d-DNGo48je.mjs";
|
|
4
4
|
import { AuthAuditReader } from "../audit.mjs";
|
|
5
5
|
import "../sql-store.mjs";
|
|
6
6
|
/**
|
|
@@ -586,6 +586,68 @@ interface AuthJurisdictionMove {
|
|
|
586
586
|
dropped: string[];
|
|
587
587
|
}>;
|
|
588
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* The claims both built-in wirings (D1 and the auth Durable Object) put on the
|
|
591
|
+
* Lunora identity, derived from one better-auth `getSession` result. Each optional
|
|
592
|
+
* claim is absent — never `undefined`, `null` or `""` — when the session does not
|
|
593
|
+
* carry it, so the identity header stays minimal and an absent claim reads as absent.
|
|
594
|
+
*/
|
|
595
|
+
type SessionIdentity = {
|
|
596
|
+
/** Profile claim `ctx.auth.getIdentity()` is documented to carry. */
|
|
597
|
+
email?: string;
|
|
598
|
+
/**
|
|
599
|
+
* The session's expiry (epoch ms). The runtime forwards it as the socket's
|
|
600
|
+
* credential expiry (`x-lunora-identity-exp`); without it a signed-out, banned or
|
|
601
|
+
* lapsed user keeps streaming RLS-scoped rows over an already-open WebSocket.
|
|
602
|
+
*/
|
|
603
|
+
expiresAtMs?: number;
|
|
604
|
+
/** Profile claim `ctx.auth.getIdentity()` is documented to carry. */
|
|
605
|
+
name?: string;
|
|
606
|
+
/**
|
|
607
|
+
* The session's active organization (`organization()` plugin), present only
|
|
608
|
+
* while the user is still a member of it. Named `orgId` because that is the claim
|
|
609
|
+
* the `@lunora/jwt-auth` presets emit, so tenant-scoped code reads one name
|
|
610
|
+
* whichever authority issued the identity.
|
|
611
|
+
*/
|
|
612
|
+
orgId?: string;
|
|
613
|
+
/**
|
|
614
|
+
* better-auth's `admin()` column, comma-joined for several roles — what
|
|
615
|
+
* `rls(policies, { roles })` and `auth.can(...)` read. Only an administrator can
|
|
616
|
+
* write it.
|
|
617
|
+
*/
|
|
618
|
+
role?: string;
|
|
619
|
+
/**
|
|
620
|
+
* The session's active team (`organization({ teams: { enabled: true } })`),
|
|
621
|
+
* present only while it belongs to `orgId` and the user is still on it.
|
|
622
|
+
*/
|
|
623
|
+
teamId?: string;
|
|
624
|
+
userId: string;
|
|
625
|
+
};
|
|
626
|
+
/**
|
|
627
|
+
* Resolve a request's session to the Lunora identity: `ctx.auth.userId` plus the
|
|
628
|
+
* claims `ctx.auth.getIdentity()` returns. Both built-in wirings (the D1 resolver
|
|
629
|
+
* codegen emits and the auth Durable Object) call this.
|
|
630
|
+
*
|
|
631
|
+
* `orgId` / `teamId` are checked against the store before they are emitted, because
|
|
632
|
+
* the session's `activeOrganizationId` / `activeTeamId` outlive the membership they
|
|
633
|
+
* stand for. better-auth (1.7.7) clears them only on the caller's own current
|
|
634
|
+
* session: a member removed by someone else, the other sessions of a member who
|
|
635
|
+
* left, every member of a deleted organization, and anyone removed through
|
|
636
|
+
* `createAuthAdmin` or SCIM (which write the tables directly, past any endpoint
|
|
637
|
+
* hook) keep them. `setActiveOrganization` never clears `activeTeamId` either, so a
|
|
638
|
+
* switch leaves the previous organization's team behind. Checking here covers every
|
|
639
|
+
* one of those paths; clearing sessions on removal would cover only the endpoints,
|
|
640
|
+
* and the session cookie cache would still serve the old value on top.
|
|
641
|
+
*
|
|
642
|
+
* The cost is one `member` read (plus `team` and `teamMember` when a team is active)
|
|
643
|
+
* per resolution of a session that has an active organization, and the worker
|
|
644
|
+
* memoises resolutions per credential, so the claim can lag a removal by up to that
|
|
645
|
+
* memo window. A read that throws propagates rather than emitting an unchecked claim.
|
|
646
|
+
* @param auth The better-auth instance.
|
|
647
|
+
* @param headers The request headers carrying the session.
|
|
648
|
+
* @returns The identity, or `null` for an anonymous request.
|
|
649
|
+
*/
|
|
650
|
+
declare const resolveSessionIdentity: (auth: LunoraAuth, headers: Headers) => Promise<SessionIdentity | null>;
|
|
589
651
|
/** The slice of a Durable Object namespace this needs — structural, so tests need no runtime. */
|
|
590
652
|
interface AuthNamespaceLike {
|
|
591
653
|
get(this: void, id: unknown): {
|
|
@@ -667,19 +729,10 @@ interface DoAuthWiring {
|
|
|
667
729
|
* Resolves a request's identity by asking the object. `null` when anonymous,
|
|
668
730
|
* unreachable, or ungated.
|
|
669
731
|
*
|
|
670
|
-
*
|
|
671
|
-
* the
|
|
672
|
-
* lapsed; `role` is better-auth's `admin()` column, which RLS role grants read;
|
|
673
|
-
* `email` and `name` are the profile claims `ctx.auth.getIdentity()` is documented
|
|
674
|
-
* to carry. Each is absent when the session does not carry it.
|
|
732
|
+
* The claims are the D1 wiring's ({@link SessionIdentity}); each is absent when
|
|
733
|
+
* the session does not carry it.
|
|
675
734
|
*/
|
|
676
|
-
resolveIdentity: (request: Request) => Promise<
|
|
677
|
-
email?: string;
|
|
678
|
-
expiresAtMs?: number;
|
|
679
|
-
name?: string;
|
|
680
|
-
role?: string;
|
|
681
|
-
userId: string;
|
|
682
|
-
}>;
|
|
735
|
+
resolveIdentity: (request: Request) => Promise<SessionIdentity | null>;
|
|
683
736
|
}
|
|
684
737
|
/**
|
|
685
738
|
* Build the worker-side wiring for an auth Durable Object.
|
|
@@ -706,4 +759,4 @@ declare const DEFAULT_AUTH_BASE_PATH: string;
|
|
|
706
759
|
* exact-path map `createWorker` consumes for top-level routes.
|
|
707
760
|
*/
|
|
708
761
|
declare const handleAuthRequest: (auth: LunoraAuth, request: Request, basePath?: string) => Promise<Response | undefined>;
|
|
709
|
-
export { AuthAccount as A, createAuthAdmin as B, CreateAuthAdminOptions as C, DEFAULT_AUTH_BASE_PATH as D, createDoAuthWiring as E, handleAuthDiscoveryRequest as F, handleAuthRequest as G, ImpersonationResult as I, ListUsersOptions as L, AuthAdmin as a, AuthAdminSession as b, AuthAdminUser as c, AuthCapabilities as d, AuthConfigInfo as e, AuthInvitation as f, AuthJurisdiction as g, AuthJurisdictionMove as h, AuthMember as i, AuthMoveResult as j, AuthMoveTableReport as k, AuthNamespaceLike as l, AuthOrgRole as m, AuthOrganization as n, AuthPage as o, AuthPasskey as p, AuthSignUpInvitation as q, AuthTeam as r, AuthTeamMember as s, AuthTimestamp as t, AuthUserFieldSpec as u, DoAuthWiring as v, DoAuthWiringOptions as w, LunoraAuthAdminError as x, authDiscoveryPaths as y, authDiscoveryPathsFor as z };
|
|
762
|
+
export { AuthAccount as A, createAuthAdmin as B, CreateAuthAdminOptions as C, DEFAULT_AUTH_BASE_PATH as D, createDoAuthWiring as E, handleAuthDiscoveryRequest as F, handleAuthRequest as G, resolveSessionIdentity as H, ImpersonationResult as I, ListUsersOptions as L, SessionIdentity as S, AuthAdmin as a, AuthAdminSession as b, AuthAdminUser as c, AuthCapabilities as d, AuthConfigInfo as e, AuthInvitation as f, AuthJurisdiction as g, AuthJurisdictionMove as h, AuthMember as i, AuthMoveResult as j, AuthMoveTableReport as k, AuthNamespaceLike as l, AuthOrgRole as m, AuthOrganization as n, AuthPage as o, AuthPasskey as p, AuthSignUpInvitation as q, AuthTeam as r, AuthTeamMember as s, AuthTimestamp as t, AuthUserFieldSpec as u, DoAuthWiring as v, DoAuthWiringOptions as w, LunoraAuthAdminError as x, authDiscoveryPaths as y, authDiscoveryPathsFor as z };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./adapter.d-CElAPJ1U.js";
|
|
2
2
|
import { LunoraError } from '@lunora/errors';
|
|
3
|
-
import { L as LunoraAuth, a as LunoraAuthOptions } from "./create-auth.d-
|
|
3
|
+
import { L as LunoraAuth, a as LunoraAuthOptions } from "./create-auth.d-DNGo48je.js";
|
|
4
4
|
import { AuthAuditReader } from "../audit.js";
|
|
5
5
|
import "../sql-store.js";
|
|
6
6
|
/**
|
|
@@ -586,6 +586,68 @@ interface AuthJurisdictionMove {
|
|
|
586
586
|
dropped: string[];
|
|
587
587
|
}>;
|
|
588
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* The claims both built-in wirings (D1 and the auth Durable Object) put on the
|
|
591
|
+
* Lunora identity, derived from one better-auth `getSession` result. Each optional
|
|
592
|
+
* claim is absent — never `undefined`, `null` or `""` — when the session does not
|
|
593
|
+
* carry it, so the identity header stays minimal and an absent claim reads as absent.
|
|
594
|
+
*/
|
|
595
|
+
type SessionIdentity = {
|
|
596
|
+
/** Profile claim `ctx.auth.getIdentity()` is documented to carry. */
|
|
597
|
+
email?: string;
|
|
598
|
+
/**
|
|
599
|
+
* The session's expiry (epoch ms). The runtime forwards it as the socket's
|
|
600
|
+
* credential expiry (`x-lunora-identity-exp`); without it a signed-out, banned or
|
|
601
|
+
* lapsed user keeps streaming RLS-scoped rows over an already-open WebSocket.
|
|
602
|
+
*/
|
|
603
|
+
expiresAtMs?: number;
|
|
604
|
+
/** Profile claim `ctx.auth.getIdentity()` is documented to carry. */
|
|
605
|
+
name?: string;
|
|
606
|
+
/**
|
|
607
|
+
* The session's active organization (`organization()` plugin), present only
|
|
608
|
+
* while the user is still a member of it. Named `orgId` because that is the claim
|
|
609
|
+
* the `@lunora/jwt-auth` presets emit, so tenant-scoped code reads one name
|
|
610
|
+
* whichever authority issued the identity.
|
|
611
|
+
*/
|
|
612
|
+
orgId?: string;
|
|
613
|
+
/**
|
|
614
|
+
* better-auth's `admin()` column, comma-joined for several roles — what
|
|
615
|
+
* `rls(policies, { roles })` and `auth.can(...)` read. Only an administrator can
|
|
616
|
+
* write it.
|
|
617
|
+
*/
|
|
618
|
+
role?: string;
|
|
619
|
+
/**
|
|
620
|
+
* The session's active team (`organization({ teams: { enabled: true } })`),
|
|
621
|
+
* present only while it belongs to `orgId` and the user is still on it.
|
|
622
|
+
*/
|
|
623
|
+
teamId?: string;
|
|
624
|
+
userId: string;
|
|
625
|
+
};
|
|
626
|
+
/**
|
|
627
|
+
* Resolve a request's session to the Lunora identity: `ctx.auth.userId` plus the
|
|
628
|
+
* claims `ctx.auth.getIdentity()` returns. Both built-in wirings (the D1 resolver
|
|
629
|
+
* codegen emits and the auth Durable Object) call this.
|
|
630
|
+
*
|
|
631
|
+
* `orgId` / `teamId` are checked against the store before they are emitted, because
|
|
632
|
+
* the session's `activeOrganizationId` / `activeTeamId` outlive the membership they
|
|
633
|
+
* stand for. better-auth (1.7.7) clears them only on the caller's own current
|
|
634
|
+
* session: a member removed by someone else, the other sessions of a member who
|
|
635
|
+
* left, every member of a deleted organization, and anyone removed through
|
|
636
|
+
* `createAuthAdmin` or SCIM (which write the tables directly, past any endpoint
|
|
637
|
+
* hook) keep them. `setActiveOrganization` never clears `activeTeamId` either, so a
|
|
638
|
+
* switch leaves the previous organization's team behind. Checking here covers every
|
|
639
|
+
* one of those paths; clearing sessions on removal would cover only the endpoints,
|
|
640
|
+
* and the session cookie cache would still serve the old value on top.
|
|
641
|
+
*
|
|
642
|
+
* The cost is one `member` read (plus `team` and `teamMember` when a team is active)
|
|
643
|
+
* per resolution of a session that has an active organization, and the worker
|
|
644
|
+
* memoises resolutions per credential, so the claim can lag a removal by up to that
|
|
645
|
+
* memo window. A read that throws propagates rather than emitting an unchecked claim.
|
|
646
|
+
* @param auth The better-auth instance.
|
|
647
|
+
* @param headers The request headers carrying the session.
|
|
648
|
+
* @returns The identity, or `null` for an anonymous request.
|
|
649
|
+
*/
|
|
650
|
+
declare const resolveSessionIdentity: (auth: LunoraAuth, headers: Headers) => Promise<SessionIdentity | null>;
|
|
589
651
|
/** The slice of a Durable Object namespace this needs — structural, so tests need no runtime. */
|
|
590
652
|
interface AuthNamespaceLike {
|
|
591
653
|
get(this: void, id: unknown): {
|
|
@@ -667,19 +729,10 @@ interface DoAuthWiring {
|
|
|
667
729
|
* Resolves a request's identity by asking the object. `null` when anonymous,
|
|
668
730
|
* unreachable, or ungated.
|
|
669
731
|
*
|
|
670
|
-
*
|
|
671
|
-
* the
|
|
672
|
-
* lapsed; `role` is better-auth's `admin()` column, which RLS role grants read;
|
|
673
|
-
* `email` and `name` are the profile claims `ctx.auth.getIdentity()` is documented
|
|
674
|
-
* to carry. Each is absent when the session does not carry it.
|
|
732
|
+
* The claims are the D1 wiring's ({@link SessionIdentity}); each is absent when
|
|
733
|
+
* the session does not carry it.
|
|
675
734
|
*/
|
|
676
|
-
resolveIdentity: (request: Request) => Promise<
|
|
677
|
-
email?: string;
|
|
678
|
-
expiresAtMs?: number;
|
|
679
|
-
name?: string;
|
|
680
|
-
role?: string;
|
|
681
|
-
userId: string;
|
|
682
|
-
}>;
|
|
735
|
+
resolveIdentity: (request: Request) => Promise<SessionIdentity | null>;
|
|
683
736
|
}
|
|
684
737
|
/**
|
|
685
738
|
* Build the worker-side wiring for an auth Durable Object.
|
|
@@ -706,4 +759,4 @@ declare const DEFAULT_AUTH_BASE_PATH: string;
|
|
|
706
759
|
* exact-path map `createWorker` consumes for top-level routes.
|
|
707
760
|
*/
|
|
708
761
|
declare const handleAuthRequest: (auth: LunoraAuth, request: Request, basePath?: string) => Promise<Response | undefined>;
|
|
709
|
-
export { AuthAccount as A, createAuthAdmin as B, CreateAuthAdminOptions as C, DEFAULT_AUTH_BASE_PATH as D, createDoAuthWiring as E, handleAuthDiscoveryRequest as F, handleAuthRequest as G, ImpersonationResult as I, ListUsersOptions as L, AuthAdmin as a, AuthAdminSession as b, AuthAdminUser as c, AuthCapabilities as d, AuthConfigInfo as e, AuthInvitation as f, AuthJurisdiction as g, AuthJurisdictionMove as h, AuthMember as i, AuthMoveResult as j, AuthMoveTableReport as k, AuthNamespaceLike as l, AuthOrgRole as m, AuthOrganization as n, AuthPage as o, AuthPasskey as p, AuthSignUpInvitation as q, AuthTeam as r, AuthTeamMember as s, AuthTimestamp as t, AuthUserFieldSpec as u, DoAuthWiring as v, DoAuthWiringOptions as w, LunoraAuthAdminError as x, authDiscoveryPaths as y, authDiscoveryPathsFor as z };
|
|
762
|
+
export { AuthAccount as A, createAuthAdmin as B, CreateAuthAdminOptions as C, DEFAULT_AUTH_BASE_PATH as D, createDoAuthWiring as E, handleAuthDiscoveryRequest as F, handleAuthRequest as G, resolveSessionIdentity as H, ImpersonationResult as I, ListUsersOptions as L, SessionIdentity as S, AuthAdmin as a, AuthAdminSession as b, AuthAdminUser as c, AuthCapabilities as d, AuthConfigInfo as e, AuthInvitation as f, AuthJurisdiction as g, AuthJurisdictionMove as h, AuthMember as i, AuthMoveResult as j, AuthMoveTableReport as k, AuthNamespaceLike as l, AuthOrgRole as m, AuthOrganization as n, AuthPage as o, AuthPasskey as p, AuthSignUpInvitation as q, AuthTeam as r, AuthTeamMember as s, AuthTimestamp as t, AuthUserFieldSpec as u, DoAuthWiring as v, DoAuthWiringOptions as w, LunoraAuthAdminError as x, authDiscoveryPaths as y, authDiscoveryPathsFor as z };
|
package/dist/plugins.d.mts
CHANGED
|
@@ -19,7 +19,7 @@ type a as IssuedSignUpInvitation,
|
|
|
19
19
|
* Declares the `signUpInvitation` table it reads. Lunora's own, not a better-auth
|
|
20
20
|
* re-export.
|
|
21
21
|
*/
|
|
22
|
-
type S as SignUpInvitation, i as inviteOnly } from "./packem_shared/invite-only.d-
|
|
22
|
+
type S as SignUpInvitation, i as inviteOnly } from "./packem_shared/invite-only.d-BDErbVxw.mjs";
|
|
23
23
|
import { RequireMcpAuthOptions, McpOptions, mcp as mcp$1, requireMcpAuth as requireMcpAuth$1 } from '@better-auth/mcp';
|
|
24
24
|
export { createMcpProtectedRequestHandler } from '@better-auth/mcp';
|
|
25
25
|
import { BetterAuthPlugin } from 'better-auth';
|
|
@@ -49,7 +49,7 @@ export { phoneNumber } from 'better-auth/plugins/phone-number';
|
|
|
49
49
|
export { siwe } from 'better-auth/plugins/siwe';
|
|
50
50
|
export { twoFactor } from 'better-auth/plugins/two-factor';
|
|
51
51
|
export { username } from 'better-auth/plugins/username';
|
|
52
|
-
import "./packem_shared/create-auth.d-
|
|
52
|
+
import "./packem_shared/create-auth.d-DNGo48je.mjs";
|
|
53
53
|
/** better-auth's `requireMcpAuth` options, with `resource` required. */
|
|
54
54
|
type LunoraRequireMcpAuthOptions = {
|
|
55
55
|
/**
|
package/dist/plugins.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ type a as IssuedSignUpInvitation,
|
|
|
19
19
|
* Declares the `signUpInvitation` table it reads. Lunora's own, not a better-auth
|
|
20
20
|
* re-export.
|
|
21
21
|
*/
|
|
22
|
-
type S as SignUpInvitation, i as inviteOnly } from "./packem_shared/invite-only.d-
|
|
22
|
+
type S as SignUpInvitation, i as inviteOnly } from "./packem_shared/invite-only.d-BAKOiLTW.js";
|
|
23
23
|
import { RequireMcpAuthOptions, McpOptions, mcp as mcp$1, requireMcpAuth as requireMcpAuth$1 } from '@better-auth/mcp';
|
|
24
24
|
export { createMcpProtectedRequestHandler } from '@better-auth/mcp';
|
|
25
25
|
import { BetterAuthPlugin } from 'better-auth';
|
|
@@ -49,7 +49,7 @@ export { phoneNumber } from 'better-auth/plugins/phone-number';
|
|
|
49
49
|
export { siwe } from 'better-auth/plugins/siwe';
|
|
50
50
|
export { twoFactor } from 'better-auth/plugins/two-factor';
|
|
51
51
|
export { username } from 'better-auth/plugins/username';
|
|
52
|
-
import "./packem_shared/create-auth.d-
|
|
52
|
+
import "./packem_shared/create-auth.d-DNGo48je.js";
|
|
53
53
|
/** better-auth's `requireMcpAuth` options, with `resource` required. */
|
|
54
54
|
type LunoraRequireMcpAuthOptions = {
|
|
55
55
|
/**
|
package/dist/schema.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TableDefinition } from '@lunora/server';
|
|
2
|
-
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-
|
|
2
|
+
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-DNGo48je.mjs";
|
|
3
3
|
import 'better-auth';
|
|
4
4
|
/**
|
|
5
5
|
* Derive Lunora table definitions from a better-auth config — the bridge that
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TableDefinition } from '@lunora/server';
|
|
2
|
-
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-
|
|
2
|
+
import { a as LunoraAuthOptions } from "./packem_shared/create-auth.d-DNGo48je.js";
|
|
3
3
|
import 'better-auth';
|
|
4
4
|
/**
|
|
5
5
|
* Derive Lunora table definitions from a better-auth config — the bridge that
|
package/dist/worker.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { a as lunoraD1Adapter } from "./packem_shared/adapter.d-CaPwF2v3.mjs";
|
|
2
|
-
export { z as authDiscoveryPathsFor, B as createAuthAdmin, E as createDoAuthWiring, F as handleAuthDiscoveryRequest, G as handleAuthRequest } from "./packem_shared/worker.d-
|
|
2
|
+
export { z as authDiscoveryPathsFor, B as createAuthAdmin, E as createDoAuthWiring, F as handleAuthDiscoveryRequest, G as handleAuthRequest, H as resolveSessionIdentity } from "./packem_shared/worker.d-C5xDdT5g.mjs";
|
|
3
3
|
export { createAuthAuditReader } from "./audit.mjs";
|
|
4
|
-
export { c as createAuth } from "./packem_shared/create-auth.d-
|
|
4
|
+
export { c as createAuth } from "./packem_shared/create-auth.d-DNGo48je.mjs";
|
|
5
5
|
export { d1Executor } from "./sql-store.mjs";
|
|
6
6
|
import 'better-auth/adapters';
|
|
7
7
|
import "./store.mjs";
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { a as lunoraD1Adapter } from "./packem_shared/adapter.d-CElAPJ1U.js";
|
|
2
|
-
export { z as authDiscoveryPathsFor, B as createAuthAdmin, E as createDoAuthWiring, F as handleAuthDiscoveryRequest, G as handleAuthRequest } from "./packem_shared/worker.d-
|
|
2
|
+
export { z as authDiscoveryPathsFor, B as createAuthAdmin, E as createDoAuthWiring, F as handleAuthDiscoveryRequest, G as handleAuthRequest, H as resolveSessionIdentity } from "./packem_shared/worker.d-CqKoBSlW.js";
|
|
3
3
|
export { createAuthAuditReader } from "./audit.js";
|
|
4
|
-
export { c as createAuth } from "./packem_shared/create-auth.d-
|
|
4
|
+
export { c as createAuth } from "./packem_shared/create-auth.d-DNGo48je.js";
|
|
5
5
|
export { d1Executor } from "./sql-store.js";
|
|
6
6
|
import 'better-auth/adapters';
|
|
7
7
|
import "./store.js";
|
package/dist/worker.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as t}from"./packem_shared/adapter-CBZ17jKi.mjs";import{createAuthAdmin as a}from"./packem_shared/LunoraAuthAdminError-uhKG1MgQ.mjs";import{createAuthAuditReader as h}from"./audit.mjs";import{createAuth as p}from"./packem_shared/createAuth-
|
|
1
|
+
import{a as t}from"./packem_shared/adapter-CBZ17jKi.mjs";import{createAuthAdmin as a}from"./packem_shared/LunoraAuthAdminError-uhKG1MgQ.mjs";import{createAuthAuditReader as h}from"./audit.mjs";import{createAuth as p}from"./packem_shared/createAuth-CLeBjzDR.mjs";import{authDiscoveryPathsFor as x,handleAuthDiscoveryRequest as d}from"./packem_shared/authDiscoveryPaths-B14lM14u.mjs";import{createDoAuthWiring as A}from"./packem_shared/createDoAuthWiring-DIxLcjKQ.mjs";import{handleAuthRequest as c}from"./packem_shared/DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import{resolveSessionIdentity as l}from"./packem_shared/resolveSessionIdentity-ChaUVZSy.mjs";import{d as v}from"./packem_shared/sql-store-uMubPYI9.mjs";export{x as authDiscoveryPathsFor,p as createAuth,a as createAuthAdmin,h as createAuthAuditReader,A as createDoAuthWiring,v as d1Executor,d as handleAuthDiscoveryRequest,c as handleAuthRequest,t as lunoraD1Adapter,l as resolveSessionIdentity};
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as R,L as _,R as S,a as T}from"./auth-do-nKx0bUTe.mjs";import"./adapter-CBZ17jKi.mjs";import"../audit.mjs";import"./createAuth-B-Pq7nmN.mjs";import"./authDiscoveryPaths-B14lM14u.mjs";import"./authDoColumnAdditions-UwF3Rl41.mjs";import"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import"./legacyIssuerCleanupStatements-D9Kf8OD1.mjs";export{R as INTERNAL_SECRET_HEADER,_ as LunoraAuthDO,S as READ_AUDIT_PATH,T as RESOLVE_SESSION_PATH};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{getAuthTablesWithResolvedIndexes as M}from"@better-auth/core/db/internal";import{LunoraError as w,toErrorBody as Y}from"@lunora/errors";import{d as U,b as K}from"./adapter-CBZ17jKi.mjs";import{AUTH_AUDIT_TABLE as Q,ensureAuthAuditTable as z,createAuthAuditReader as Z}from"../audit.mjs";import{resolveAuthOptions as ee,createAuth as te}from"./createAuth-B-Pq7nmN.mjs";import{handleAuthDiscoveryRequest as ne}from"./authDiscoveryPaths-B14lM14u.mjs";import{authDoSchemaStatements as se,authDoColumnAdditions as re}from"./authDoColumnAdditions-UwF3Rl41.mjs";import{handleAuthRequest as oe}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";import{decodeWire as I,encodeWire as j}from"@lunora/wire";import{q as a}from"./quote-identifier-CGiYFBvY.mjs";import{schemaDeclaresIssuer as ie,legacyIssuerCleanupStatements as ae,indexesReferencingIssuer as ce}from"./legacyIssuerCleanupStatements-D9Kf8OD1.mjs";const ue=(t,e)=>{const n=Math.max(t.length,e.length);let s=t.length^e.length;for(let r=0;r<n;r+=1){const o=r<t.length?t.charCodeAt(r):0,i=r<e.length?e.charCodeAt(r):0;s|=o^i}return s===0},v=2166136261,F=16777619,k=(t,e=v)=>{let n=e;for(let s=0;s<t.length;s+=1)n^=t.charCodeAt(s),n=Math.imul(n,F);return(n>>>0).toString(16).padStart(8,"0")},le=t=>`${k(t,v)}${k(t,F)}`,fe="/__lunora/auth/move",T="__lunora_auth_move__",R="__lunora_auth_move_rows__",y="__lunora_auth_purged__",x=100,me=16,pe=100,de=2n**64n,H=t=>t.startsWith("sqlite_")||t.startsWith("_cf_")||t===T||t===R||t===y,P=async(t,e)=>t.transaction(async()=>Promise.resolve().then(e)),m=(t,e,...n)=>[...t.sql.exec(e,...n)],u=(t,e,...n)=>{[...t.sql.exec(e,...n)]},_=t=>m(t,"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name").map(e=>String(e.name)).filter(e=>!H(e)),q=(t,e)=>m(t,"SELECT name, pk FROM pragma_table_info(?)",e).map(n=>({name:String(n.name),pk:Number(n.pk)})),g=(t,e)=>q(t,e).map(({name:n})=>n),W=(t,e)=>{const n=q(t,e),s=n.filter(({pk:r})=>r>0).toSorted((r,o)=>r.pk-o.pk).map(({name:r})=>r);return s.length>0?s:n.map(({name:r})=>r)},B=(t,e)=>Number(m(t,`SELECT count(*) AS n FROM ${a(e)}`)[0]?.n??0),V=t=>JSON.stringify(j(t)),b=(t,e)=>BigInt(`0x${le(V(e.map(n=>t[n]??null)))}`),G=(t,e)=>{let n=0,s=0n;for(const r of t)n+=1,s=(s+e(r))%de;return`${String(n)}:${s.toString(16)}`},X=(t,e)=>{const n=g(t,e).toSorted((s,r)=>s.localeCompare(r));return G(t.sql.exec(`SELECT * FROM ${a(e)}`),s=>b(s,n))},Ee=t=>_(t).map(e=>{const n=m(t,"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?",e)[0]?.sql,s=m(t,"SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ? AND sql IS NOT NULL",e).map(r=>({name:String(r.name),sql:String(r.sql)}));return{columns:g(t,e),indexes:s,name:e,rows:B(t,e),sql:typeof n=="string"?n:""}}),C=(t,e)=>{if(typeof e!="string"||!_(t).includes(e))throw new TypeError(`unknown table ${JSON.stringify(e)}`);return e},he=(t,e,n)=>{const s=m(t,`SELECT rowid AS "__lunora_rowid__", * FROM ${a(e)} WHERE rowid > ? ORDER BY rowid LIMIT ?`,n,x),r=s.at(-1)?.__lunora_rowid__;return{last:typeof r=="number"?r:void 0,rows:s.map(({__lunora_rowid__:o,...i})=>i)}},Te=t=>g(t,T).length>0,O=t=>Te(t)?Object.fromEntries(m(t,`SELECT tbl, after, done, pass, cols, watermark FROM ${a(T)}`).map(e=>[String(e.tbl),{after:Number(e.after),columns:JSON.parse(String(e.cols)),done:e.done===1,pass:Number(e.pass),watermark:Number(e.watermark)}])):{},Se=(t,e,n)=>G(t.sql.exec(`SELECT h FROM ${a(R)} WHERE tbl = ? AND pass = ?`,e,n),s=>BigInt(`0x${String(s.h)}`)),we=t=>{u(t,`CREATE TABLE IF NOT EXISTS ${a(T)} (tbl TEXT NOT NULL PRIMARY KEY, after INTEGER NOT NULL, done INTEGER NOT NULL, pass INTEGER NOT NULL, cols TEXT NOT NULL, watermark INTEGER NOT NULL DEFAULT 0)`),u(t,`CREATE TABLE IF NOT EXISTS ${a(R)} (tbl TEXT NOT NULL, k TEXT NOT NULL, h TEXT NOT NULL, pass INTEGER NOT NULL, PRIMARY KEY (tbl, k))`)},Re=(t,e,n)=>{if(!_(t).includes(e))return 0;const s=W(t,e).map(r=>`u.${a(r)}`).join(", ");return Number(m(t,`SELECT count(*) AS n FROM ${a(e)} u WHERE u.rowid > ? AND NOT EXISTS (SELECT 1 FROM ${a(R)} c WHERE c.tbl = ? AND c.k = json_array(${s}))`,n,e)[0]?.n??0)},Ae=/^CREATE TABLE /iu,Ne=/^CREATE (?:UNIQUE )?INDEX /iu,_e=(t,e,n)=>{if(H(e.name))throw new TypeError(`refusing reserved table ${JSON.stringify(e.name)}`);if(!Ae.test(e.sql)||e.indexes.some(({sql:r})=>!Ne.test(r)))throw new TypeError(`refusing unexpected DDL for ${JSON.stringify(e.name)}`);if(n.has(e.name)){const r=new Set(g(t,e.name));for(const o of e.columns.filter(i=>!r.has(i)))u(t,`ALTER TABLE ${a(e.name)} ADD COLUMN ${a(o)}`)}else u(t,e.sql);const s=new Set(m(t,"SELECT name FROM sqlite_master WHERE type = 'index'").map(r=>String(r.name)));for(const r of e.indexes.filter(({name:o})=>!s.has(o)))u(t,r.sql)},Oe=(t,e,n)=>{we(t);const s=Re(t,n.userTable,O(t)[n.userTable]?.watermark??0);if(s>0&&!n.force)throw new w("AUTH_MOVE_TARGET_NOT_EMPTY",`the pinned auth object has ${String(s)} user(s) the copy did not write; copying into it would merge two user bases — pass force to copy anyway`,{data:{users:s}});const r=new Set(_(t)),o=O(t);for(const c of e){_e(t,c,r);const f=o[c.name],p=JSON.stringify(c.columns.toSorted((d,E)=>d.localeCompare(E)));f===void 0?u(t,`INSERT INTO ${a(T)} (tbl, after, done, pass, cols) VALUES (?, 0, 0, 1, ?)`,c.name,p):u(t,`UPDATE ${a(T)} SET cols = ? WHERE tbl = ?`,p,c.name)}u(t,`UPDATE ${a(T)} SET watermark = (SELECT coalesce(max(rowid), 0) FROM ${a(n.userTable)}) WHERE tbl = ?`,n.userTable);const i=O(t);return{cursors:Object.fromEntries(e.map(({name:c})=>[c,i[c]?.after??0])),done:Object.fromEntries(e.map(({name:c})=>[c,i[c]?.done??!1])),targetRows:Object.fromEntries(e.map(({name:c})=>[c,B(t,c)]))}},J=(t,e,n,s,r)=>m(t,`SELECT ${r.map(o=>a(o)).join(", ")} FROM ${a(e)} WHERE ${n.map(o=>`${a(o)} IS ?`).join(" AND ")} LIMIT 1`,...s)[0],$=(t,e)=>{if(!e)throw new w("AUTH_MOVE_CONFLICT",`a row copied into ${t} collides with a row the pinned auth object holds on its own — nothing from this page was written; pass force to keep the pinned object's rows`,{data:{table:t}});return"conflicts"},ge=t=>t instanceof Error&&t.message.includes("UNIQUE constraint failed"),Le=(t,e)=>{if(e.prior!==void 0)return $(e.table,e.force);try{u(t,`INSERT INTO ${a(e.table)} (${e.columns.map(n=>a(n)).join(", ")}) VALUES (${e.columns.map(()=>"?").join(", ")})`,...e.columns.map(n=>e.row[n]))}catch(n){if(!ge(n))throw n;return $(e.table,e.force)}return"copied"},ye=(t,e,n)=>{const s=b(n,e.columns).toString(16);return s===e.h?"unchanged":e.prior===void 0||s!==e.prior?$(e.table,e.force):(u(t,`UPDATE ${a(e.table)} SET ${e.columns.map(r=>`${a(r)} = ?`).join(", ")} WHERE ${e.keys.map(r=>`${a(r)} IS ?`).join(" AND ")}`,...e.columns.map(r=>e.row[r]),...e.key),"updated")},$e=(t,e,n,s)=>{const{force:r,keys:o,pass:i}=s,c=Object.keys(n).toSorted((S,L)=>S.localeCompare(L));if(c.length+o.length>pe)throw new TypeError(`${e} has too many columns to copy`);const f=o.map(S=>n[S]??null),p=V(f),d=b(n,c).toString(16),E=m(t,`SELECT h FROM ${a(R)} WHERE tbl = ? AND k = ?`,e,p)[0]?.h,l={columns:c,force:r,h:d,key:f,keys:o,prior:typeof E=="string"?E:void 0,row:n,table:e},h=l.prior===d?void 0:J(t,e,o,f,c);let A="unchanged";return l.prior!==d&&(A=h===void 0?Le(t,l):ye(t,l,h)),u(t,`INSERT INTO ${a(R)} (tbl, k, h, pass) VALUES (?, ?, ?, ?) ON CONFLICT (tbl, k) DO UPDATE SET h = excluded.h, pass = excluded.pass`,e,p,d,i),A},be=(t,e,n)=>{const{counts:s,force:r,keys:o,marker:i}=n,c=m(t,`SELECT k, h FROM ${a(R)} WHERE tbl = ? AND pass < ?`,e,i.pass);for(const f of c){const p=I(JSON.parse(String(f.k))),d=J(t,e,o,p,i.columns);d!==void 0&&(b(d,i.columns).toString(16)===String(f.h)?(u(t,`DELETE FROM ${a(e)} WHERE ${o.map(E=>`${a(E)} IS ?`).join(" AND ")}`,...p),s.deleted+=1):s[$(e,r)]+=1),u(t,`DELETE FROM ${a(R)} WHERE tbl = ? AND k = ?`,e,String(f.k))}},Ie=async(t,e,n)=>{const s=O(t)[e];if(s===void 0)throw new TypeError(`table ${JSON.stringify(e)} was not part of this move`);return P(t,()=>{const r={conflicts:0,copied:0,deleted:0,unchanged:0,updated:0},o=W(t,e);for(const i of n.rows)r[$e(t,e,i,{force:n.force,keys:o,pass:s.pass})]+=1;return n.final&&be(t,e,{counts:r,force:n.force,keys:o,marker:s}),u(t,`UPDATE ${a(T)} SET after = ?, done = ? WHERE tbl = ?`,n.last,n.final?1:0,e),r})},De=async(t,e)=>{const n=_(t);return await P(t,()=>{const s=n.filter(o=>e[o]===void 0);if(s.length>0)throw new w("AUTH_MOVE_INCOMPLETE",`refusing to purge the un-pinned auth object: ${s.join(", ")} not fully copied — run the copy to done: true first`,{data:{unfinished:s}});const r=n.filter(o=>X(t,o)!==e[o]);if(r.length>0)throw new w("AUTH_MOVE_SOURCE_CHANGED",`refusing to purge the un-pinned auth object: ${r.join(", ")} changed since it was copied — run the copy again to carry the changes, then purge`,{data:{changed:r}});for(const o of n)u(t,`DROP TABLE ${a(o)}`);u(t,`CREATE TABLE IF NOT EXISTS ${a(y)} (at INTEGER NOT NULL)`),u(t,`INSERT INTO ${a(y)} (at) VALUES (?)`,Date.now())}),{dropped:n}},Me=t=>{if(g(t,y).length>0)throw new w("AUTH_MOVE_SOURCE_PURGED","the un-pinned auth object was purged after its tables were copied; anything in it now is new, and copying it would delete the pinned object's users")},Ue=new Set(["fingerprints","manifest","page","purge"]),ke=(t,e,n)=>{e.prepare();const s=O(t);return{copied:n?Object.fromEntries(Object.entries(s).map(([r,o])=>[r,Se(t,r,o.pass)])):{},markers:s,order:e.order()}},Ce=(t,e)=>{for(const n of e)u(t,`UPDATE ${a(T)} SET after = 0, done = 0, pass = pass + 1 WHERE tbl = ?`,n);return{}},je=[/(?:UNIQUE|NOT NULL|CHECK|FOREIGN KEY|PRIMARY KEY) constraint failed/u,/no such (?:table|column)/u,/datatype mismatch/u,/SQLITE_[A-Z_]+/u],ve=t=>{const e=t instanceof Error?t.message:String(t);for(const n of je){const s=n.exec(e);if(s)return s[0]}return t instanceof Error?t.name:"unknown"},Fe=async(t,e,n)=>{switch(Ue.has(String(e.op))&&Me(t),e.op){case"begin":return n.prepare(),Oe(t,e.tables,{force:e.force===!0,userTable:n.userTable()});case"fingerprints":return{fingerprints:Object.fromEntries(_(t).map(s=>[s,X(t,s)]))};case"manifest":return{tables:Ee(t)};case"page":{const s=he(t,C(t,e.table),Number(e.after??0));return{last:s.last,rows:j(s.rows)}}case"purge":{const s=await De(t,e.expected??{});return n.onPurge(),s}case"rescan":return Ce(t,e.tables??[]);case"status":return ke(t,n,e.fingerprints===!0);case"write":return Ie(t,C(t,e.table),{final:e.final===!0,force:e.force===!0,last:Number(e.last),rows:I(e.rows)});default:throw new TypeError(`unknown move op ${JSON.stringify(e.op)}`)}},xe=async(t,e,n)=>{try{return await Fe(t,e,n)}catch(s){if(s instanceof w)throw s;console.error("@lunora/auth: auth move step failed",s);const r=String(e.op),o=typeof e.table=="string"?e.table:void 0,i=ve(s);throw new w("AUTH_MOVE_FAILED",`auth move ${r}${o===void 0?"":` on ${o}`} failed: ${i}`,{data:{cause:i,op:r,...o===void 0?{}:{table:o}}})}},He=(t,e)=>{const n=s=>{const r=e.first.indexOf(s);if(r!==-1)return r;const o=e.last.indexOf(s);return o===-1?e.first.length:e.first.length+1+o};return t.toSorted((s,r)=>n(s.name)-n(r.name)||s.name.localeCompare(r.name))},Pe=(t,e)=>({conflicts:0,copied:0,deleted:0,sourceRows:t.rows,table:t.name,targetRows:e,unchanged:0,updated:0}),st=t=>{const e=async(n,s)=>{const r=await t(n,s);if(!r.ok){let o={};try{const i=await r.json();typeof i.error=="object"&&i.error!==null&&(o=i.error)}catch{}throw new w(o.code??"AUTH_MOVE_FAILED",o.message??`auth move ${String(s.op)} on the ${n} object failed (${String(r.status)})`,{data:o.data,status:r.status})}return r.json()};return{copy:async(n={})=>{const s=n.force===!0,r=await e("target",{op:"status"}),{tables:o}=await e("source",{op:"manifest"}),i=await e("target",{force:s,op:"begin",tables:o}),c=[];let f=0;for(const l of He(o,r.order)){const h=Pe(l,i.targetRows[l.name]??0);let A=i.cursors[l.name]??0;for(c.push(h);;){if(f>=me)return{done:!1,tables:c};f+=1;const S=await e("source",{after:A,op:"page",table:l.name}),L=I(S.rows),D=L.length<x;if(L.length===0&&i.done[l.name]===!0)break;const N=await e("target",{final:D,force:s,last:S.last??A,op:"write",rows:S.rows,table:l.name});if(h.conflicts+=N.conflicts,h.copied+=N.copied,h.deleted+=N.deleted,h.unchanged+=N.unchanged,h.updated+=N.updated,h.targetRows+=N.copied-N.deleted,A=S.last??A,D)break}}const{fingerprints:p}=await e("source",{op:"fingerprints"}),d=await e("target",{fingerprints:!0,op:"status"}),E=Object.keys(p).filter(l=>d.copied[l]!==p[l]);return E.length>0&&await e("target",{op:"rescan",tables:E}),{done:E.length===0,tables:c}},purge:async()=>{const{copied:n,markers:s}=await e("target",{fingerprints:!0,op:"status"}),r=Object.fromEntries(Object.entries(s).filter(([,o])=>o.done).map(([o])=>[o,n[o]]));return e("source",{expected:r,op:"purge"})}}},qe="/__lunora/auth/session",We="/__lunora/auth/audit",Be="x-lunora-auth-do-secret",Ve=t=>{if(t===null||typeof t!="object"||Array.isArray(t))return{error:"body must be a JSON object"};const e={};for(const[n,s]of Object.entries(t))if(n==="limit"||n==="sinceSeq"){if(typeof s!="number"||!Number.isFinite(s))return{error:`"${n}" must be a finite number`};e[n]=s}else if(n==="actorId"||n==="event"){if(typeof s!="string")return{error:`"${n}" must be a string`};e[n]=s}else return{error:`unknown audit read option "${n}"`};return{options:e}};class rt{#n;#a;#c;#e;#t;#s=!1;#r={first:["user"],last:[]};constructor(e,n,s={}){this.#e=e.storage,this.#n=U(e.storage),this.#c=n,this.#a=s}#o(){if(this.#t!==void 0)return this.#t;const e=this.#c(),n=ee(e),{tables:s}=M(n);if(this.#r={first:[s.user?.modelName??"user",s.account?.modelName??"account",s.session?.modelName??"session"],last:[Q,s.rateLimit?.modelName??"rateLimit"]},!this.#s){for(const r of se(n))[...this.#e.sql.exec(r)];for(const r of re(n,o=>this.#u(o)))[...this.#e.sql.exec(r)];this.#l(n),this.#s=!0}return this.#t=te({...e,database:K(this.#e)}),this.#t}#u(e){return[...this.#e.sql.exec("SELECT name FROM pragma_table_info(?)",e)].map(s=>String(s.name))}#l(e){const{tables:n}=M(e),{account:s}=n;if(s===void 0)return;const r=Object.entries(s.fields).map(([o,i])=>i.fieldName??o);if(!(ie(r)||!this.#u(s.modelName).includes("issuer")))try{const o=[...this.#e.sql.exec("SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ?",s.modelName)].map(i=>({name:String(i.name),sql:typeof i.sql=="string"?i.sql:void 0}));for(const i of ae(s.modelName,ce(o)))[...this.#e.sql.exec(i)]}catch(o){console.error("@lunora/auth: could not drop the reverted `account.issuer` column; if it is still present, sign-ups will fail until it is removed.",o)}}async#f(e){if(!this.#i(e))return Response.json({error:"unauthorized"},{status:401});let n;try{n=await e.json()}catch{return Response.json({error:"invalid body"},{status:400})}const s=Ve(n??{});if("error"in s)return Response.json({error:s.error},{status:400});await z(this.#n);const r=await Z(this.#n).read(s.options);return Response.json({entries:r})}async#m(e){if(e.method!=="POST"||!this.#i(e))return Response.json({error:"unauthorized"},{status:401});try{const n=await e.json(),s=await xe(this.#e,n,{onPurge:()=>{this.#s=!1,this.#t=void 0,this.#n=U(this.#e)},order:()=>this.#r,prepare:()=>{this.#o()},userTable:()=>this.#r.first[0]??"user"});return Response.json(s)}catch(n){const{body:s,redacted:r,status:o}=Y(n,{encodeData:i=>i,fallbackCode:"AUTH_MOVE_FAILED",redactedMessage:"auth move failed"});return r&&console.error("@lunora/auth: auth move failed",n),Response.json({error:s},{status:o})}}#i(e){const{internalSecret:n}=this.#a;if(n===void 0||n==="")return!1;const s=e.headers.get(Be);return s!==null&&ue(s,n)}async#p(e){if(!this.#i(e))return Response.json({error:"unauthorized"},{status:401});const s=await this.#o().api.getSession({headers:e.headers}),r=s?.user.id;if(r===void 0)return Response.json({});const o=s?.session.expiresAt,i=s?.user,c=i?.role;return Response.json({...typeof i?.email=="string"&&i.email.length>0?{email:i.email}:{},...o instanceof Date?{expiresAtMs:o.getTime()}:{},...typeof i?.name=="string"&&i.name.length>0?{name:i.name}:{},...typeof c=="string"&&c.length>0?{role:c}:{},userId:r})}async fetch(e){const n=new URL(e.url);if(n.pathname===qe)return this.#p(e);if(n.pathname===We)return this.#f(e);if(n.pathname===fe)return this.#m(e);const s=this.#o();return await oe(s,e)??await ne(s,e)??Response.json({error:"not an auth route"},{status:404})}}export{Be as I,rt as L,fe as M,We as R,qe as a,st as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as a}from"@lunora/errors";import{getIP as l}from"better-auth/api";import{betterAuth as u}from"better-auth/minimal";import{o as h}from"./on-cloudflare-edge-Dx6gFjBO.mjs";import{authDiscoveryPaths as f}from"./authDiscoveryPaths-B14lM14u.mjs";import{validateSessionPolicy as m}from"./sessionPresets-C867Mlo4.mjs";const n="cf-connecting-ip",p=t=>{const e=t!==void 0&&t.length>0;return h()?e?[n,"x-forwarded-for"]:[n]:e?["x-forwarded-for"]:[]},i="/**",A=100,E=t=>{const e=t.rateLimit?.customRules??{};return e[i]!==void 0?e:{...e,[i]:(o,r)=>l(o,t)===null?{max:r.max*A,window:r.window}:r}},d=32,g=t=>{const e=typeof t=="string"?t.trim().length:0;return e>0&&e<d},c=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,w=new Set(["127.0.0.1","localhost"]),L=t=>{if(typeof t!="string")return;let e;try{e=new URL(t)}catch{return}if(!(e.protocol!=="http:"||!w.has(e.hostname)))return{allowedHosts:[e.hostname,`${e.hostname}:*`],fallback:t,protocol:"http"}},v=t=>{const e=L(t.baseURL)??t.baseURL;if(g(t.secret)){const s=`@lunora/auth: AUTH_SECRET is only ${String(t.secret?.trim().length)} characters. Use at least ${String(d)} for a brute-force-resistant secret — generate one with \`openssl rand -hex 32\`.`;if(!c(e))throw new a("INTERNAL",s);console.warn(s)}const o=t.advanced??{},r=o.ipAddress??{};return{...t,advanced:{...o,defaultCookieAttributes:o.defaultCookieAttributes??{httpOnly:!0,path:"/",sameSite:"lax"},ipAddress:{...r,...r.ipAddressHeaders===void 0?{ipAddressHeaders:p(r.trustedProxies)}:{}},...o.useSecureCookies===void 0?{useSecureCookies:!c(e)}:{}},baseURL:e}},C=t=>{const e=v(t),o=e.rateLimit?.enabled===void 0,r=e.rateLimit?.storage===void 0&&e.rateLimit?.enabled!==!1,s=e.rateLimit?.enabled===!1?{}:{rateLimit:{...e.rateLimit,...o?{enabled:!0}:{},...r?{storage:"database"}:{},customRules:E(e)}};return{...e,...s,...e.session?.cookieCache===void 0?{session:{...e.session,cookieCache:{enabled:!0,maxAge:60}}}:{}}},S=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&&m(t.session),f(t),C(t)},x=t=>{const e=S(t);if(typeof e.database!="function")throw new a("INTERNAL","@lunora/auth: `createAuth` needs an adapter factory as `database` (`lunoraD1Adapter(env.DB)` or the Durable Object wiring). A raw D1 binding is for the migration-only instance: build it with `createMigrationAuth` from `@lunora/auth/migrate`.");return u(e)};export{x as createAuth,S as prepareAuthOptions,C as resolveAuthOptions};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as v}from"@lunora/errors";import{I as d,a as w,c as A,R,M as E}from"./auth-do-nKx0bUTe.mjs";import{isDiscoveryRequest as g,unlessNotFound as j}from"./authDiscoveryPaths-B14lM14u.mjs";import{isAuthRoutePath as T,DEFAULT_AUTH_BASE_PATH as b}from"./DEFAULT_AUTH_BASE_PATH-DneiLGpv.mjs";const H=a=>{const{internalSecret:i,jurisdiction:c,objectName:l="auth"}=a,m=new Set(a.discoveryPaths),h=a.namespace;let{namespace:r}=a;if(r&&c!==void 0){if(typeof r.jurisdiction!="function")throw new TypeError(`@lunora/auth: Durable Object namespace does not support jurisdiction("${c}") — update @cloudflare/workers-types or remove the jurisdiction option`);r=r.jurisdiction(c)}const u=()=>{if(r)return r.get(r.idFromName(l))},y=async(t,n,o)=>{if(!i)return;const s=u();if(!s)return;const e=await s.fetch(new Request(new URL(t,n),{body:JSON.stringify(o),headers:{"content-type":"application/json",[d]:i},method:"POST"}));return e.ok?e:void 0},p=r,f=h&&p&&c!==void 0?A(async(t,n)=>{if(!i)throw new v("AUTH_MOVE_NOT_CONFIGURED","copying the auth object needs its internal secret; set the auth object's `internalSecret`");const o=t==="source"?h:p;return o.get(o.idFromName(l)).fetch(new Request(new URL(E,"https://auth-do.invalid"),{body:JSON.stringify(n),headers:{"content-type":"application/json",[d]:i},method:"POST"}))}):void 0;return{...f===void 0?{}:{jurisdictionMove:f},auditReader:{read:async t=>{const n=await y(R,"https://auth-do.invalid",t);return n?(await n.json())?.entries??[]:[]}},authHandler:async t=>{if(T(new URL(t.url).pathname,b))return u()?.fetch(t)},discoveryHandler:async t=>{if(g(t,m))return j(await u()?.fetch(t))},resolveIdentity:async t=>{if(!i)return null;const n=u();if(!n)return null;const o=new Headers(t.headers);o.set(d,i);const s=await n.fetch(new Request(new URL(w,t.url),{headers:o}));if(!s.ok)return null;const e=await s.json();return e?.userId?{...typeof e.email=="string"&&e.email.length>0?{email:e.email}:{},...typeof e.expiresAtMs=="number"&&Number.isFinite(e.expiresAtMs)?{expiresAtMs:e.expiresAtMs}:{},...typeof e.name=="string"&&e.name.length>0?{name:e.name}:{},...typeof e.role=="string"&&e.role.length>0?{role:e.role}:{},userId:e.userId}:null}}};export{H as createDoAuthWiring};
|