@lunora/auth 1.0.0-alpha.166 → 1.0.0-alpha.167
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/audit.mjs +2 -2
- package/dist/packem_shared/createLunoraAuthClient-DRcFNwp9.mjs +1 -0
- package/dist/plugins-client.d.mts +8 -4
- package/dist/plugins-client.d.ts +8 -4
- package/dist/plugins-client.mjs +1 -1
- package/package.json +1 -1
- package/dist/packem_shared/createLunoraAuthClient-DZwrWj-w.mjs +0 -1
package/dist/audit.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{standardRules as
|
|
1
|
+
import{standardRules as N,piiRules as D,redact as $}from"@visulima/redact";const C=new Set(["auth","authorization","bearer","card","cc","cookie","credential","cvc","cvv","dsn","hmac","hotp","iban","jwt","kek","mnemonic","otp","pass","passcode","passphrase","passwd","password","pem","pin","pwd","salt","secret","session","sid","sig","signature","ssn","token","totp","verifier"]),x=new Set(["card","pass","pin","session"]),U=new Set(["access","api","client","encryption","master","priv","private","secret","signing"]),M=new Set(["2fa","auth","authorization","backup","factor","hotp","mfa","otp","pin","recovery","reset","security","totp","verification","verify"]),z=new Set(["card no","connection string","database url","one time"]),W=["accesskey","apikey","authorization","cookie","credential","mfacode","otpcode","passcode","passphrase","passwd","password","privatekey","secret","secretkey","sessionid","signature","token"],F=new Set(["again","b64","base","base64","bytes","confirm","confirmation","data","digest","encrypted","hash","hashed","header","hex","id","input","key","no","num","number","plain","plaintext","raw","repeat","str","string","text","value"]),G=new Set(["count","counts","length","limit","limits","size","tokens","total","usage"]),X=new Set(["max","min","num","total"]),S=4096,Z=128,j=16384,l="<REDACTED>",P="[redaction budget exceeded]",v=e=>e.replaceAll(/([a-z\d])([A-Z])/g,"$1 $2").replaceAll(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").toLowerCase().split(/[^a-z\d]+/).filter(t=>t.length>0),y=e=>e.length>3&&e.endsWith("s")&&!e.endsWith("ss")?e.slice(0,-1):e,H=(e,t)=>{const n=y(e[t]),s=t>0?y(e[t-1]):void 0;if(C.has(n))return n;if(s!==void 0){if(n==="key"&&U.has(s))return"key";if(n==="code"&&M.has(s))return"code";if(z.has(`${s} ${n}`))return`${s} ${n}`}return W.some(a=>n.endsWith(a))?n:void 0},k=e=>{const t=v(e);if(t.length===0)return"none";const n=G.has(t.at(-1))||X.has(t[0]);for(let s=t.length-1;s>=0;s-=1){const a=H(t,s);if(a!==void 0)return n?"count-secret":t.slice(s+1).every(i=>F.has(i))?x.has(a)&&s===t.length-1?"scalar-secret":"secret":"none"}return"none"},V=e=>typeof e=="boolean"||e===null||e===void 0?e:l,q=e=>{const t=e.indexOf("://")+3,n=e.slice(t),s=n.split(/[?#]/,1)[0],a=s.lastIndexOf("@"),i=s.indexOf(":"),c=s.indexOf("/"),r=i!==-1&&/^\d+$/.test(s.slice(i+1,c===-1?void 0:c)),o=i!==-1&&!r&&(c===-1||i<c);if(a===-1&&o&&n.includes("@",s.length))return e.slice(0,t);const d=a!==-1&&(c===-1||a<c||i!==-1&&i<a&&!r);return`${e.slice(0,t)}${d?s.slice(a+1):s}`},B=/\b[a-z][\d+.a-z-]{1,15}:\/\/[^\s"'<>()[\]{}]+/gi,Y=/-----BEGIN ([A-Z ]{0,32}PRIVATE KEY)-----[\s\S]*?(?:-----END [A-Z ]{0,32}PRIVATE KEY-----|$)/g,K=/\b(Basic|Bearer|Digest)\s+[\w+/=.~-]{4,}/g,R=String.raw`%[\dA-Fa-f]{2}|\\(?:[bfnrt]|u[\dA-Fa-f]{4})`,I=[{body:String.raw`[\dA-Za-z]{10,}`,name:"Stripe secret or restricted key",prefix:String.raw`[rs]k_(?:live|test)_`},{body:String.raw`[\d+/=A-Za-z]{20,}`,name:"Stripe webhook secret",prefix:"whsec_"},{body:String.raw`[\dA-Za-z]{30,}`,name:"GitHub classic token (personal, OAuth, user/server-to-server, refresh)",prefix:"gh[oprsu]_"},{body:String.raw`\w{20,}`,name:"GitHub fine-grained token",prefix:"github_pat_"},{body:String.raw`[\w-]{20,}`,name:"GitLab personal, pipeline-trigger and deploy tokens",prefix:"gl(?:pat|ptt|dt)-"},{body:String.raw`[\dA-Za-z-]{10,}`,name:"Slack token",prefix:"xox[a-eprs]-"},{body:String.raw`\d-[\dA-Za-z-]{10,}`,name:"Slack app-level token",prefix:"xapp-"},{body:String.raw`[\dA-Z]{16}\b`,name:"AWS access-key id, long-lived and temporary",prefix:"(?:AKIA|ASIA)"},{body:String.raw`[\w-]{35}(?![\w-])`,name:"Google API key",prefix:"AIza"},{body:String.raw`[\w-]{24,}`,name:"Google OAuth client secret",prefix:"GOCSPX-"},{body:String.raw`[\w-]{20,}`,name:"Google OAuth access token",prefix:String.raw`ya29\.`},{body:String.raw`[\dA-Za-z]{36}\b`,name:"npm token",prefix:"npm_"},{body:String.raw`[\dA-Za-z]{30,}`,name:"Hugging Face token",prefix:"hf_"},{body:String.raw`[\dA-Fa-f]{32}\b`,name:"Shopify token",prefix:"shp(?:at|ca|pa|ss)_"},{body:String.raw`[\da-f]{64}\b`,name:"DigitalOcean token",prefix:"do[opr]_v1_"},{body:String.raw`[\w-]{20,}`,name:"OpenAI project/service/admin key, Anthropic key",prefix:"sk-(?:admin|ant|proj|svcacct)-"},{body:String.raw`[\dA-Za-z]{20}T3BlbkFJ[\dA-Za-z]{20}\b`,name:"OpenAI legacy key",prefix:"sk-"},{body:String.raw`[\w-]{22}\.[\w-]{43}(?![\w-])`,name:"SendGrid key",prefix:String.raw`SG\.`}],J=String.raw`(?<=^|[^\w-]|${R})eyJ[\w-]{2,}\.[\w-]{2,}\.[\w-]*`,Q=new RegExp([...I.map(({body:e,prefix:t})=>String.raw`(?<=^|[^\dA-Za-z]|${R})${t}${e}`),J].join("|"),"g"),ee=new RegExp([...I.map(({prefix:e})=>e),"eyJ"].join("|")),te=/(\s|^)(-u|--user|--password|--pass|--token|--api-key)(\s+|=)[^\s"']+/g,f=/(?<![\w.-])(\\?["']?)([A-Za-z_][\w.-]{0,63})\1[ \t]{0,4}([:=])[ \t]{0,4}/g,w=/(?:basic|bearer|digest|token)\s+[^\s"&'),;\]}]+|\\"(?:[^"\\\n]|\\[^"])*(?:\\")?|"[^\n"]*"?|'[^\n']*'?|[^\s"&'),;\]}]+/iy,b=/(?:[ \t]+(?![A-Za-z_][\w.-]{0,63}[:=])[^\s,;)\]}]+)*/y,ne=e=>{let t="",n=0;f.lastIndex=0;for(let s=f.exec(e);s!==null;s=f.exec(e)){const a=k(s[2]),i=s.index+s[0].length;w.lastIndex=i;const c=a==="none"?null:w.exec(e);if(c===null||a==="count-secret"&&/^["']?[\d.]+["']?$/.test(c[0])){f.lastIndex=Math.max(i,s.index+1);continue}const r=c[0],o=r.startsWith('\\"')?'\\"':r.startsWith('"')||r.startsWith("'")?r[0]:"";let d=i+r.length;o===""&&s[3]===":"&&(b.lastIndex=d,d+=b.exec(e)?.[0].length??0),t+=`${e.slice(n,i)}${o}${l}${r.length>o.length&&r.endsWith(o)?o:""}`,n=d,f.lastIndex=n}return t+e.slice(n)},se=/[:=@]|basic|bearer|digest|-u\b|--/i,re=e=>{const t=ee.test(e);if(!t&&!se.test(e))return e;const n=e.replaceAll(Y,`-----BEGIN $1-----${l}`).replaceAll(B,q).replaceAll(K,`$1 ${l}`);return ne((t?n.replaceAll(Q,l):n).replaceAll(te,`$1$2$3${l}`))},ae=(e,t)=>{if(t.remaining<=0)return P;const n=e.slice(0,S+Z);t.remaining-=n.length;const s=re(n);return s.length>S||e.length>n.length?`${s.slice(0,S)}…[truncated]`:s},ie=e=>e instanceof Error||e instanceof RegExp||e instanceof Map||e instanceof Set||e instanceof WeakMap||e instanceof WeakSet||e instanceof Promise||e instanceof ArrayBuffer||ArrayBuffer.isView(e),g=Symbol("no-json"),oe=e=>{const{toJSON:t}=e;if(typeof t!="function")return g;try{const n=t.call(e);return n===e?g:n}catch{return g}},ce=32,u=(e,t,n,s,a)=>{if(typeof e=="string")return s?l:ae(e,a);if(typeof e!="object"||e===null||!Array.isArray(e)&&ie(e))return e;if(t.has(e)||n>=ce)return l;const i=Array.isArray(e)?g:oe(e);if(i!==g){t.add(e);const r=u(i,t,n+1,s,a);return t.delete(e),r}t.add(e);const c=Array.isArray(e)?e.map(r=>u(r,t,n+1,s,a)):Object.fromEntries(Object.entries(e).map(([r,o])=>{const d=k(r);return d==="secret"||d==="scalar-secret"&&(typeof o!="object"||o===null)?[r,V(o)]:[r,u(o,t,n+1,d==="count-secret"&&(typeof o=="string"||Array.isArray(o)),a)]}));return t.delete(e),c},de=e=>u(e,new WeakSet,0,!1,{remaining:j}),E="__lunora_auth_audit__",le=[...N,...D],pe=1e3,Ae=1e4,A=null,m=e=>{if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="bigint"||typeof e=="boolean")return String(e)},Ee=async e=>{await e.run(`CREATE TABLE IF NOT EXISTS "${E}" (
|
|
2
2
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
3
3
|
ts REAL NOT NULL,
|
|
4
4
|
event TEXT NOT NULL,
|
|
@@ -9,4 +9,4 @@ import{standardRules as D,piiRules as b,redact as $}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 "${E}" ADD COLUMN target_email TEXT`,[])}catch(t){if(!(t instanceof Error?t.message.toLowerCase():String(t).toLowerCase()).includes("duplicate column"))throw t}},_=new WeakMap,L=e=>{const t=_.get(e);if(t)return t;const n=Ee(e);return _.set(e,n),n.catch(()=>_.delete(e)),n},ue=async(e,t,n={})=>{await L(e);let s;return t.detail!==void 0&&(s=n.redactDetail===!1?t.detail:$(de(t.detail),le)),await e.run(`INSERT INTO "${E}" (ts, event, outcome, actor_id, actor_email, target_email, ip, user_agent, detail) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,[t.ts,t.event,t.outcome,t.actorId??A,t.actorEmail??A,t.targetEmail??A,t.ip??A,t.userAgent??A,s===void 0?A:JSON.stringify(s)]),typeof n.retention=="number"&&n.retention>0&&await e.run(`DELETE FROM "${E}" WHERE seq <= (SELECT MAX(seq) - ? FROM "${E}")`,[n.retention]),{...t,detail:s}},fe=async(e,t={})=>{await L(e);const n=Number.isFinite(t.limit)?Math.max(1,Math.min(t.limit,Ae)):pe,s=["seq > ?"],a=[t.sinceSeq??0],i=t.sinceSeq===void 0?"DESC":"ASC";return t.actorId!==void 0&&(s.push("actor_id = ?"),a.push(t.actorId)),t.event!==void 0&&(s.push("event = ?"),a.push(t.event)),a.push(n),(await e.all(`SELECT seq, ts, event, outcome, actor_id, actor_email, target_email, ip, user_agent, detail FROM "${E}" WHERE ${s.join(" AND ")} ORDER BY seq ${i} LIMIT ?`,a)).map(r=>{const o={event:m(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[p,O]of d){const T=m(r[O]);T!==void 0&&(o[p]=T)}const h=m(r.detail);if(h!==void 0)try{const p=JSON.parse(h);typeof p=="object"&&p!==null&&!Array.isArray(p)&&(o.detail=p)}catch{}return o})},Se=e=>({read:t=>fe(e,t)});export{E as AUTH_AUDIT_TABLE,ue as appendAuthAuditEntry,Se as createAuthAuditReader,L as ensureAuthAuditTable,fe as readAuthAuditLog};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{oauthProviderClient as d}from"@better-auth/oauth-provider/client";import{passkeyClient as g}from"@better-auth/passkey/client";import{oneTapClient as m,lastLoginMethodClient as p,deviceAuthorizationClient as C,anonymousClient as y,multiSessionClient as f,phoneNumberClient as S,usernameClient as b,adminClient as T,emailOTPClient as L,magicLinkClient as v,twoFactorClient as w,organizationClient as A}from"better-auth/client/plugins";const s=Symbol.for("lunora:session-change"),P="lunora:session-change",a=()=>{const n=globalThis;let o=n[s];return o===void 0&&(o={detach:void 0,listeners:new Set,tab:`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`},n[s]=o),o},k=()=>"document"in globalThis,E=async()=>{const n=[];for(const o of a().listeners)try{n.push(o())}catch{}await Promise.allSettled(n)},r=async()=>{if(k()&&typeof BroadcastChannel=="function")try{const n=new BroadcastChannel(P);n.postMessage({tab:a().tab,type:"lunora:session-change"}),n.close()}catch{}await E()},i={organization:A,twoFactor:w,passkey:g,magicLink:v,emailOtp:L,admin:T,username:b,phoneNumber:S,multiSession:f,anonymous:y,deviceAuthorization:C,lastLoginMethod:p,oauthProvider:d},N=(n={})=>{const o=[];for(const t of Object.keys(i))n[t]&&o.push(i[t]());return o},M=()=>globalThis.location?.origin,B=async()=>r(),O=()=>({fetchPlugins:[{hooks:{onSuccess:n=>{(n.request.method??"GET").toUpperCase()!=="GET"&&r().catch(()=>{})}},id:"lunora-session-sync",name:"lunora-session-sync"}],id:"lunora-session-sync"}),F=(n,o={})=>{const{baseURL:t,extraPlugins:c=[],oneTapClientId:e,plugins:l,...u}=o,h=e===void 0||e===""?[]:[m({clientId:e})];return n({...u,baseURL:t??M(),plugins:[O(),...N(l),...h,...c]})};export{F as createLunoraAuthClient,N as lunoraAuthPlugins,O as lunoraSessionSync,B as notifyLunoraSessionChange};
|
|
@@ -66,8 +66,11 @@ interface LunoraSessionSyncPlugin {
|
|
|
66
66
|
id: "lunora-session-sync";
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
|
-
* Tell every `LunoraClient` in the page
|
|
70
|
-
*
|
|
69
|
+
* Tell every `LunoraClient` in the page, and in the browser's other tabs on the
|
|
70
|
+
* same origin, that the auth session may have changed, so each asks
|
|
71
|
+
* `/get-session` who is signed in now. A tab on another origin that shares the
|
|
72
|
+
* cookie is not reached: it switches on its next socket reconnect, and the
|
|
73
|
+
* worker's replay check keeps its writes safe meanwhile.
|
|
71
74
|
*
|
|
72
75
|
* Needed for a **cookie** session: a sign-in or sign-out sets or clears an
|
|
73
76
|
* `HttpOnly` cookie in a request the Lunora client never sees, and nothing it
|
|
@@ -77,8 +80,9 @@ interface LunoraSessionSyncPlugin {
|
|
|
77
80
|
* `@lunora/auth-ui` call it for you; call it yourself after a session change
|
|
78
81
|
* made any other way.
|
|
79
82
|
*
|
|
80
|
-
* Resolves once every client has re-resolved (or failed to), so
|
|
81
|
-
* reads `client.currentIdentity()` next can `await` it
|
|
83
|
+
* Resolves once every client in this tab has re-resolved (or failed to), so
|
|
84
|
+
* code that reads `client.currentIdentity()` next can `await` it; the other
|
|
85
|
+
* tabs re-resolve on their own. Never rejects.
|
|
82
86
|
*/
|
|
83
87
|
declare const notifyLunoraSessionChange: () => Promise<void>;
|
|
84
88
|
/**
|
package/dist/plugins-client.d.ts
CHANGED
|
@@ -66,8 +66,11 @@ interface LunoraSessionSyncPlugin {
|
|
|
66
66
|
id: "lunora-session-sync";
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
|
-
* Tell every `LunoraClient` in the page
|
|
70
|
-
*
|
|
69
|
+
* Tell every `LunoraClient` in the page, and in the browser's other tabs on the
|
|
70
|
+
* same origin, that the auth session may have changed, so each asks
|
|
71
|
+
* `/get-session` who is signed in now. A tab on another origin that shares the
|
|
72
|
+
* cookie is not reached: it switches on its next socket reconnect, and the
|
|
73
|
+
* worker's replay check keeps its writes safe meanwhile.
|
|
71
74
|
*
|
|
72
75
|
* Needed for a **cookie** session: a sign-in or sign-out sets or clears an
|
|
73
76
|
* `HttpOnly` cookie in a request the Lunora client never sees, and nothing it
|
|
@@ -77,8 +80,9 @@ interface LunoraSessionSyncPlugin {
|
|
|
77
80
|
* `@lunora/auth-ui` call it for you; call it yourself after a session change
|
|
78
81
|
* made any other way.
|
|
79
82
|
*
|
|
80
|
-
* Resolves once every client has re-resolved (or failed to), so
|
|
81
|
-
* reads `client.currentIdentity()` next can `await` it
|
|
83
|
+
* Resolves once every client in this tab has re-resolved (or failed to), so
|
|
84
|
+
* code that reads `client.currentIdentity()` next can `await` it; the other
|
|
85
|
+
* tabs re-resolve on their own. Never rejects.
|
|
82
86
|
*/
|
|
83
87
|
declare const notifyLunoraSessionChange: () => Promise<void>;
|
|
84
88
|
/**
|
package/dist/plugins-client.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLunoraAuthClient as n,lunoraAuthPlugins as t,lunoraSessionSync as o,notifyLunoraSessionChange as l}from"./packem_shared/createLunoraAuthClient-
|
|
1
|
+
import{createLunoraAuthClient as n,lunoraAuthPlugins as t,lunoraSessionSync as o,notifyLunoraSessionChange as l}from"./packem_shared/createLunoraAuthClient-DRcFNwp9.mjs";import{apiKeyClient as a}from"@better-auth/api-key/client";import{oauthDeviceAuthorizationClient as s,oauthProviderClient as u}from"@better-auth/oauth-provider/client";import{passkeyClient as d}from"@better-auth/passkey/client";import{adminClient as p,anonymousClient as f,customSessionClient as c,deviceAuthorizationClient as g,emailOTPClient as A,inferAdditionalFields as x,inferOrgAdditionalFields as y,jwtClient as S,lastLoginMethodClient as L,magicLinkClient as T,multiSessionClient as k,oneTapClient as v,oneTimeTokenClient as w,organizationClient as z,phoneNumberClient as F,siweClient as P,twoFactorClient as O,usernameClient as b}from"better-auth/client/plugins";export{p as adminClient,f as anonymousClient,a as apiKeyClient,n as createLunoraAuthClient,c as customSessionClient,g as deviceAuthorizationClient,A as emailOTPClient,x as inferAdditionalFields,y as inferOrgAdditionalFields,S as jwtClient,L as lastLoginMethodClient,t as lunoraAuthPlugins,o as lunoraSessionSync,T as magicLinkClient,k as multiSessionClient,l as notifyLunoraSessionChange,s as oauthDeviceAuthorizationClient,u as oauthProviderClient,v as oneTapClient,w as oneTimeTokenClient,z as organizationClient,d as passkeyClient,F as phoneNumberClient,P as siweClient,O as twoFactorClient,b as usernameClient};
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{oauthProviderClient as m}from"@better-auth/oauth-provider/client";import{passkeyClient as h}from"@better-auth/passkey/client";import{oneTapClient as g,lastLoginMethodClient as d,deviceAuthorizationClient as p,anonymousClient as C,multiSessionClient as y,phoneNumberClient as f,usernameClient as S,adminClient as T,emailOTPClient as L,magicLinkClient as b,twoFactorClient as P,organizationClient as k}from"better-auth/client/plugins";const i=Symbol.for("lunora:session-change-listeners"),v=()=>{const n=globalThis;let o=n[i];return o===void 0&&(o=new Set,n[i]=o),o},r=async()=>{const n=[];for(const o of v())try{n.push(o())}catch{}await Promise.allSettled(n)},s={organization:k,twoFactor:P,passkey:h,magicLink:b,emailOtp:L,admin:T,username:S,phoneNumber:f,multiSession:y,anonymous:C,deviceAuthorization:p,lastLoginMethod:d,oauthProvider:m},A=(n={})=>{const o=[];for(const e of Object.keys(s))n[e]&&o.push(s[e]());return o},E=()=>globalThis.location?.origin,z=async()=>r(),I=()=>({fetchPlugins:[{hooks:{onSuccess:n=>{(n.request.method??"GET").toUpperCase()!=="GET"&&r().catch(()=>{})}},id:"lunora-session-sync",name:"lunora-session-sync"}],id:"lunora-session-sync"}),G=(n,o={})=>{const{baseURL:e,extraPlugins:a=[],oneTapClientId:t,plugins:l,...c}=o,u=t===void 0||t===""?[]:[g({clientId:t})];return n({...c,baseURL:e??E(),plugins:[I(),...A(l),...u,...a]})};export{G as createLunoraAuthClient,A as lunoraAuthPlugins,I as lunoraSessionSync,z as notifyLunoraSessionChange};
|