sentri 4.1.0 → 5.0.0

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.
@@ -0,0 +1 @@
1
+ 'use strict';var crypto=require('crypto'),kysely=require('kysely'),Ge=require('bcrypt'),Z=require('jsonwebtoken'),cookie=require('hono/cookie'),hono=require('hono');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Ge__default=/*#__PURE__*/_interopDefault(Ge);var Z__default=/*#__PURE__*/_interopDefault(Z);var Fr=Object.defineProperty;var n=(e,r)=>Fr(e,"name",{value:r,configurable:true});var Ke=Object.assign(Object.create(null),{UNAUTHORIZED:401,TOKEN_EXPIRED:401,TOKEN_INVALID:401,INVALID_CREDENTIALS:401,FORBIDDEN:403,USER_NOT_FOUND:404,IDENTIFIER_NOT_FOUND:404,USER_ALREADY_EXISTS:409,IDENTIFIER_ALREADY_EXISTS:409,INVALID_ROLE:400,VALIDATION_ERROR:400,CONFIGURATION_ERROR:500,TOKEN_REUSE:401}),l=class extends Error{static{n(this,"SentriError");}code;statusCode;constructor(r,t,s){super(t),this.name="SentriError",this.code=r,this.statusCode=s??Ke[r]??500;}};var $e=new WeakMap,He=32,Ve=10,Me=31;function Be(e){if(e.mode==="client"){if(!e.keyUri||e.keyUri.trim().length===0)throw new l("CONFIGURATION_ERROR","keyUri must not be empty");return}if(!e.secret||e.secret.trim().length===0)throw new l("CONFIGURATION_ERROR","secret must not be empty");if((e.algorithm??"HS256").startsWith("HS")&&e.secret.length<He)throw new l("CONFIGURATION_ERROR",`secret must be at least ${He} characters for HMAC algorithms`);let s=e.saltRounds??12;if(!Number.isInteger(s)||s<Ve||s>Me)throw new l("CONFIGURATION_ERROR",`saltRounds must be an integer between ${Ve} and ${Me}`);if(!e.validRoles||e.validRoles.length===0)throw new l("CONFIGURATION_ERROR","validRoles must contain at least one role");if(!e.dialect)throw new l("CONFIGURATION_ERROR","dialect is required in server mode")}n(Be,"validateConfig");function k(e){let r=$e.get(e);if(r)return r;let t={algorithm:e.algorithm??"HS256",accessExpiresIn:e.accessExpiresIn??"15m",refreshExpiresIn:e.refreshExpiresIn??"7d",saltRounds:e.saltRounds??12,validRoles:e.validRoles,validRolesSet:new Set(e.validRoles),cookieName:e.cookie?.name??"refresh_token",accessCookieName:e.accessCookie?.name??"access_token"};return $e.set(e,t),t}n(k,"resolveServerConfig");var Pr=/^(\d+)([smhdw])$/,Kr={s:1e3,m:6e4,h:36e5,d:864e5,w:6048e5},je=new Map;function K(e){if(typeof e=="number")return e*1e3;let r=je.get(e);if(r!==void 0)return r;let t=Pr.exec(e);if(!t?.[1]||!t?.[2])throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let s=Kr[t[2]];if(s===void 0)throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let i=parseInt(t[1],10)*s;return je.set(e,i),i}n(K,"parseExpiry");var N={info:n(()=>{},"info"),warn:n(()=>{},"warn"),error:n(()=>{},"error")};function p(e,r,t){return {service:e,event:r,...t}}n(p,"buildLogData");async function ze(e){await e.schema.createTable("sentri_users").ifNotExists().addColumn("id","varchar(36)",r=>r.primaryKey().notNull()).addColumn("password_hash","varchar(255)",r=>r.notNull()).addColumn("roles","text",r=>r.notNull().defaultTo("[]")).addColumn("created_at","timestamp",r=>r.notNull().defaultTo(kysely.sql`CURRENT_TIMESTAMP`)).execute(),await e.schema.createTable("sentri_sessions").ifNotExists().addColumn("id","varchar(36)",r=>r.primaryKey().notNull()).addColumn("user_id","varchar(36)",r=>r.notNull().references("sentri_users.id").onDelete("cascade")).addColumn("expires_at","timestamp",r=>r.notNull()).addColumn("ip_address","varchar(45)").addColumn("user_agent","text").addColumn("replaced_by","varchar(36)").addColumn("created_at","timestamp",r=>r.notNull().defaultTo(kysely.sql`CURRENT_TIMESTAMP`)).execute(),await e.schema.createTable("sentri_identifiers").ifNotExists().addColumn("id","varchar(36)",r=>r.primaryKey().notNull()).addColumn("user_id","varchar(36)",r=>r.notNull().references("sentri_users.id").onDelete("cascade")).addColumn("type","varchar(100)",r=>r.notNull()).addColumn("value","varchar(255)",r=>r.notNull().unique()).addColumn("created_at","timestamp",r=>r.notNull().defaultTo(kysely.sql`CURRENT_TIMESTAMP`)).execute(),await e.schema.createIndex("idx_sentri_sessions_user_id").ifNotExists().on("sentri_sessions").column("user_id").execute(),await e.schema.createIndex("idx_sentri_identifiers_user_id").ifNotExists().on("sentri_identifiers").column("user_id").execute();}n(ze,"runMigrations");var Xe=new Map;function E(e){let r=Xe.get(e);return r||(r=new kysely.Kysely({dialect:e}),Xe.set(e,r)),r}n(E,"getDatabase");async function G(e,r=12){return Ge__default.default.hash(e,r)}n(G,"hashPassword");async function W(e,r){return Ge__default.default.compare(e,r)}n(W,"verifyPassword");var We=new Map,Ze=new Map,Mr=3600*1e3;function Ye(e){let r=We.get(e);if(!r){let t=crypto.createPrivateKey(e),s=crypto.createPublicKey(t),i=s.export({format:"jwk"}),c=crypto.createHash("sha256").update(JSON.stringify({e:i.e,kty:i.kty,n:i.n})).digest("base64url"),d={...i,use:"sig",kid:c};r={kid:c,publicKey:s,jwk:d},We.set(e,r);}return r}n(Ye,"getOrBuildKey");function Qe(e){let{jwk:r}=Ye(e);return {keys:[r]}}n(Qe,"buildJwks");function er(e){return Ye(e).publicKey}n(er,"getPublicKeyFromPrivate");async function rr(e){let r=Date.now(),t=Ze.get(e);if(t&&r-t.fetchedAt<Mr)return t.publicKey;let s=await fetch(e);if(!s.ok)throw new l("CONFIGURATION_ERROR",`Failed to fetch public key from ${e}: HTTP ${s.status}`);let i=await s.json();if(!i.keys||i.keys.length===0)throw new l("CONFIGURATION_ERROR",`No keys found in JWKS response from ${e}`);let u=i.keys[0],c=crypto.createPublicKey({key:u,format:"jwk"});return Ze.set(e,{publicKey:c,fetchedAt:r}),c}n(rr,"fetchPublicKey");var tr=new Map,sr=new Map,nr=new Map;function or(e){return e.startsWith("RS")||e.startsWith("PS")}n(or,"isRSA");function ir(e){let r=tr.get(e);return r||(r={access:`${e}:access`,refresh:`${e}:refresh`},tr.set(e,r)),r}n(ir,"getHsSecrets");function Br(e){let r=sr.get(e);return r||(r=crypto.createPrivateKey(e),sr.set(e,r)),r}n(Br,"getRsPrivateKey");function ar(e){let r=k(e);if(or(r.algorithm)){let i=Br(e.secret);return {accessKey:i,refreshKey:i}}let{access:t,refresh:s}=ir(e.secret);return {accessKey:t,refreshKey:s}}n(ar,"getSigningKeys");function ur(e,r){let t=k(e);if(or(t.algorithm))return er(e.secret);let{access:s,refresh:i}=ir(e.secret);return r==="access"?s:i}n(ur,"getVerifyKey");function dr(e,r,t,s){let i=`${t}:${s}`,u=nr.get(i);return u||(u={expiresIn:t,algorithm:s},nr.set(i,u)),Z__default.default.sign(e,r,u)}n(dr,"sign");function cr(e,r,t){try{let s=Z__default.default.verify(e,r,{algorithms:[t]});if(typeof s=="string"||s===null)throw new l("TOKEN_INVALID","Token payload is not an object");return s}catch(s){throw s instanceof l?s:s instanceof Z__default.default.TokenExpiredError?new l("TOKEN_EXPIRED","Token has expired"):new l("TOKEN_INVALID","Token is invalid or malformed")}}n(cr,"verify");function J(e,r){let t=k(r),{accessKey:s}=ar(r);return dr(e,s,t.accessExpiresIn,t.algorithm)}n(J,"signAccessToken");function Y(e,r){let t=k(r),{refreshKey:s}=ar(r);return dr({sessionId:e},s,t.refreshExpiresIn,t.algorithm)}n(Y,"signRefreshToken");function de(e,r){let t=k(r),s=ur(r,"access");return cr(e,s,t.algorithm)}n(de,"verifyAccessToken");function Q(e,r){let t=k(r),s=ur(r,"refresh");return cr(e,s,t.algorithm)}n(Q,"verifyRefreshToken");function lr(e,r){try{let t=Z__default.default.verify(e,r,{algorithms:["RS256","RS384","RS512","PS256","PS384","PS512"]});if(typeof t=="string"||t===null)throw new l("TOKEN_INVALID","Token payload is not an object");return t}catch(t){throw t instanceof l?t:t instanceof Z__default.default.TokenExpiredError?new l("TOKEN_EXPIRED","Token has expired"):new l("TOKEN_INVALID","Token is invalid or malformed")}}n(lr,"verifyTokenWithPublicKey");function Ce(e){try{return JSON.parse(e)}catch{return []}}n(Ce,"parseRoles");function zr(e){return JSON.stringify(e)}n(zr,"serializeRoles");function hr(e){return {id:e.id,userId:e.user_id,type:e.type,value:e.value,createdAt:new Date(e.created_at)}}n(hr,"mapIdentifier");async function ee(e,r){let t=await e.selectFrom("sentri_identifiers as i").innerJoin("sentri_users as u","u.id","i.user_id").select(["u.id","u.password_hash","u.roles"]).where("i.value","=",r).executeTakeFirst();return t?{id:t.id,passwordHash:t.password_hash,roles:Ce(t.roles)}:null}n(ee,"findUserByIdentifierValue");async function ce(e,r){let t=await e.selectFrom("sentri_users").select(["id","password_hash","roles"]).where("id","=",r).executeTakeFirst();return t?{id:t.id,passwordHash:t.password_hash,roles:Ce(t.roles)}:null}n(ce,"findUserById");async function wr(e,r,t){let s=t.map(i=>({id:crypto.randomUUID(),user_id:r,type:i.type,value:i.value}));return await e.insertInto("sentri_identifiers").values(s).execute(),s.map(i=>({id:i.id,userId:i.user_id,type:i.type,value:i.value,createdAt:new Date}))}n(wr,"createIdentifiers");async function re(e,r){return (await e.selectFrom("sentri_identifiers").selectAll().where("user_id","=",r).orderBy("created_at","asc").execute()).map(hr)}n(re,"findIdentifiersByUserId");async function be(e,r,t){let s=await e.selectFrom("sentri_identifiers").selectAll().where("id","=",r).where("user_id","=",t).executeTakeFirst();return s?hr(s):null}n(be,"findIdentifierById");async function pr(e,r){let t=await e.selectFrom("sentri_identifiers").select(s=>s.fn.countAll().as("count")).where("user_id","=",r).executeTakeFirst();return Number(t?.count??0)}n(pr,"countIdentifiersByUserId");async function mr(e,r,t,s){await e.updateTable("sentri_identifiers").set({type:s.type,value:s.value}).where("id","=",r).where("user_id","=",t).execute();}n(mr,"updateIdentifier");async function yr(e,r,t){await e.deleteFrom("sentri_identifiers").where("user_id","=",r).where("id","in",t).execute();}n(yr,"deleteIdentifiers");async function Ir(e,r,t){await e.updateTable("sentri_users").set({password_hash:t}).where("id","=",r).execute();}n(Ir,"updateUserPassword");async function gr(e,r,t){await e.updateTable("sentri_users").set({roles:zr(t)}).where("id","=",r).execute();}n(gr,"updateUserRoles");async function Oe(e,r){let t=crypto.randomUUID();return await e.insertInto("sentri_sessions").values({id:t,user_id:r.userId,expires_at:r.expiresAt.toISOString(),ip_address:r.ipAddress??null,user_agent:r.userAgent??null}).execute(),{id:t}}n(Oe,"createSession");async function le(e,r){let t=await e.selectFrom("sentri_sessions as s").innerJoin("sentri_users as u","u.id","s.user_id").select(["s.id as session_id","s.user_id","s.expires_at","s.ip_address","s.user_agent","s.replaced_by","s.created_at as session_created_at","u.id as user_id_col","u.password_hash","u.roles"]).where("s.id","=",r).executeTakeFirst();return t?{id:t.session_id,userId:t.user_id,expiresAt:new Date(t.expires_at),ipAddress:t.ip_address,userAgent:t.user_agent,replacedBy:t.replaced_by,createdAt:new Date(t.session_created_at),user:{id:t.user_id_col,passwordHash:t.password_hash,roles:Ce(t.roles)}}:null}n(le,"findSessionById");async function fe(e,r){await e.deleteFrom("sentri_sessions").where("id","=",r).execute();}n(fe,"deleteSession");async function _r(e,r,t){await e.updateTable("sentri_sessions").set({replaced_by:t}).where("id","=",r).execute();}n(_r,"markSessionReplaced");async function he(e,r){await e.deleteFrom("sentri_sessions").where("user_id","=",r).execute();}n(he,"deleteAllSessionsForUser");async function Rr(e,r){return (await e.selectFrom("sentri_sessions").selectAll().where("user_id","=",r).where("replaced_by","is",null).where("expires_at",">",new Date).execute()).map(s=>({id:s.id,userId:s.user_id,expiresAt:new Date(s.expires_at),ipAddress:s.ip_address,userAgent:s.user_agent,replacedBy:s.replaced_by,createdAt:new Date(s.created_at)}))}n(Rr,"findSessionsByUserId");async function we(e,r,t){let s=k(r),i=E(r.dialect),u=e.roles??[],c=u.filter(_=>!s.validRolesSet.has(_));if(c.length>0)return {success:false,error:new l("INVALID_ROLE",`Invalid roles: ${c.join(", ")}`)};if(!e.identifiers||e.identifiers.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one identifier is required")};let d=e.identifiers.map(_=>({type:_.type.trim(),value:_.value.trim()}));if(new Set(d.map(_=>_.value)).size!==d.length)return {success:false,error:new l("VALIDATION_ERROR","Duplicate identifier values in request")};for(let _ of d)if(await ee(i,_.value))return {success:false,error:new l("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${_.value}`)};let h=await G(e.password,s.saltRounds),{userId:D,identifierRows:b}=await i.transaction().execute(async _=>{let F=crypto.randomUUID();await _.insertInto("sentri_users").values({id:F,password_hash:h,roles:JSON.stringify(u)}).execute();let j=d.map(ae=>({id:crypto.randomUUID(),user_id:F,type:ae.type,value:ae.value}));return await _.insertInto("sentri_identifiers").values(j).execute(),{userId:F,identifierRows:j}}),T={success:true,user:{id:D,roles:u,identifiers:b.map(_=>({id:_.id,type:_.type,value:_.value}))}};return r.hooks?.onRegister&&await r.hooks.onRegister(T.user),T}n(we,"register");async function pe(e,r,t){let s=k(r),i=E(r.dialect),u=await ee(i,e.identifier.trim());if(!u){let T=new l("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,T,{ip:t?.ip??""}),{success:false,error:T}}if(!await W(e.password,u.passwordHash)){let T=new l("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,T,{ip:t?.ip??""}),{success:false,error:T}}let d=new Date(Date.now()+K(s.refreshExpiresIn)),a=await Oe(i,{userId:u.id,expiresAt:d,ipAddress:t?.ip??null,userAgent:t?.userAgent??null}),h={id:u.id,roles:u.roles},D=J({id:u.id,roles:u.roles,sessionId:a.id},r),b=Y(a.id,r);return r.hooks?.onLoginSuccess&&await r.hooks.onLoginSuccess(h,{ip:t?.ip??"",userAgent:t?.userAgent??""}),{success:true,accessToken:D,refreshToken:b,user:h}}n(pe,"login");async function $(e,r,t){let s=k(r),i=E(r.dialect),u;try{({sessionId:u}=Q(e,r));}catch(T){return T instanceof l?{success:false,error:T}:{success:false,error:new l("TOKEN_INVALID","Invalid refresh token")}}let c=await le(i,u);if(!c)return {success:false,error:new l("UNAUTHORIZED","Session not found or revoked")};if(c.replacedBy)return await he(i,c.userId),{success:false,error:new l("TOKEN_REUSE","Token reuse detected. All sessions revoked.")};if(c.expiresAt.getTime()<Date.now())return await fe(i,u),{success:false,error:new l("TOKEN_EXPIRED","Session has expired")};let d=new Date(Date.now()+K(s.refreshExpiresIn)),a=await Oe(i,{userId:c.userId,expiresAt:d,ipAddress:t?.ip??null,userAgent:t?.userAgent??null});await _r(i,u,a.id);let h={id:c.user.id,roles:c.user.roles},D=J({...h,sessionId:a.id},r),b=Y(a.id,r);return {success:true,accessToken:D,refreshToken:b,user:h}}n($,"refresh");async function me(e,r){let t=E(r.dialect),s;try{({sessionId:s}=Q(e,r));}catch{return}let i=await le(t,s);i&&(await fe(t,s),r.hooks?.onLogout&&await r.hooks.onLogout(i.userId));}n(me,"logout");async function ye(e,r){let t=E(r.dialect);await he(t,e),r.hooks?.onLogout&&await r.hooks.onLogout(e);}n(ye,"logoutAll");async function Ie(e,r){let t=E(r.dialect),s=await ce(t,e);if(!s)return {success:false,error:new l("USER_NOT_FOUND","User not found")};let i=await re(t,e);return {success:true,user:{id:s.id,roles:s.roles,identifiers:i.map(u=>({id:u.id,type:u.type,value:u.value}))}}}n(Ie,"getUser");async function ge(e,r,t,s){let i=k(s),u=E(s.dialect),c=await ce(u,e);if(!c)return {success:false,error:new l("USER_NOT_FOUND","User not found")};if(!await W(r,c.passwordHash))return {success:false,error:new l("INVALID_CREDENTIALS","Invalid credentials")};let a=await G(t,i.saltRounds);return await Ir(u,e,a),await he(u,e),s.hooks?.onPasswordChanged&&await s.hooks.onPasswordChanged(e),{success:true}}n(ge,"changePassword");async function _e(e,r,t){let s=k(t),i=E(t.dialect),u=r.filter(h=>!s.validRolesSet.has(h));if(u.length>0)return {success:false,error:new l("INVALID_ROLE",`Invalid roles: ${u.join(", ")}`)};let c=await ce(i,e);if(!c)return {success:false,error:new l("USER_NOT_FOUND","User not found")};let d=new Set(c.roles);for(let h of r)d.add(h);let a=Array.from(d);return await gr(i,e,a),{success:true,user:{id:c.id,roles:a}}}n(_e,"assignRoles");async function Re(e,r,t){let s=E(t.dialect);if(r.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one identifier is required")};let i=r.map(d=>({type:d.type.trim(),value:d.value.trim()}));if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new l("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i)if(await ee(s,d.value))return {success:false,error:new l("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)};return await wr(s,e,i),{success:true,identifiers:(await re(s,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}n(Re,"bulkCreateIdentifiers");async function ke(e,r,t){let s=E(t.dialect);if(r.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one update is required")};let i=r.map(d=>({id:d.id,type:d.type.trim(),value:d.value.trim()}));if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new l("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i){let a=await be(s,d.id,e);if(!a)return {success:false,error:new l("IDENTIFIER_NOT_FOUND",`Identifier not found: ${d.id}`)};if(a.value!==d.value&&await ee(s,d.value))return {success:false,error:new l("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)}}return await s.transaction().execute(async d=>{for(let a of i)await mr(d,a.id,e,{type:a.type,value:a.value});}),{success:true,identifiers:(await re(s,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}n(ke,"bulkUpdateIdentifiers");async function ve(e,r,t){let s=E(t.dialect);if(r.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one ID is required")};let i=Array.from(new Set(r));for(let d of i)if(!await be(s,d,e))return {success:false,error:new l("IDENTIFIER_NOT_FOUND",`Identifier not found: ${d}`)};return await pr(s,e)-i.length<1?{success:false,error:new l("VALIDATION_ERROR","Cannot delete all identifiers \u2014 at least one must remain")}:(await yr(s,e,i),{success:true,identifiers:(await re(s,e)).map(d=>({id:d.id,type:d.type,value:d.value}))})}n(ve,"bulkDeleteIdentifiers");async function vr(e,r){let t=E(r.dialect);return (await Rr(t,e)).map(i=>({id:i.id,ipAddress:i.ipAddress,userAgent:i.userAgent,createdAt:i.createdAt,expiresAt:i.expiresAt}))}n(vr,"getSessions");async function Ar(e,r,t){let s=E(t.dialect),i=await le(s,r);i&&i.userId===e&&await fe(s,r);}n(Ar,"revokeSession");function O(e){return k(e).cookieName}n(O,"getCookieName");function Ae(e){return k(e).accessCookieName}n(Ae,"getAccessCookieName");function U(e,r){if(!e)return;let t=`${r}=`,s=0;for(;s<e.length;){for(;s<e.length&&e[s]===" ";)s++;let i=e.indexOf(";",s),u=i===-1?e.length:i;if(e.startsWith(t,s))return e.slice(s+t.length,u);s=u+1;}}n(U,"readCookie");function xr(e){let r=(e??"strict").toLowerCase();return r==="lax"?"Lax":r==="none"?"None":"Strict"}n(xr,"normalizeSameSite");function te(e,r,t){let s=t.cookie??{},i=K(k(t).refreshExpiresIn)/1e3;cookie.setCookie(e,O(t),r,{httpOnly:s.httpOnly??true,secure:s.secure??false,sameSite:xr(s.sameSite),path:s.path??"/",maxAge:i});}n(te,"setCookieFromConfig");function Ee(e,r){let t=r.cookie??{};cookie.deleteCookie(e,O(r),{path:t.path??"/"});}n(Ee,"clearCookieFromConfig");function se(e,r,t){if(!t.accessCookie)return;let s=t.accessCookie,i=K(k(t).accessExpiresIn)/1e3;cookie.setCookie(e,Ae(t),r,{httpOnly:false,secure:s.secure??false,sameSite:xr(s.sameSite),path:s.path??"/",maxAge:i});}n(se,"setAccessCookieFromConfig");function Ue(e,r){if(!r.accessCookie)return;let t=r.accessCookie;cookie.deleteCookie(e,Ae(r),{path:t.path??"/"});}n(Ue,"clearAccessCookieFromConfig");function Te(e,r){let t=e.req.header("authorization");return t?.startsWith("Bearer ")?t.slice(7):U(e.req.header("cookie"),Ae(r))}n(Te,"getCurrentAccessToken");function L(e){let r=e.logger??N,t=e.loggerService??"sentri";return e.mode==="client"?Xr(e.keyUri,r,t):Gr(e,r,t)}n(L,"protect");function Xr(e,r,t){return async(s,i)=>{let u=s.req.header("authorization"),c=u?.startsWith("Bearer ")?u.slice(7):void 0,d=s.get("requestId");if(!c)throw r.warn(p(t,"auth.protect.failure",{mode:"client",errorCode:"UNAUTHORIZED",...d!==void 0&&{requestId:d}})),new l("UNAUTHORIZED","Missing or malformed Authorization header");try{let a=await rr(e),h=lr(c,a);s.set("user",{id:h.id,roles:h.roles}),r.info(p(t,"auth.protect.success",{mode:"client",userId:h.id,...d!==void 0&&{requestId:d}})),await i();}catch(a){let h=a instanceof l?a.code:"TOKEN_INVALID";throw r.warn(p(t,"auth.protect.failure",{mode:"client",errorCode:h,...d!==void 0&&{requestId:d}})),a}}}n(Xr,"protectClient");function Gr(e,r,t){return async(s,i)=>{let u=Te(s,e),c=s.get("requestId");if(!u)throw r.warn(p(t,"auth.protect.failure",{mode:"server",errorCode:"UNAUTHORIZED",...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Missing or malformed Authorization header");try{let d=de(u,e);if(e.isTokenRevoked&&await e.isTokenRevoked(d.sessionId))throw r.warn(p(t,"auth.protect.token_revoked",{mode:"server",userId:d.id,...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Token has been revoked");s.set("user",{id:d.id,roles:d.roles}),r.info(p(t,"auth.protect.success",{mode:"server",userId:d.id,...c!==void 0&&{requestId:c}})),await i();}catch(d){if(d instanceof l&&d.code==="TOKEN_EXPIRED"){let a=U(s.req.header("cookie"),O(e));if(!a)throw r.warn(p(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED",...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Token expired. Please login again.");let h;try{h=await $(a,e);}catch{throw r.warn(p(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED",...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Session expired. Please login again.")}if(!h.success)throw r.warn(p(t,"auth.protect.failure",{mode:"server",errorCode:h.error.code,...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Session expired. Please login again.");te(s,h.refreshToken,e),se(s,h.accessToken,e),s.header("X-New-Access-Token",h.accessToken),s.set("user",h.user),r.info(p(t,"auth.protect.auto_refresh",{mode:"server",userId:h.user.id,...c!==void 0&&{requestId:c}})),await i();}else {let a=d instanceof l?d.code:"TOKEN_INVALID";throw r.warn(p(t,"auth.protect.failure",{mode:"server",errorCode:a,...c!==void 0&&{requestId:c}})),d}}}}n(Gr,"protectServer");function Nr(e,r,t){let s=`Requires one of roles: ${e.join(", ")}`;return async(i,u)=>{let c=i.get("user"),d=i.get("requestId");if(!c)throw r.warn(p(t,"auth.authorize.unauthenticated",{requiredRoles:e,...d!==void 0&&{requestId:d}})),new l("UNAUTHORIZED","Not authenticated");let a=c.roles;if(!e.some(h=>a.includes(h)))throw r.warn(p(t,"auth.authorize.denied",{userId:c.id,userRoles:[...a],requiredRoles:e,...d!==void 0&&{requestId:d}})),new l("FORBIDDEN",s);r.info(p(t,"auth.authorize.passed",{userId:c.id,userRoles:[...a],requiredRoles:e,...d!==void 0&&{requestId:d}})),await u();}}n(Nr,"createAuthorizeHandler");function ne(e,r){return n(function(...s){return Nr(s,e,r)},"authorize")}n(ne,"createAuthorize");function Wr(...e){return Nr(e,N,"sentri")}n(Wr,"authorize");var Zr=new l("FORBIDDEN","You do not have permission to perform this action");function Dr(e,r,t){return async(s,i)=>{let u=s.get("user"),c=s.get("requestId");if(!u)throw r.warn(p(t,"auth.permit.unauthenticated",{...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Not authenticated");let d=u.id;if(e.roles&&e.roles.length>0){let a=u.roles;if(e.roles.some(h=>a.includes(h))){r.info(p(t,"auth.permit.role_bypass",{userId:d,bypassedByRole:true,...c!==void 0&&{requestId:c}})),await i();return}}try{let a=e.check(s);if(a instanceof Promise?await a:a)r.info(p(t,"auth.permit.passed",{userId:d,...c!==void 0&&{requestId:c}})),await i();else throw r.warn(p(t,"auth.permit.denied",{userId:d,...c!==void 0&&{requestId:c}})),Zr}catch(a){throw a}}}n(Dr,"createPermitHandler");function oe(e,r){return n(function(s){return Dr(typeof s=="function"?{check:s}:s,e,r)},"permit")}n(oe,"createPermit");function Jr(e){return Dr(typeof e=="function"?{check:e}:e,N,"sentri")}n(Jr,"permit");function ie(e){return (r,t)=>r instanceof l?t.json({error:true,statusCode:r.statusCode,code:r.code,message:r.message,data:null},r.statusCode):(e?.onUnhandled?.(r),t.json({error:true,statusCode:500,code:"INTERNAL_SERVER_ERROR",message:"Internal server error",data:null},500))}n(ie,"createErrorHandler");var Le=class{static{n(this,"InMemoryRateLimiter");}store=new Map;constructor(){setInterval(()=>{let r=Date.now();for(let[t,s]of this.store.entries())s.expiresAt<r&&this.store.delete(t);},6e4).unref();}async consume(r,t,s){let i=Date.now(),u=this.store.get(r);if((!u||u.expiresAt<i)&&(u={count:0,expiresAt:i+s},this.store.set(r,u)),u.count>t){let c=Math.ceil((u.expiresAt-i)/1e3);throw new Error(`Rate limit exceeded. Try again in ${c} seconds.`)}u.count++;}},qe=class{static{n(this,"RedisRateLimiter");}redis;logger;constructor(r,t){this.redis=r,this.logger=t;}async consume(r,t,s){let i=`sentri:rl:${r}`,u=Math.ceil(s/1e3);try{let c=await this.redis.multi().incr(i).expire(i,u,"NX").exec();if(!c||c.length===0)return;if(c[0][1]>t)throw new Error("Rate limit exceeded. Try again later.")}catch(c){if(c instanceof Error&&c.message.includes("Rate limit exceeded"))throw c;this.logger.error({msg:"Redis RateLimiter failed, bypassing limit",error:c});}}},H=null;async function Sr(e,r){if(H)return H;if(e)try{let{Redis:t}=await import('ioredis'),s=new t(e,{lazyConnect:!0,maxRetriesPerRequest:1});return await s.connect(),r?.info({msg:"Connected to Redis for Rate Limiting"}),H=new qe(s,r??N),H}catch(t){r?.warn({msg:"Failed to connect to Redis for Rate Limiting, falling back to InMemoryRateLimiter",error:t});}return H=new Le,H}n(Sr,"getRateLimiter");var xe=8,V=72,Ne=255,Cr=100,M=50;function g(e){return new l("VALIDATION_ERROR",e)}n(g,"badRequest");function x(e,r,t,s){return e.json({error:false,statusCode:r,message:t,data:s},r)}n(x,"ok");function C(e,r){return e.json({error:true,statusCode:r.statusCode,code:r.code,message:r.message,data:null},r.statusCode)}n(C,"fail");async function q(e){try{let r=await e.req.json();if(r==null||typeof r!="object"||Array.isArray(r))throw new l("VALIDATION_ERROR","Request body is missing or not a JSON object. Did you set Content-Type: application/json?");return r}catch(r){throw r instanceof l?r:new l("VALIDATION_ERROR","Invalid JSON body. Did you set Content-Type: application/json?")}}n(q,"parseBody");function Qr(e,r){if(!r.apiKey)return;let t=e.req.header("x-api-key");if(typeof t!="string"||t!==r.apiKey)throw new l("UNAUTHORIZED","Invalid or missing API key")}n(Qr,"validateApiKey");function Fe(e){if(e)try{let r=e();r instanceof Promise&&r.catch(()=>{});}catch{}}n(Fe,"fireHook");function et(e){return e.startsWith("RS")||e.startsWith("PS")}n(et,"isRSA");function Pe(e,r){if(typeof e!="object"||e===null||Array.isArray(e))throw g(`identifiers[${r}] must be an object`);let t=e;if(typeof t.type!="string"||t.type.trim().length===0)throw g(`identifiers[${r}].type is required and must be a non-empty string`);if(t.type.length>Cr)throw g(`identifiers[${r}].type must not exceed ${Cr} characters`);if(typeof t.value!="string"||t.value.trim().length===0)throw g(`identifiers[${r}].value is required and must be a non-empty string`);if(t.value.length>Ne)throw g(`identifiers[${r}].value must not exceed ${Ne} characters`);return {type:t.type,value:t.value}}n(Pe,"validateIdentifierInput");function R(e){let r=e.get("requestId");return r!==void 0?{requestId:r}:{}}n(R,"reqId");function br(e){let r=new hono.Hono,t=e,s=k(t),i=e.logger??N,u=e.loggerService??"sentri",c=L(e),d=ne(i,u),a=oe(i,u),h=Sr(e.redisUrl,i),D=e.router?.register??(o=>we(o,t)),b=e.router?.login??(o=>pe(o,t)),T=e.router?.refresh??(o=>$(o,t)),_=e.router?.logout??(o=>o!==void 0?me(o,t):Promise.resolve()),F=e.router?.logoutAll??(o=>ye(o,t)),j=e.router?.getUser??(o=>Ie(o,t)),ae=e.router?.assignRoles??((o,w)=>_e(o,w,t)),Or=e.router?.changePassword??((o,w,f)=>ge(o,w,f,t)),Ur=e.router?.bulkCreateIdentifiers??((o,w)=>Re(o,w,t)),Lr=e.router?.bulkUpdateIdentifiers??((o,w)=>ke(o,w,t)),qr=e.router?.bulkDeleteIdentifiers??((o,w)=>ve(o,w,t)),ue=a(o=>!!o.get("user"));return r.onError(ie()),et(s.algorithm)&&r.get("/keys",o=>(o.header("Cache-Control","public, max-age=3600"),o.json(Qe(e.secret)))),r.post("/register",async o=>{let w=Date.now();try{Qr(o,e);let f=await q(o),{identifiers:y,password:I,roles:m}=f;if(!Array.isArray(y)||y.length===0)throw g("identifiers is required and must be a non-empty array");if(y.length>M)throw g(`identifiers must not exceed ${M} entries`);let v=y.map((X,De)=>Pe(X,De));if(typeof I!="string"||I.length<xe)throw g(`password is required and must be at least ${xe} characters`);if(I.length>V)throw g(`password must not exceed ${V} characters`);if(m!==void 0&&!Array.isArray(m))throw g("roles must be an array of strings when provided");if(Array.isArray(m)&&!m.every(X=>typeof X=="string"))throw g("each role must be a string");let S=Array.isArray(m)?m:void 0,A=S!==void 0?{identifiers:v,password:I,roles:S}:{identifiers:v,password:I},P=o.req.header("x-forwarded-for")||o.env?.incoming?.ip||"127.0.0.1",B=o.req.header("user-agent")||"Unknown";if(e.rateLimit!==!1){let X=await h,De=typeof e.rateLimit=="object"?e.rateLimit.maxRegisterAttempts??5:5;await X.consume(`register:${P}`,De,900*1e3);}let z=await D(A,{ip:P,userAgent:B});return z.success?(i.info(p(u,"auth.register.success",{userId:z.user.id,duration_ms:Date.now()-w,...R(o)})),x(o,201,"User registered successfully",{user:z.user})):(i.warn(p(u,"auth.register.failure",{errorCode:z.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,z.error))}catch(f){throw f}}),r.post("/login",async o=>{let w=Date.now();try{let f=await q(o),{identifier:y,password:I}=f;if(typeof y!="string"||y.trim().length===0)throw g("identifier is required and must be a non-empty string");if(y.length>Ne)throw g(`identifier must not exceed ${Ne} characters`);if(typeof I!="string"||I.length===0)throw g("password is required");if(I.length>V)throw g(`password must not exceed ${V} characters`);let m=y.trim(),v=o.req.header("x-forwarded-for")||o.env?.incoming?.ip||"127.0.0.1",S=o.req.header("user-agent")||"Unknown";if(e.rateLimit!==!1){let P=await h,B=typeof e.rateLimit=="object"?e.rateLimit.maxLoginAttempts??5:5;await P.consume(`login:${v}`,B,900*1e3);}let A=await b({identifier:m,password:I},{ip:v,userAgent:S});return A.success?(Fe(()=>e.hooks?.onLoginSuccess?.(A.user,{ip:v,userAgent:S})),te(o,A.refreshToken,e),se(o,A.accessToken,e),i.info(p(u,"auth.login.success",{userId:A.user.id,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Login successful",{accessToken:A.accessToken,user:A.user})):(Fe(()=>e.hooks?.onLoginFailed?.(m,A.error,{ip:v})),i.warn(p(u,"auth.login.failure",{errorCode:A.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,A.error))}catch(f){throw f}}),r.post("/refresh",async o=>{let w=Date.now();try{let f=U(o.req.header("cookie"),O(e));if(!f)throw new l("UNAUTHORIZED","Refresh token cookie is missing");let y=o.req.header("x-forwarded-for")||o.env?.incoming?.ip||"127.0.0.1",I=o.req.header("user-agent")||"Unknown",m=await T(f,{ip:y,userAgent:I});return m.success?(te(o,m.refreshToken,e),se(o,m.accessToken,e),i.info(p(u,"auth.refresh.success",{userId:m.user.id,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Token refreshed",{accessToken:m.accessToken})):(Ee(o,e),i.warn(p(u,"auth.refresh.failure",{errorCode:m.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,m.error))}catch(f){throw f}}),r.post("/logout",async o=>{let w=Date.now();try{let f=U(o.req.header("cookie"),O(e));return await _(f),Ee(o,e),Ue(o,e),i.info(p(u,"auth.logout",{duration_ms:Date.now()-w,...R(o)})),x(o,200,"Logged out",null)}catch(f){throw f}}),r.post("/logout-all",c,async o=>{let w=Date.now();try{let f=o.get("user").id;return await F(f),Fe(()=>e.hooks?.onLogout?.(f)),Ee(o,e),Ue(o,e),i.info(p(u,"auth.logout_all",{userId:f,duration_ms:Date.now()-w,...R(o)})),x(o,200,"All sessions revoked",null)}catch(f){throw f}}),r.get("/me",c,async o=>{let w=Date.now();try{let f=await j(o.get("user").id);return f.success?(i.info(p(u,"auth.me.success",{userId:f.user.id,duration_ms:Date.now()-w,...R(o)})),x(o,200,"OK",f.user)):(i.warn(p(u,"auth.me.failure",{userId:o.get("user").id,errorCode:f.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,f.error))}catch(f){throw f}}),r.get("/me/identifiers",c,async o=>{let w=Date.now();try{let f=await j(o.get("user").id);return f.success?(i.info(p(u,"auth.me.identifiers.success",{userId:f.user.id,count:f.user.identifiers?.length??0,duration_ms:Date.now()-w,...R(o)})),x(o,200,"OK",{identifiers:f.user.identifiers??[]})):(i.warn(p(u,"auth.me.identifiers.failure",{userId:o.get("user").id,errorCode:f.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,f.error))}catch(f){throw f}}),r.post("/me/identifiers",c,ue,async o=>{let w=Date.now();try{let f=await q(o),{identifiers:y}=f;if(!Array.isArray(y)||y.length===0)throw g("identifiers is required and must be a non-empty array");if(y.length>M)throw g(`identifiers must not exceed ${M} entries`);let I=y.map((v,S)=>Pe(v,S)),m=await Ur(o.get("user").id,I);return m.success?(i.info(p(u,"auth.identifiers.created",{userId:o.get("user").id,count:m.identifiers.length,duration_ms:Date.now()-w,...R(o)})),x(o,201,"Identifiers added successfully",{identifiers:m.identifiers})):(i.warn(p(u,"auth.identifiers.create_failure",{userId:o.get("user").id,errorCode:m.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,m.error))}catch(f){throw f}}),r.put("/me/identifiers",c,ue,async o=>{let w=Date.now();try{let f=await q(o),{identifiers:y}=f;if(!Array.isArray(y)||y.length===0)throw g("identifiers is required and must be a non-empty array");if(y.length>M)throw g(`identifiers must not exceed ${M} entries`);let I=y.map((v,S)=>{if(typeof v!="object"||v===null||Array.isArray(v))throw g(`identifiers[${S}] must be an object`);let A=v;if(typeof A.id!="string"||A.id.trim().length===0)throw g(`identifiers[${S}].id is required and must be a non-empty string`);let{type:P,value:B}=Pe(v,S);return {id:A.id,type:P,value:B}}),m=await Lr(o.get("user").id,I);return m.success?(i.info(p(u,"auth.identifiers.updated",{userId:o.get("user").id,count:m.identifiers.length,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Identifiers updated successfully",{identifiers:m.identifiers})):(i.warn(p(u,"auth.identifiers.update_failure",{userId:o.get("user").id,errorCode:m.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,m.error))}catch(f){throw f}}),r.delete("/me/identifiers",c,ue,async o=>{let w=Date.now();try{let f=await q(o),{ids:y}=f;if(!Array.isArray(y)||y.length===0)throw g("ids is required and must be a non-empty array of strings");if(!y.every(m=>typeof m=="string"))throw g("each id must be a string");let I=await qr(o.get("user").id,y);return I.success?(i.info(p(u,"auth.identifiers.deleted",{userId:o.get("user").id,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Identifiers deleted successfully",{identifiers:I.identifiers})):(i.warn(p(u,"auth.identifiers.delete_failure",{userId:o.get("user").id,errorCode:I.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,I.error))}catch(f){throw f}}),r.patch("/me/password",c,ue,async o=>{let w=Date.now();try{let f=await q(o),{currentPassword:y,newPassword:I}=f;if(typeof y!="string"||y.length===0)throw g("currentPassword is required");if(typeof I!="string"||I.length<xe)throw g(`newPassword must be at least ${xe} characters`);if(I.length>V)throw g(`newPassword must not exceed ${V} characters`);if(y===I)throw g("newPassword must be different from currentPassword");let m=await Or(o.get("user").id,y,I);return m.success?(i.info(p(u,"auth.password.changed",{userId:o.get("user").id,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Password updated successfully. All sessions have been revoked.",null)):(i.warn(p(u,"auth.password.change_failure",{userId:o.get("user").id,errorCode:m.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,m.error))}catch(f){throw f}}),r.post("/users/:userId/roles",c,d("admin"),async o=>{let w=Date.now();try{let f=await q(o),{roles:y}=f,I=o.req.param("userId");if(!I)throw g("userId is required");if(!Array.isArray(y)||y.length===0)throw g("roles must be a non-empty array of strings");if(!y.every(v=>typeof v=="string"))throw g("each role must be a string");let m=await ae(I,y);return m.success?(i.info(p(u,"auth.roles.assigned",{targetUserId:I,roles:y,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Roles assigned successfully",{user:m.user})):(i.warn(p(u,"auth.roles.assign_failure",{targetUserId:I,errorCode:m.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,m.error))}catch(f){throw f}}),r.get("/sessions",L(e),async o=>{let w=Date.now(),f=o.get("user").id,I=await(e.router?.getSessions??(m=>vr(m,t)))(f);return i.info(p(u,"auth.sessions.get",{userId:f,duration_ms:Date.now()-w,...R(o)})),x(o,200,"OK",{sessions:I})}),r.delete("/sessions/:id",L(e),async o=>{let w=Date.now(),f=o.get("user").id,y=o.req.param("id");return await(e.router?.revokeSession??((m,v)=>Ar(m,v,t)))(f,y),i.info(p(u,"auth.sessions.revoke",{userId:f,sessionId:y,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Session revoked",null)}),r}n(br,"createAuthRouter");function zs(e){if(e.mode==="client"){let a={mode:"client",keyUri:e.keyUri,...e.validRoles!==void 0&&{validRoles:e.validRoles},...e.logger!==void 0&&{logger:e.logger},...e.loggerService!==void 0&&{loggerService:e.loggerService}};Be(a);let h=a.logger??N,D=a.loggerService??"sentri",b=ne(h,D),T=oe(h,D);return {protect:n(()=>L(a),"protect"),authorize:n((..._)=>b(..._),"authorize"),permit:n(_=>T(_),"permit"),errorHandler:n(_=>ie(_),"errorHandler")}}let{privateKey:r}=crypto.generateKeyPairSync("rsa",{modulusLength:2048,privateKeyEncoding:{type:"pkcs8",format:"pem"},publicKeyEncoding:{type:"spki",format:"pem"}}),t={mode:"server",dialect:e.dialect,secret:r,algorithm:"RS256",validRoles:e.validRoles,...e.accessExpiresIn!==void 0&&{accessExpiresIn:e.accessExpiresIn},...e.refreshExpiresIn!==void 0&&{refreshExpiresIn:e.refreshExpiresIn},...e.saltRounds!==void 0&&{saltRounds:e.saltRounds},...e.apiKey!==void 0&&{apiKey:e.apiKey},...e.cookie!==void 0&&{cookie:e.cookie},...e.accessCookie!==void 0&&{accessCookie:e.accessCookie},...e.hooks!==void 0&&{hooks:e.hooks},...e.router!==void 0&&{router:e.router},...e.isTokenRevoked!==void 0&&{isTokenRevoked:e.isTokenRevoked},...e.redisUrl!==void 0&&{redisUrl:e.redisUrl},...e.logger!==void 0&&{logger:e.logger},...e.loggerService!==void 0&&{loggerService:e.loggerService}},s=t.logger??N,i=t.loggerService??"sentri",u=k(t),c=ne(s,i),d=oe(s,i);return {protect:n(()=>L(t),"protect"),authorize:n((...a)=>c(...a),"authorize"),permit:n(a=>d(a),"permit"),errorHandler:n(a=>ie(a),"errorHandler"),router:n(()=>br(t),"router"),migrate:n(()=>ze(E(t.dialect)),"migrate"),getCurrentAccessToken:n(a=>Te(a,t),"getCurrentAccessToken"),hashPassword:n(a=>G(a,u.saltRounds),"hashPassword"),verifyPassword:n((a,h)=>W(a,h),"verifyPassword"),signAccessToken:n(a=>J(a,t),"signAccessToken"),signRefreshToken:n(a=>Y(a,t),"signRefreshToken"),verifyAccessToken:n(a=>de(a,t),"verifyAccessToken"),verifyRefreshToken:n(a=>Q(a,t),"verifyRefreshToken"),register:n(a=>we(a,t),"register"),login:n(a=>pe(a,t),"login"),refresh:n(a=>$(a,t),"refresh"),logout:n(a=>me(a,t),"logout"),logoutAll:n(a=>ye(a,t),"logoutAll"),getUser:n(a=>Ie(a,t),"getUser"),changePassword:n((a,h,D)=>ge(a,h,D,t),"changePassword"),assignRoles:n((a,h)=>_e(a,h,t),"assignRoles"),bulkCreateIdentifiers:n((a,h)=>Re(a,h,t),"bulkCreateIdentifiers"),bulkUpdateIdentifiers:n((a,h)=>ke(a,h,t),"bulkUpdateIdentifiers"),bulkDeleteIdentifiers:n((a,h)=>ve(a,h,t),"bulkDeleteIdentifiers")}}n(zs,"createAuthHono");exports.SENTRI_ERROR_STATUS=Ke;exports.SentriError=l;exports.authorize=Wr;exports.createAuthHono=zs;exports.createAuthRouter=br;exports.createAuthorize=ne;exports.createErrorHandler=ie;exports.createPermit=oe;exports.getCurrentAccessToken=Te;exports.permit=Jr;exports.protect=L;
@@ -0,0 +1,114 @@
1
+ import * as kysely from 'kysely';
2
+ import { MiddlewareHandler, Context, Next, Hono } from 'hono';
3
+ import { AuthUser, AuthConfig, SentriLogger, ServerAuthConfig, CookieConfig, AccessCookieConfig, AuthHooks, RouterHandlers, RegisterInput, RegisterResult, LoginInput, AuthResult, RefreshResult, GetUserResult, ChangePasswordResult, AssignRolesResult, IdentifierInput, BulkIdentifiersResult } from '../../core/index.cjs';
4
+ export { ApiResponse, ClientAuthConfig, IdentifierRecord, SENTRI_ERROR_STATUS, SentriError, SentriErrorCode } from '../../core/index.cjs';
5
+
6
+ interface SentriHonoVariables {
7
+ user: AuthUser | undefined;
8
+ requestId: string | undefined;
9
+ }
10
+ type SentriHonoEnv = {
11
+ Variables: SentriHonoVariables;
12
+ };
13
+ type SentriHonoMiddleware = MiddlewareHandler<SentriHonoEnv>;
14
+
15
+ declare function protect(config: AuthConfig): (c: Context<SentriHonoEnv>, next: Next) => Promise<Response | void>;
16
+
17
+ declare function createAuthorize(logger: SentriLogger, service: string): <TRole extends string>(...allowedRoles: TRole[]) => SentriHonoMiddleware;
18
+ declare function authorize<TRole extends string>(...allowedRoles: TRole[]): SentriHonoMiddleware;
19
+
20
+ /** A function that determines whether the current request is permitted. */
21
+ type HonoPermitCheck = (c: Context<SentriHonoEnv>) => boolean | Promise<boolean>;
22
+ /**
23
+ * Options for {@link permit} when you need role-bypass alongside a resource check.
24
+ *
25
+ * @example
26
+ * auth.permit({
27
+ * roles: ['admin'],
28
+ * check: async (c) => {
29
+ * const post = await db.findPost(c.req.param('id'));
30
+ * return post?.authorId === c.get('user')!.id;
31
+ * },
32
+ * })
33
+ */
34
+ interface HonoPermitOptions<TRole extends string> {
35
+ roles?: TRole[];
36
+ check: HonoPermitCheck;
37
+ }
38
+ declare function createPermit(logger: SentriLogger, service: string): <TRole extends string>(optionsOrCheck: HonoPermitOptions<TRole> | HonoPermitCheck) => SentriHonoMiddleware;
39
+ declare function permit<TRole extends string>(optionsOrCheck: HonoPermitOptions<TRole> | HonoPermitCheck): SentriHonoMiddleware;
40
+
41
+ interface HonoErrorHandlerOptions {
42
+ onUnhandled?: (error: unknown) => void;
43
+ }
44
+ type HonoErrorHandler = (err: Error, c: Context) => Response | Promise<Response>;
45
+ declare function createErrorHandler(options?: HonoErrorHandlerOptions): HonoErrorHandler;
46
+
47
+ declare function getCurrentAccessToken(c: Context, config: ServerAuthConfig): string | undefined;
48
+
49
+ declare function createAuthRouter<TRole extends string>(config: ServerAuthConfig<TRole>): Hono<SentriHonoEnv>;
50
+
51
+ interface CreateHonoServerOptions<TRole extends string = string> {
52
+ mode: 'server';
53
+ validRoles: readonly TRole[];
54
+ dialect: kysely.Dialect;
55
+ accessExpiresIn?: string | number;
56
+ refreshExpiresIn?: string | number;
57
+ saltRounds?: number;
58
+ apiKey?: string;
59
+ cookie?: CookieConfig;
60
+ accessCookie?: AccessCookieConfig;
61
+ hooks?: AuthHooks;
62
+ router?: RouterHandlers;
63
+ isTokenRevoked?: (sessionId: string) => boolean | Promise<boolean>;
64
+ redisUrl?: string;
65
+ logger?: SentriLogger;
66
+ loggerService?: string;
67
+ }
68
+ interface CreateHonoClientOptions<TRole extends string = string> {
69
+ mode: 'client';
70
+ keyUri: string;
71
+ validRoles?: readonly TRole[];
72
+ logger?: SentriLogger;
73
+ loggerService?: string;
74
+ }
75
+ interface HonoAuthClient<TRole extends string = string> {
76
+ protect(): SentriHonoMiddleware;
77
+ authorize(...roles: TRole[]): SentriHonoMiddleware;
78
+ permit(check: HonoPermitCheck): SentriHonoMiddleware;
79
+ permit(options: HonoPermitOptions<TRole>): SentriHonoMiddleware;
80
+ errorHandler(options?: HonoErrorHandlerOptions): HonoErrorHandler;
81
+ }
82
+ interface HonoServerAuthClient<TRole extends string = string> extends HonoAuthClient<TRole> {
83
+ router(): Hono<SentriHonoEnv>;
84
+ migrate(): Promise<void>;
85
+ getCurrentAccessToken(c: Context): string | undefined;
86
+ hashPassword(plain: string): Promise<string>;
87
+ verifyPassword(plain: string, hash: string): Promise<boolean>;
88
+ signAccessToken(payload: AuthUser<TRole>): string;
89
+ signRefreshToken(sessionId: string): string;
90
+ verifyAccessToken(token: string): AuthUser<TRole>;
91
+ verifyRefreshToken(token: string): {
92
+ sessionId: string;
93
+ };
94
+ register(input: RegisterInput<TRole>): Promise<RegisterResult<TRole>>;
95
+ login(input: LoginInput): Promise<AuthResult<TRole>>;
96
+ refresh(refreshToken: string): Promise<RefreshResult<TRole>>;
97
+ logout(refreshToken: string): Promise<void>;
98
+ logoutAll(userId: string): Promise<void>;
99
+ getUser(userId: string): Promise<GetUserResult<TRole>>;
100
+ changePassword(userId: string, currentPassword: string, newPassword: string): Promise<ChangePasswordResult>;
101
+ assignRoles(userId: string, roles: TRole[]): Promise<AssignRolesResult<TRole>>;
102
+ bulkCreateIdentifiers(userId: string, identifiers: IdentifierInput[]): Promise<BulkIdentifiersResult>;
103
+ bulkUpdateIdentifiers(userId: string, updates: Array<{
104
+ id: string;
105
+ type: string;
106
+ value: string;
107
+ }>): Promise<BulkIdentifiersResult>;
108
+ bulkDeleteIdentifiers(userId: string, ids: string[]): Promise<BulkIdentifiersResult>;
109
+ }
110
+ type HonoClientAuthClient<TRole extends string = string> = HonoAuthClient<TRole>;
111
+ declare function createAuthHono<TRole extends string = string>(options: CreateHonoServerOptions<TRole>): HonoServerAuthClient<TRole>;
112
+ declare function createAuthHono<TRole extends string = string>(options: CreateHonoClientOptions<TRole>): HonoClientAuthClient<TRole>;
113
+
114
+ export { AccessCookieConfig, AssignRolesResult, AuthConfig, AuthHooks, AuthResult, AuthUser, BulkIdentifiersResult, ChangePasswordResult, CookieConfig, type CreateHonoClientOptions, type CreateHonoServerOptions, GetUserResult, type HonoAuthClient, type HonoClientAuthClient, type HonoErrorHandler, type HonoErrorHandlerOptions, type HonoPermitCheck, type HonoPermitOptions, type HonoServerAuthClient, IdentifierInput, LoginInput, RefreshResult, RegisterInput, RegisterResult, RouterHandlers, type SentriHonoEnv, type SentriHonoMiddleware, type SentriHonoVariables, SentriLogger, ServerAuthConfig, authorize, createAuthHono, createAuthRouter, createAuthorize, createErrorHandler, createPermit, getCurrentAccessToken, permit, protect };
@@ -0,0 +1,114 @@
1
+ import * as kysely from 'kysely';
2
+ import { MiddlewareHandler, Context, Next, Hono } from 'hono';
3
+ import { AuthUser, AuthConfig, SentriLogger, ServerAuthConfig, CookieConfig, AccessCookieConfig, AuthHooks, RouterHandlers, RegisterInput, RegisterResult, LoginInput, AuthResult, RefreshResult, GetUserResult, ChangePasswordResult, AssignRolesResult, IdentifierInput, BulkIdentifiersResult } from '../../core/index.js';
4
+ export { ApiResponse, ClientAuthConfig, IdentifierRecord, SENTRI_ERROR_STATUS, SentriError, SentriErrorCode } from '../../core/index.js';
5
+
6
+ interface SentriHonoVariables {
7
+ user: AuthUser | undefined;
8
+ requestId: string | undefined;
9
+ }
10
+ type SentriHonoEnv = {
11
+ Variables: SentriHonoVariables;
12
+ };
13
+ type SentriHonoMiddleware = MiddlewareHandler<SentriHonoEnv>;
14
+
15
+ declare function protect(config: AuthConfig): (c: Context<SentriHonoEnv>, next: Next) => Promise<Response | void>;
16
+
17
+ declare function createAuthorize(logger: SentriLogger, service: string): <TRole extends string>(...allowedRoles: TRole[]) => SentriHonoMiddleware;
18
+ declare function authorize<TRole extends string>(...allowedRoles: TRole[]): SentriHonoMiddleware;
19
+
20
+ /** A function that determines whether the current request is permitted. */
21
+ type HonoPermitCheck = (c: Context<SentriHonoEnv>) => boolean | Promise<boolean>;
22
+ /**
23
+ * Options for {@link permit} when you need role-bypass alongside a resource check.
24
+ *
25
+ * @example
26
+ * auth.permit({
27
+ * roles: ['admin'],
28
+ * check: async (c) => {
29
+ * const post = await db.findPost(c.req.param('id'));
30
+ * return post?.authorId === c.get('user')!.id;
31
+ * },
32
+ * })
33
+ */
34
+ interface HonoPermitOptions<TRole extends string> {
35
+ roles?: TRole[];
36
+ check: HonoPermitCheck;
37
+ }
38
+ declare function createPermit(logger: SentriLogger, service: string): <TRole extends string>(optionsOrCheck: HonoPermitOptions<TRole> | HonoPermitCheck) => SentriHonoMiddleware;
39
+ declare function permit<TRole extends string>(optionsOrCheck: HonoPermitOptions<TRole> | HonoPermitCheck): SentriHonoMiddleware;
40
+
41
+ interface HonoErrorHandlerOptions {
42
+ onUnhandled?: (error: unknown) => void;
43
+ }
44
+ type HonoErrorHandler = (err: Error, c: Context) => Response | Promise<Response>;
45
+ declare function createErrorHandler(options?: HonoErrorHandlerOptions): HonoErrorHandler;
46
+
47
+ declare function getCurrentAccessToken(c: Context, config: ServerAuthConfig): string | undefined;
48
+
49
+ declare function createAuthRouter<TRole extends string>(config: ServerAuthConfig<TRole>): Hono<SentriHonoEnv>;
50
+
51
+ interface CreateHonoServerOptions<TRole extends string = string> {
52
+ mode: 'server';
53
+ validRoles: readonly TRole[];
54
+ dialect: kysely.Dialect;
55
+ accessExpiresIn?: string | number;
56
+ refreshExpiresIn?: string | number;
57
+ saltRounds?: number;
58
+ apiKey?: string;
59
+ cookie?: CookieConfig;
60
+ accessCookie?: AccessCookieConfig;
61
+ hooks?: AuthHooks;
62
+ router?: RouterHandlers;
63
+ isTokenRevoked?: (sessionId: string) => boolean | Promise<boolean>;
64
+ redisUrl?: string;
65
+ logger?: SentriLogger;
66
+ loggerService?: string;
67
+ }
68
+ interface CreateHonoClientOptions<TRole extends string = string> {
69
+ mode: 'client';
70
+ keyUri: string;
71
+ validRoles?: readonly TRole[];
72
+ logger?: SentriLogger;
73
+ loggerService?: string;
74
+ }
75
+ interface HonoAuthClient<TRole extends string = string> {
76
+ protect(): SentriHonoMiddleware;
77
+ authorize(...roles: TRole[]): SentriHonoMiddleware;
78
+ permit(check: HonoPermitCheck): SentriHonoMiddleware;
79
+ permit(options: HonoPermitOptions<TRole>): SentriHonoMiddleware;
80
+ errorHandler(options?: HonoErrorHandlerOptions): HonoErrorHandler;
81
+ }
82
+ interface HonoServerAuthClient<TRole extends string = string> extends HonoAuthClient<TRole> {
83
+ router(): Hono<SentriHonoEnv>;
84
+ migrate(): Promise<void>;
85
+ getCurrentAccessToken(c: Context): string | undefined;
86
+ hashPassword(plain: string): Promise<string>;
87
+ verifyPassword(plain: string, hash: string): Promise<boolean>;
88
+ signAccessToken(payload: AuthUser<TRole>): string;
89
+ signRefreshToken(sessionId: string): string;
90
+ verifyAccessToken(token: string): AuthUser<TRole>;
91
+ verifyRefreshToken(token: string): {
92
+ sessionId: string;
93
+ };
94
+ register(input: RegisterInput<TRole>): Promise<RegisterResult<TRole>>;
95
+ login(input: LoginInput): Promise<AuthResult<TRole>>;
96
+ refresh(refreshToken: string): Promise<RefreshResult<TRole>>;
97
+ logout(refreshToken: string): Promise<void>;
98
+ logoutAll(userId: string): Promise<void>;
99
+ getUser(userId: string): Promise<GetUserResult<TRole>>;
100
+ changePassword(userId: string, currentPassword: string, newPassword: string): Promise<ChangePasswordResult>;
101
+ assignRoles(userId: string, roles: TRole[]): Promise<AssignRolesResult<TRole>>;
102
+ bulkCreateIdentifiers(userId: string, identifiers: IdentifierInput[]): Promise<BulkIdentifiersResult>;
103
+ bulkUpdateIdentifiers(userId: string, updates: Array<{
104
+ id: string;
105
+ type: string;
106
+ value: string;
107
+ }>): Promise<BulkIdentifiersResult>;
108
+ bulkDeleteIdentifiers(userId: string, ids: string[]): Promise<BulkIdentifiersResult>;
109
+ }
110
+ type HonoClientAuthClient<TRole extends string = string> = HonoAuthClient<TRole>;
111
+ declare function createAuthHono<TRole extends string = string>(options: CreateHonoServerOptions<TRole>): HonoServerAuthClient<TRole>;
112
+ declare function createAuthHono<TRole extends string = string>(options: CreateHonoClientOptions<TRole>): HonoClientAuthClient<TRole>;
113
+
114
+ export { AccessCookieConfig, AssignRolesResult, AuthConfig, AuthHooks, AuthResult, AuthUser, BulkIdentifiersResult, ChangePasswordResult, CookieConfig, type CreateHonoClientOptions, type CreateHonoServerOptions, GetUserResult, type HonoAuthClient, type HonoClientAuthClient, type HonoErrorHandler, type HonoErrorHandlerOptions, type HonoPermitCheck, type HonoPermitOptions, type HonoServerAuthClient, IdentifierInput, LoginInput, RefreshResult, RegisterInput, RegisterResult, RouterHandlers, type SentriHonoEnv, type SentriHonoMiddleware, type SentriHonoVariables, SentriLogger, ServerAuthConfig, authorize, createAuthHono, createAuthRouter, createAuthorize, createErrorHandler, createPermit, getCurrentAccessToken, permit, protect };
@@ -0,0 +1 @@
1
+ import {generateKeyPairSync,createPrivateKey,createPublicKey,createHash,randomUUID}from'crypto';import {sql,Kysely}from'kysely';import Ge from'bcrypt';import Z from'jsonwebtoken';import {setCookie,deleteCookie}from'hono/cookie';import {Hono}from'hono';var Fr=Object.defineProperty;var n=(e,r)=>Fr(e,"name",{value:r,configurable:true});var Ke=Object.assign(Object.create(null),{UNAUTHORIZED:401,TOKEN_EXPIRED:401,TOKEN_INVALID:401,INVALID_CREDENTIALS:401,FORBIDDEN:403,USER_NOT_FOUND:404,IDENTIFIER_NOT_FOUND:404,USER_ALREADY_EXISTS:409,IDENTIFIER_ALREADY_EXISTS:409,INVALID_ROLE:400,VALIDATION_ERROR:400,CONFIGURATION_ERROR:500,TOKEN_REUSE:401}),l=class extends Error{static{n(this,"SentriError");}code;statusCode;constructor(r,t,s){super(t),this.name="SentriError",this.code=r,this.statusCode=s??Ke[r]??500;}};var $e=new WeakMap,He=32,Ve=10,Me=31;function Be(e){if(e.mode==="client"){if(!e.keyUri||e.keyUri.trim().length===0)throw new l("CONFIGURATION_ERROR","keyUri must not be empty");return}if(!e.secret||e.secret.trim().length===0)throw new l("CONFIGURATION_ERROR","secret must not be empty");if((e.algorithm??"HS256").startsWith("HS")&&e.secret.length<He)throw new l("CONFIGURATION_ERROR",`secret must be at least ${He} characters for HMAC algorithms`);let s=e.saltRounds??12;if(!Number.isInteger(s)||s<Ve||s>Me)throw new l("CONFIGURATION_ERROR",`saltRounds must be an integer between ${Ve} and ${Me}`);if(!e.validRoles||e.validRoles.length===0)throw new l("CONFIGURATION_ERROR","validRoles must contain at least one role");if(!e.dialect)throw new l("CONFIGURATION_ERROR","dialect is required in server mode")}n(Be,"validateConfig");function k(e){let r=$e.get(e);if(r)return r;let t={algorithm:e.algorithm??"HS256",accessExpiresIn:e.accessExpiresIn??"15m",refreshExpiresIn:e.refreshExpiresIn??"7d",saltRounds:e.saltRounds??12,validRoles:e.validRoles,validRolesSet:new Set(e.validRoles),cookieName:e.cookie?.name??"refresh_token",accessCookieName:e.accessCookie?.name??"access_token"};return $e.set(e,t),t}n(k,"resolveServerConfig");var Pr=/^(\d+)([smhdw])$/,Kr={s:1e3,m:6e4,h:36e5,d:864e5,w:6048e5},je=new Map;function K(e){if(typeof e=="number")return e*1e3;let r=je.get(e);if(r!==void 0)return r;let t=Pr.exec(e);if(!t?.[1]||!t?.[2])throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let s=Kr[t[2]];if(s===void 0)throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let i=parseInt(t[1],10)*s;return je.set(e,i),i}n(K,"parseExpiry");var N={info:n(()=>{},"info"),warn:n(()=>{},"warn"),error:n(()=>{},"error")};function p(e,r,t){return {service:e,event:r,...t}}n(p,"buildLogData");async function ze(e){await e.schema.createTable("sentri_users").ifNotExists().addColumn("id","varchar(36)",r=>r.primaryKey().notNull()).addColumn("password_hash","varchar(255)",r=>r.notNull()).addColumn("roles","text",r=>r.notNull().defaultTo("[]")).addColumn("created_at","timestamp",r=>r.notNull().defaultTo(sql`CURRENT_TIMESTAMP`)).execute(),await e.schema.createTable("sentri_sessions").ifNotExists().addColumn("id","varchar(36)",r=>r.primaryKey().notNull()).addColumn("user_id","varchar(36)",r=>r.notNull().references("sentri_users.id").onDelete("cascade")).addColumn("expires_at","timestamp",r=>r.notNull()).addColumn("ip_address","varchar(45)").addColumn("user_agent","text").addColumn("replaced_by","varchar(36)").addColumn("created_at","timestamp",r=>r.notNull().defaultTo(sql`CURRENT_TIMESTAMP`)).execute(),await e.schema.createTable("sentri_identifiers").ifNotExists().addColumn("id","varchar(36)",r=>r.primaryKey().notNull()).addColumn("user_id","varchar(36)",r=>r.notNull().references("sentri_users.id").onDelete("cascade")).addColumn("type","varchar(100)",r=>r.notNull()).addColumn("value","varchar(255)",r=>r.notNull().unique()).addColumn("created_at","timestamp",r=>r.notNull().defaultTo(sql`CURRENT_TIMESTAMP`)).execute(),await e.schema.createIndex("idx_sentri_sessions_user_id").ifNotExists().on("sentri_sessions").column("user_id").execute(),await e.schema.createIndex("idx_sentri_identifiers_user_id").ifNotExists().on("sentri_identifiers").column("user_id").execute();}n(ze,"runMigrations");var Xe=new Map;function E(e){let r=Xe.get(e);return r||(r=new Kysely({dialect:e}),Xe.set(e,r)),r}n(E,"getDatabase");async function G(e,r=12){return Ge.hash(e,r)}n(G,"hashPassword");async function W(e,r){return Ge.compare(e,r)}n(W,"verifyPassword");var We=new Map,Ze=new Map,Mr=3600*1e3;function Ye(e){let r=We.get(e);if(!r){let t=createPrivateKey(e),s=createPublicKey(t),i=s.export({format:"jwk"}),c=createHash("sha256").update(JSON.stringify({e:i.e,kty:i.kty,n:i.n})).digest("base64url"),d={...i,use:"sig",kid:c};r={kid:c,publicKey:s,jwk:d},We.set(e,r);}return r}n(Ye,"getOrBuildKey");function Qe(e){let{jwk:r}=Ye(e);return {keys:[r]}}n(Qe,"buildJwks");function er(e){return Ye(e).publicKey}n(er,"getPublicKeyFromPrivate");async function rr(e){let r=Date.now(),t=Ze.get(e);if(t&&r-t.fetchedAt<Mr)return t.publicKey;let s=await fetch(e);if(!s.ok)throw new l("CONFIGURATION_ERROR",`Failed to fetch public key from ${e}: HTTP ${s.status}`);let i=await s.json();if(!i.keys||i.keys.length===0)throw new l("CONFIGURATION_ERROR",`No keys found in JWKS response from ${e}`);let u=i.keys[0],c=createPublicKey({key:u,format:"jwk"});return Ze.set(e,{publicKey:c,fetchedAt:r}),c}n(rr,"fetchPublicKey");var tr=new Map,sr=new Map,nr=new Map;function or(e){return e.startsWith("RS")||e.startsWith("PS")}n(or,"isRSA");function ir(e){let r=tr.get(e);return r||(r={access:`${e}:access`,refresh:`${e}:refresh`},tr.set(e,r)),r}n(ir,"getHsSecrets");function Br(e){let r=sr.get(e);return r||(r=createPrivateKey(e),sr.set(e,r)),r}n(Br,"getRsPrivateKey");function ar(e){let r=k(e);if(or(r.algorithm)){let i=Br(e.secret);return {accessKey:i,refreshKey:i}}let{access:t,refresh:s}=ir(e.secret);return {accessKey:t,refreshKey:s}}n(ar,"getSigningKeys");function ur(e,r){let t=k(e);if(or(t.algorithm))return er(e.secret);let{access:s,refresh:i}=ir(e.secret);return r==="access"?s:i}n(ur,"getVerifyKey");function dr(e,r,t,s){let i=`${t}:${s}`,u=nr.get(i);return u||(u={expiresIn:t,algorithm:s},nr.set(i,u)),Z.sign(e,r,u)}n(dr,"sign");function cr(e,r,t){try{let s=Z.verify(e,r,{algorithms:[t]});if(typeof s=="string"||s===null)throw new l("TOKEN_INVALID","Token payload is not an object");return s}catch(s){throw s instanceof l?s:s instanceof Z.TokenExpiredError?new l("TOKEN_EXPIRED","Token has expired"):new l("TOKEN_INVALID","Token is invalid or malformed")}}n(cr,"verify");function J(e,r){let t=k(r),{accessKey:s}=ar(r);return dr(e,s,t.accessExpiresIn,t.algorithm)}n(J,"signAccessToken");function Y(e,r){let t=k(r),{refreshKey:s}=ar(r);return dr({sessionId:e},s,t.refreshExpiresIn,t.algorithm)}n(Y,"signRefreshToken");function de(e,r){let t=k(r),s=ur(r,"access");return cr(e,s,t.algorithm)}n(de,"verifyAccessToken");function Q(e,r){let t=k(r),s=ur(r,"refresh");return cr(e,s,t.algorithm)}n(Q,"verifyRefreshToken");function lr(e,r){try{let t=Z.verify(e,r,{algorithms:["RS256","RS384","RS512","PS256","PS384","PS512"]});if(typeof t=="string"||t===null)throw new l("TOKEN_INVALID","Token payload is not an object");return t}catch(t){throw t instanceof l?t:t instanceof Z.TokenExpiredError?new l("TOKEN_EXPIRED","Token has expired"):new l("TOKEN_INVALID","Token is invalid or malformed")}}n(lr,"verifyTokenWithPublicKey");function Ce(e){try{return JSON.parse(e)}catch{return []}}n(Ce,"parseRoles");function zr(e){return JSON.stringify(e)}n(zr,"serializeRoles");function hr(e){return {id:e.id,userId:e.user_id,type:e.type,value:e.value,createdAt:new Date(e.created_at)}}n(hr,"mapIdentifier");async function ee(e,r){let t=await e.selectFrom("sentri_identifiers as i").innerJoin("sentri_users as u","u.id","i.user_id").select(["u.id","u.password_hash","u.roles"]).where("i.value","=",r).executeTakeFirst();return t?{id:t.id,passwordHash:t.password_hash,roles:Ce(t.roles)}:null}n(ee,"findUserByIdentifierValue");async function ce(e,r){let t=await e.selectFrom("sentri_users").select(["id","password_hash","roles"]).where("id","=",r).executeTakeFirst();return t?{id:t.id,passwordHash:t.password_hash,roles:Ce(t.roles)}:null}n(ce,"findUserById");async function wr(e,r,t){let s=t.map(i=>({id:randomUUID(),user_id:r,type:i.type,value:i.value}));return await e.insertInto("sentri_identifiers").values(s).execute(),s.map(i=>({id:i.id,userId:i.user_id,type:i.type,value:i.value,createdAt:new Date}))}n(wr,"createIdentifiers");async function re(e,r){return (await e.selectFrom("sentri_identifiers").selectAll().where("user_id","=",r).orderBy("created_at","asc").execute()).map(hr)}n(re,"findIdentifiersByUserId");async function be(e,r,t){let s=await e.selectFrom("sentri_identifiers").selectAll().where("id","=",r).where("user_id","=",t).executeTakeFirst();return s?hr(s):null}n(be,"findIdentifierById");async function pr(e,r){let t=await e.selectFrom("sentri_identifiers").select(s=>s.fn.countAll().as("count")).where("user_id","=",r).executeTakeFirst();return Number(t?.count??0)}n(pr,"countIdentifiersByUserId");async function mr(e,r,t,s){await e.updateTable("sentri_identifiers").set({type:s.type,value:s.value}).where("id","=",r).where("user_id","=",t).execute();}n(mr,"updateIdentifier");async function yr(e,r,t){await e.deleteFrom("sentri_identifiers").where("user_id","=",r).where("id","in",t).execute();}n(yr,"deleteIdentifiers");async function Ir(e,r,t){await e.updateTable("sentri_users").set({password_hash:t}).where("id","=",r).execute();}n(Ir,"updateUserPassword");async function gr(e,r,t){await e.updateTable("sentri_users").set({roles:zr(t)}).where("id","=",r).execute();}n(gr,"updateUserRoles");async function Oe(e,r){let t=randomUUID();return await e.insertInto("sentri_sessions").values({id:t,user_id:r.userId,expires_at:r.expiresAt.toISOString(),ip_address:r.ipAddress??null,user_agent:r.userAgent??null}).execute(),{id:t}}n(Oe,"createSession");async function le(e,r){let t=await e.selectFrom("sentri_sessions as s").innerJoin("sentri_users as u","u.id","s.user_id").select(["s.id as session_id","s.user_id","s.expires_at","s.ip_address","s.user_agent","s.replaced_by","s.created_at as session_created_at","u.id as user_id_col","u.password_hash","u.roles"]).where("s.id","=",r).executeTakeFirst();return t?{id:t.session_id,userId:t.user_id,expiresAt:new Date(t.expires_at),ipAddress:t.ip_address,userAgent:t.user_agent,replacedBy:t.replaced_by,createdAt:new Date(t.session_created_at),user:{id:t.user_id_col,passwordHash:t.password_hash,roles:Ce(t.roles)}}:null}n(le,"findSessionById");async function fe(e,r){await e.deleteFrom("sentri_sessions").where("id","=",r).execute();}n(fe,"deleteSession");async function _r(e,r,t){await e.updateTable("sentri_sessions").set({replaced_by:t}).where("id","=",r).execute();}n(_r,"markSessionReplaced");async function he(e,r){await e.deleteFrom("sentri_sessions").where("user_id","=",r).execute();}n(he,"deleteAllSessionsForUser");async function Rr(e,r){return (await e.selectFrom("sentri_sessions").selectAll().where("user_id","=",r).where("replaced_by","is",null).where("expires_at",">",new Date).execute()).map(s=>({id:s.id,userId:s.user_id,expiresAt:new Date(s.expires_at),ipAddress:s.ip_address,userAgent:s.user_agent,replacedBy:s.replaced_by,createdAt:new Date(s.created_at)}))}n(Rr,"findSessionsByUserId");async function we(e,r,t){let s=k(r),i=E(r.dialect),u=e.roles??[],c=u.filter(_=>!s.validRolesSet.has(_));if(c.length>0)return {success:false,error:new l("INVALID_ROLE",`Invalid roles: ${c.join(", ")}`)};if(!e.identifiers||e.identifiers.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one identifier is required")};let d=e.identifiers.map(_=>({type:_.type.trim(),value:_.value.trim()}));if(new Set(d.map(_=>_.value)).size!==d.length)return {success:false,error:new l("VALIDATION_ERROR","Duplicate identifier values in request")};for(let _ of d)if(await ee(i,_.value))return {success:false,error:new l("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${_.value}`)};let h=await G(e.password,s.saltRounds),{userId:D,identifierRows:b}=await i.transaction().execute(async _=>{let F=randomUUID();await _.insertInto("sentri_users").values({id:F,password_hash:h,roles:JSON.stringify(u)}).execute();let j=d.map(ae=>({id:randomUUID(),user_id:F,type:ae.type,value:ae.value}));return await _.insertInto("sentri_identifiers").values(j).execute(),{userId:F,identifierRows:j}}),T={success:true,user:{id:D,roles:u,identifiers:b.map(_=>({id:_.id,type:_.type,value:_.value}))}};return r.hooks?.onRegister&&await r.hooks.onRegister(T.user),T}n(we,"register");async function pe(e,r,t){let s=k(r),i=E(r.dialect),u=await ee(i,e.identifier.trim());if(!u){let T=new l("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,T,{ip:t?.ip??""}),{success:false,error:T}}if(!await W(e.password,u.passwordHash)){let T=new l("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,T,{ip:t?.ip??""}),{success:false,error:T}}let d=new Date(Date.now()+K(s.refreshExpiresIn)),a=await Oe(i,{userId:u.id,expiresAt:d,ipAddress:t?.ip??null,userAgent:t?.userAgent??null}),h={id:u.id,roles:u.roles},D=J({id:u.id,roles:u.roles,sessionId:a.id},r),b=Y(a.id,r);return r.hooks?.onLoginSuccess&&await r.hooks.onLoginSuccess(h,{ip:t?.ip??"",userAgent:t?.userAgent??""}),{success:true,accessToken:D,refreshToken:b,user:h}}n(pe,"login");async function $(e,r,t){let s=k(r),i=E(r.dialect),u;try{({sessionId:u}=Q(e,r));}catch(T){return T instanceof l?{success:false,error:T}:{success:false,error:new l("TOKEN_INVALID","Invalid refresh token")}}let c=await le(i,u);if(!c)return {success:false,error:new l("UNAUTHORIZED","Session not found or revoked")};if(c.replacedBy)return await he(i,c.userId),{success:false,error:new l("TOKEN_REUSE","Token reuse detected. All sessions revoked.")};if(c.expiresAt.getTime()<Date.now())return await fe(i,u),{success:false,error:new l("TOKEN_EXPIRED","Session has expired")};let d=new Date(Date.now()+K(s.refreshExpiresIn)),a=await Oe(i,{userId:c.userId,expiresAt:d,ipAddress:t?.ip??null,userAgent:t?.userAgent??null});await _r(i,u,a.id);let h={id:c.user.id,roles:c.user.roles},D=J({...h,sessionId:a.id},r),b=Y(a.id,r);return {success:true,accessToken:D,refreshToken:b,user:h}}n($,"refresh");async function me(e,r){let t=E(r.dialect),s;try{({sessionId:s}=Q(e,r));}catch{return}let i=await le(t,s);i&&(await fe(t,s),r.hooks?.onLogout&&await r.hooks.onLogout(i.userId));}n(me,"logout");async function ye(e,r){let t=E(r.dialect);await he(t,e),r.hooks?.onLogout&&await r.hooks.onLogout(e);}n(ye,"logoutAll");async function Ie(e,r){let t=E(r.dialect),s=await ce(t,e);if(!s)return {success:false,error:new l("USER_NOT_FOUND","User not found")};let i=await re(t,e);return {success:true,user:{id:s.id,roles:s.roles,identifiers:i.map(u=>({id:u.id,type:u.type,value:u.value}))}}}n(Ie,"getUser");async function ge(e,r,t,s){let i=k(s),u=E(s.dialect),c=await ce(u,e);if(!c)return {success:false,error:new l("USER_NOT_FOUND","User not found")};if(!await W(r,c.passwordHash))return {success:false,error:new l("INVALID_CREDENTIALS","Invalid credentials")};let a=await G(t,i.saltRounds);return await Ir(u,e,a),await he(u,e),s.hooks?.onPasswordChanged&&await s.hooks.onPasswordChanged(e),{success:true}}n(ge,"changePassword");async function _e(e,r,t){let s=k(t),i=E(t.dialect),u=r.filter(h=>!s.validRolesSet.has(h));if(u.length>0)return {success:false,error:new l("INVALID_ROLE",`Invalid roles: ${u.join(", ")}`)};let c=await ce(i,e);if(!c)return {success:false,error:new l("USER_NOT_FOUND","User not found")};let d=new Set(c.roles);for(let h of r)d.add(h);let a=Array.from(d);return await gr(i,e,a),{success:true,user:{id:c.id,roles:a}}}n(_e,"assignRoles");async function Re(e,r,t){let s=E(t.dialect);if(r.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one identifier is required")};let i=r.map(d=>({type:d.type.trim(),value:d.value.trim()}));if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new l("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i)if(await ee(s,d.value))return {success:false,error:new l("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)};return await wr(s,e,i),{success:true,identifiers:(await re(s,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}n(Re,"bulkCreateIdentifiers");async function ke(e,r,t){let s=E(t.dialect);if(r.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one update is required")};let i=r.map(d=>({id:d.id,type:d.type.trim(),value:d.value.trim()}));if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new l("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i){let a=await be(s,d.id,e);if(!a)return {success:false,error:new l("IDENTIFIER_NOT_FOUND",`Identifier not found: ${d.id}`)};if(a.value!==d.value&&await ee(s,d.value))return {success:false,error:new l("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)}}return await s.transaction().execute(async d=>{for(let a of i)await mr(d,a.id,e,{type:a.type,value:a.value});}),{success:true,identifiers:(await re(s,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}n(ke,"bulkUpdateIdentifiers");async function ve(e,r,t){let s=E(t.dialect);if(r.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one ID is required")};let i=Array.from(new Set(r));for(let d of i)if(!await be(s,d,e))return {success:false,error:new l("IDENTIFIER_NOT_FOUND",`Identifier not found: ${d}`)};return await pr(s,e)-i.length<1?{success:false,error:new l("VALIDATION_ERROR","Cannot delete all identifiers \u2014 at least one must remain")}:(await yr(s,e,i),{success:true,identifiers:(await re(s,e)).map(d=>({id:d.id,type:d.type,value:d.value}))})}n(ve,"bulkDeleteIdentifiers");async function vr(e,r){let t=E(r.dialect);return (await Rr(t,e)).map(i=>({id:i.id,ipAddress:i.ipAddress,userAgent:i.userAgent,createdAt:i.createdAt,expiresAt:i.expiresAt}))}n(vr,"getSessions");async function Ar(e,r,t){let s=E(t.dialect),i=await le(s,r);i&&i.userId===e&&await fe(s,r);}n(Ar,"revokeSession");function O(e){return k(e).cookieName}n(O,"getCookieName");function Ae(e){return k(e).accessCookieName}n(Ae,"getAccessCookieName");function U(e,r){if(!e)return;let t=`${r}=`,s=0;for(;s<e.length;){for(;s<e.length&&e[s]===" ";)s++;let i=e.indexOf(";",s),u=i===-1?e.length:i;if(e.startsWith(t,s))return e.slice(s+t.length,u);s=u+1;}}n(U,"readCookie");function xr(e){let r=(e??"strict").toLowerCase();return r==="lax"?"Lax":r==="none"?"None":"Strict"}n(xr,"normalizeSameSite");function te(e,r,t){let s=t.cookie??{},i=K(k(t).refreshExpiresIn)/1e3;setCookie(e,O(t),r,{httpOnly:s.httpOnly??true,secure:s.secure??false,sameSite:xr(s.sameSite),path:s.path??"/",maxAge:i});}n(te,"setCookieFromConfig");function Ee(e,r){let t=r.cookie??{};deleteCookie(e,O(r),{path:t.path??"/"});}n(Ee,"clearCookieFromConfig");function se(e,r,t){if(!t.accessCookie)return;let s=t.accessCookie,i=K(k(t).accessExpiresIn)/1e3;setCookie(e,Ae(t),r,{httpOnly:false,secure:s.secure??false,sameSite:xr(s.sameSite),path:s.path??"/",maxAge:i});}n(se,"setAccessCookieFromConfig");function Ue(e,r){if(!r.accessCookie)return;let t=r.accessCookie;deleteCookie(e,Ae(r),{path:t.path??"/"});}n(Ue,"clearAccessCookieFromConfig");function Te(e,r){let t=e.req.header("authorization");return t?.startsWith("Bearer ")?t.slice(7):U(e.req.header("cookie"),Ae(r))}n(Te,"getCurrentAccessToken");function L(e){let r=e.logger??N,t=e.loggerService??"sentri";return e.mode==="client"?Xr(e.keyUri,r,t):Gr(e,r,t)}n(L,"protect");function Xr(e,r,t){return async(s,i)=>{let u=s.req.header("authorization"),c=u?.startsWith("Bearer ")?u.slice(7):void 0,d=s.get("requestId");if(!c)throw r.warn(p(t,"auth.protect.failure",{mode:"client",errorCode:"UNAUTHORIZED",...d!==void 0&&{requestId:d}})),new l("UNAUTHORIZED","Missing or malformed Authorization header");try{let a=await rr(e),h=lr(c,a);s.set("user",{id:h.id,roles:h.roles}),r.info(p(t,"auth.protect.success",{mode:"client",userId:h.id,...d!==void 0&&{requestId:d}})),await i();}catch(a){let h=a instanceof l?a.code:"TOKEN_INVALID";throw r.warn(p(t,"auth.protect.failure",{mode:"client",errorCode:h,...d!==void 0&&{requestId:d}})),a}}}n(Xr,"protectClient");function Gr(e,r,t){return async(s,i)=>{let u=Te(s,e),c=s.get("requestId");if(!u)throw r.warn(p(t,"auth.protect.failure",{mode:"server",errorCode:"UNAUTHORIZED",...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Missing or malformed Authorization header");try{let d=de(u,e);if(e.isTokenRevoked&&await e.isTokenRevoked(d.sessionId))throw r.warn(p(t,"auth.protect.token_revoked",{mode:"server",userId:d.id,...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Token has been revoked");s.set("user",{id:d.id,roles:d.roles}),r.info(p(t,"auth.protect.success",{mode:"server",userId:d.id,...c!==void 0&&{requestId:c}})),await i();}catch(d){if(d instanceof l&&d.code==="TOKEN_EXPIRED"){let a=U(s.req.header("cookie"),O(e));if(!a)throw r.warn(p(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED",...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Token expired. Please login again.");let h;try{h=await $(a,e);}catch{throw r.warn(p(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED",...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Session expired. Please login again.")}if(!h.success)throw r.warn(p(t,"auth.protect.failure",{mode:"server",errorCode:h.error.code,...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Session expired. Please login again.");te(s,h.refreshToken,e),se(s,h.accessToken,e),s.header("X-New-Access-Token",h.accessToken),s.set("user",h.user),r.info(p(t,"auth.protect.auto_refresh",{mode:"server",userId:h.user.id,...c!==void 0&&{requestId:c}})),await i();}else {let a=d instanceof l?d.code:"TOKEN_INVALID";throw r.warn(p(t,"auth.protect.failure",{mode:"server",errorCode:a,...c!==void 0&&{requestId:c}})),d}}}}n(Gr,"protectServer");function Nr(e,r,t){let s=`Requires one of roles: ${e.join(", ")}`;return async(i,u)=>{let c=i.get("user"),d=i.get("requestId");if(!c)throw r.warn(p(t,"auth.authorize.unauthenticated",{requiredRoles:e,...d!==void 0&&{requestId:d}})),new l("UNAUTHORIZED","Not authenticated");let a=c.roles;if(!e.some(h=>a.includes(h)))throw r.warn(p(t,"auth.authorize.denied",{userId:c.id,userRoles:[...a],requiredRoles:e,...d!==void 0&&{requestId:d}})),new l("FORBIDDEN",s);r.info(p(t,"auth.authorize.passed",{userId:c.id,userRoles:[...a],requiredRoles:e,...d!==void 0&&{requestId:d}})),await u();}}n(Nr,"createAuthorizeHandler");function ne(e,r){return n(function(...s){return Nr(s,e,r)},"authorize")}n(ne,"createAuthorize");function Wr(...e){return Nr(e,N,"sentri")}n(Wr,"authorize");var Zr=new l("FORBIDDEN","You do not have permission to perform this action");function Dr(e,r,t){return async(s,i)=>{let u=s.get("user"),c=s.get("requestId");if(!u)throw r.warn(p(t,"auth.permit.unauthenticated",{...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Not authenticated");let d=u.id;if(e.roles&&e.roles.length>0){let a=u.roles;if(e.roles.some(h=>a.includes(h))){r.info(p(t,"auth.permit.role_bypass",{userId:d,bypassedByRole:true,...c!==void 0&&{requestId:c}})),await i();return}}try{let a=e.check(s);if(a instanceof Promise?await a:a)r.info(p(t,"auth.permit.passed",{userId:d,...c!==void 0&&{requestId:c}})),await i();else throw r.warn(p(t,"auth.permit.denied",{userId:d,...c!==void 0&&{requestId:c}})),Zr}catch(a){throw a}}}n(Dr,"createPermitHandler");function oe(e,r){return n(function(s){return Dr(typeof s=="function"?{check:s}:s,e,r)},"permit")}n(oe,"createPermit");function Jr(e){return Dr(typeof e=="function"?{check:e}:e,N,"sentri")}n(Jr,"permit");function ie(e){return (r,t)=>r instanceof l?t.json({error:true,statusCode:r.statusCode,code:r.code,message:r.message,data:null},r.statusCode):(e?.onUnhandled?.(r),t.json({error:true,statusCode:500,code:"INTERNAL_SERVER_ERROR",message:"Internal server error",data:null},500))}n(ie,"createErrorHandler");var Le=class{static{n(this,"InMemoryRateLimiter");}store=new Map;constructor(){setInterval(()=>{let r=Date.now();for(let[t,s]of this.store.entries())s.expiresAt<r&&this.store.delete(t);},6e4).unref();}async consume(r,t,s){let i=Date.now(),u=this.store.get(r);if((!u||u.expiresAt<i)&&(u={count:0,expiresAt:i+s},this.store.set(r,u)),u.count>t){let c=Math.ceil((u.expiresAt-i)/1e3);throw new Error(`Rate limit exceeded. Try again in ${c} seconds.`)}u.count++;}},qe=class{static{n(this,"RedisRateLimiter");}redis;logger;constructor(r,t){this.redis=r,this.logger=t;}async consume(r,t,s){let i=`sentri:rl:${r}`,u=Math.ceil(s/1e3);try{let c=await this.redis.multi().incr(i).expire(i,u,"NX").exec();if(!c||c.length===0)return;if(c[0][1]>t)throw new Error("Rate limit exceeded. Try again later.")}catch(c){if(c instanceof Error&&c.message.includes("Rate limit exceeded"))throw c;this.logger.error({msg:"Redis RateLimiter failed, bypassing limit",error:c});}}},H=null;async function Sr(e,r){if(H)return H;if(e)try{let{Redis:t}=await import('ioredis'),s=new t(e,{lazyConnect:!0,maxRetriesPerRequest:1});return await s.connect(),r?.info({msg:"Connected to Redis for Rate Limiting"}),H=new qe(s,r??N),H}catch(t){r?.warn({msg:"Failed to connect to Redis for Rate Limiting, falling back to InMemoryRateLimiter",error:t});}return H=new Le,H}n(Sr,"getRateLimiter");var xe=8,V=72,Ne=255,Cr=100,M=50;function g(e){return new l("VALIDATION_ERROR",e)}n(g,"badRequest");function x(e,r,t,s){return e.json({error:false,statusCode:r,message:t,data:s},r)}n(x,"ok");function C(e,r){return e.json({error:true,statusCode:r.statusCode,code:r.code,message:r.message,data:null},r.statusCode)}n(C,"fail");async function q(e){try{let r=await e.req.json();if(r==null||typeof r!="object"||Array.isArray(r))throw new l("VALIDATION_ERROR","Request body is missing or not a JSON object. Did you set Content-Type: application/json?");return r}catch(r){throw r instanceof l?r:new l("VALIDATION_ERROR","Invalid JSON body. Did you set Content-Type: application/json?")}}n(q,"parseBody");function Qr(e,r){if(!r.apiKey)return;let t=e.req.header("x-api-key");if(typeof t!="string"||t!==r.apiKey)throw new l("UNAUTHORIZED","Invalid or missing API key")}n(Qr,"validateApiKey");function Fe(e){if(e)try{let r=e();r instanceof Promise&&r.catch(()=>{});}catch{}}n(Fe,"fireHook");function et(e){return e.startsWith("RS")||e.startsWith("PS")}n(et,"isRSA");function Pe(e,r){if(typeof e!="object"||e===null||Array.isArray(e))throw g(`identifiers[${r}] must be an object`);let t=e;if(typeof t.type!="string"||t.type.trim().length===0)throw g(`identifiers[${r}].type is required and must be a non-empty string`);if(t.type.length>Cr)throw g(`identifiers[${r}].type must not exceed ${Cr} characters`);if(typeof t.value!="string"||t.value.trim().length===0)throw g(`identifiers[${r}].value is required and must be a non-empty string`);if(t.value.length>Ne)throw g(`identifiers[${r}].value must not exceed ${Ne} characters`);return {type:t.type,value:t.value}}n(Pe,"validateIdentifierInput");function R(e){let r=e.get("requestId");return r!==void 0?{requestId:r}:{}}n(R,"reqId");function br(e){let r=new Hono,t=e,s=k(t),i=e.logger??N,u=e.loggerService??"sentri",c=L(e),d=ne(i,u),a=oe(i,u),h=Sr(e.redisUrl,i),D=e.router?.register??(o=>we(o,t)),b=e.router?.login??(o=>pe(o,t)),T=e.router?.refresh??(o=>$(o,t)),_=e.router?.logout??(o=>o!==void 0?me(o,t):Promise.resolve()),F=e.router?.logoutAll??(o=>ye(o,t)),j=e.router?.getUser??(o=>Ie(o,t)),ae=e.router?.assignRoles??((o,w)=>_e(o,w,t)),Or=e.router?.changePassword??((o,w,f)=>ge(o,w,f,t)),Ur=e.router?.bulkCreateIdentifiers??((o,w)=>Re(o,w,t)),Lr=e.router?.bulkUpdateIdentifiers??((o,w)=>ke(o,w,t)),qr=e.router?.bulkDeleteIdentifiers??((o,w)=>ve(o,w,t)),ue=a(o=>!!o.get("user"));return r.onError(ie()),et(s.algorithm)&&r.get("/keys",o=>(o.header("Cache-Control","public, max-age=3600"),o.json(Qe(e.secret)))),r.post("/register",async o=>{let w=Date.now();try{Qr(o,e);let f=await q(o),{identifiers:y,password:I,roles:m}=f;if(!Array.isArray(y)||y.length===0)throw g("identifiers is required and must be a non-empty array");if(y.length>M)throw g(`identifiers must not exceed ${M} entries`);let v=y.map((X,De)=>Pe(X,De));if(typeof I!="string"||I.length<xe)throw g(`password is required and must be at least ${xe} characters`);if(I.length>V)throw g(`password must not exceed ${V} characters`);if(m!==void 0&&!Array.isArray(m))throw g("roles must be an array of strings when provided");if(Array.isArray(m)&&!m.every(X=>typeof X=="string"))throw g("each role must be a string");let S=Array.isArray(m)?m:void 0,A=S!==void 0?{identifiers:v,password:I,roles:S}:{identifiers:v,password:I},P=o.req.header("x-forwarded-for")||o.env?.incoming?.ip||"127.0.0.1",B=o.req.header("user-agent")||"Unknown";if(e.rateLimit!==!1){let X=await h,De=typeof e.rateLimit=="object"?e.rateLimit.maxRegisterAttempts??5:5;await X.consume(`register:${P}`,De,900*1e3);}let z=await D(A,{ip:P,userAgent:B});return z.success?(i.info(p(u,"auth.register.success",{userId:z.user.id,duration_ms:Date.now()-w,...R(o)})),x(o,201,"User registered successfully",{user:z.user})):(i.warn(p(u,"auth.register.failure",{errorCode:z.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,z.error))}catch(f){throw f}}),r.post("/login",async o=>{let w=Date.now();try{let f=await q(o),{identifier:y,password:I}=f;if(typeof y!="string"||y.trim().length===0)throw g("identifier is required and must be a non-empty string");if(y.length>Ne)throw g(`identifier must not exceed ${Ne} characters`);if(typeof I!="string"||I.length===0)throw g("password is required");if(I.length>V)throw g(`password must not exceed ${V} characters`);let m=y.trim(),v=o.req.header("x-forwarded-for")||o.env?.incoming?.ip||"127.0.0.1",S=o.req.header("user-agent")||"Unknown";if(e.rateLimit!==!1){let P=await h,B=typeof e.rateLimit=="object"?e.rateLimit.maxLoginAttempts??5:5;await P.consume(`login:${v}`,B,900*1e3);}let A=await b({identifier:m,password:I},{ip:v,userAgent:S});return A.success?(Fe(()=>e.hooks?.onLoginSuccess?.(A.user,{ip:v,userAgent:S})),te(o,A.refreshToken,e),se(o,A.accessToken,e),i.info(p(u,"auth.login.success",{userId:A.user.id,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Login successful",{accessToken:A.accessToken,user:A.user})):(Fe(()=>e.hooks?.onLoginFailed?.(m,A.error,{ip:v})),i.warn(p(u,"auth.login.failure",{errorCode:A.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,A.error))}catch(f){throw f}}),r.post("/refresh",async o=>{let w=Date.now();try{let f=U(o.req.header("cookie"),O(e));if(!f)throw new l("UNAUTHORIZED","Refresh token cookie is missing");let y=o.req.header("x-forwarded-for")||o.env?.incoming?.ip||"127.0.0.1",I=o.req.header("user-agent")||"Unknown",m=await T(f,{ip:y,userAgent:I});return m.success?(te(o,m.refreshToken,e),se(o,m.accessToken,e),i.info(p(u,"auth.refresh.success",{userId:m.user.id,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Token refreshed",{accessToken:m.accessToken})):(Ee(o,e),i.warn(p(u,"auth.refresh.failure",{errorCode:m.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,m.error))}catch(f){throw f}}),r.post("/logout",async o=>{let w=Date.now();try{let f=U(o.req.header("cookie"),O(e));return await _(f),Ee(o,e),Ue(o,e),i.info(p(u,"auth.logout",{duration_ms:Date.now()-w,...R(o)})),x(o,200,"Logged out",null)}catch(f){throw f}}),r.post("/logout-all",c,async o=>{let w=Date.now();try{let f=o.get("user").id;return await F(f),Fe(()=>e.hooks?.onLogout?.(f)),Ee(o,e),Ue(o,e),i.info(p(u,"auth.logout_all",{userId:f,duration_ms:Date.now()-w,...R(o)})),x(o,200,"All sessions revoked",null)}catch(f){throw f}}),r.get("/me",c,async o=>{let w=Date.now();try{let f=await j(o.get("user").id);return f.success?(i.info(p(u,"auth.me.success",{userId:f.user.id,duration_ms:Date.now()-w,...R(o)})),x(o,200,"OK",f.user)):(i.warn(p(u,"auth.me.failure",{userId:o.get("user").id,errorCode:f.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,f.error))}catch(f){throw f}}),r.get("/me/identifiers",c,async o=>{let w=Date.now();try{let f=await j(o.get("user").id);return f.success?(i.info(p(u,"auth.me.identifiers.success",{userId:f.user.id,count:f.user.identifiers?.length??0,duration_ms:Date.now()-w,...R(o)})),x(o,200,"OK",{identifiers:f.user.identifiers??[]})):(i.warn(p(u,"auth.me.identifiers.failure",{userId:o.get("user").id,errorCode:f.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,f.error))}catch(f){throw f}}),r.post("/me/identifiers",c,ue,async o=>{let w=Date.now();try{let f=await q(o),{identifiers:y}=f;if(!Array.isArray(y)||y.length===0)throw g("identifiers is required and must be a non-empty array");if(y.length>M)throw g(`identifiers must not exceed ${M} entries`);let I=y.map((v,S)=>Pe(v,S)),m=await Ur(o.get("user").id,I);return m.success?(i.info(p(u,"auth.identifiers.created",{userId:o.get("user").id,count:m.identifiers.length,duration_ms:Date.now()-w,...R(o)})),x(o,201,"Identifiers added successfully",{identifiers:m.identifiers})):(i.warn(p(u,"auth.identifiers.create_failure",{userId:o.get("user").id,errorCode:m.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,m.error))}catch(f){throw f}}),r.put("/me/identifiers",c,ue,async o=>{let w=Date.now();try{let f=await q(o),{identifiers:y}=f;if(!Array.isArray(y)||y.length===0)throw g("identifiers is required and must be a non-empty array");if(y.length>M)throw g(`identifiers must not exceed ${M} entries`);let I=y.map((v,S)=>{if(typeof v!="object"||v===null||Array.isArray(v))throw g(`identifiers[${S}] must be an object`);let A=v;if(typeof A.id!="string"||A.id.trim().length===0)throw g(`identifiers[${S}].id is required and must be a non-empty string`);let{type:P,value:B}=Pe(v,S);return {id:A.id,type:P,value:B}}),m=await Lr(o.get("user").id,I);return m.success?(i.info(p(u,"auth.identifiers.updated",{userId:o.get("user").id,count:m.identifiers.length,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Identifiers updated successfully",{identifiers:m.identifiers})):(i.warn(p(u,"auth.identifiers.update_failure",{userId:o.get("user").id,errorCode:m.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,m.error))}catch(f){throw f}}),r.delete("/me/identifiers",c,ue,async o=>{let w=Date.now();try{let f=await q(o),{ids:y}=f;if(!Array.isArray(y)||y.length===0)throw g("ids is required and must be a non-empty array of strings");if(!y.every(m=>typeof m=="string"))throw g("each id must be a string");let I=await qr(o.get("user").id,y);return I.success?(i.info(p(u,"auth.identifiers.deleted",{userId:o.get("user").id,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Identifiers deleted successfully",{identifiers:I.identifiers})):(i.warn(p(u,"auth.identifiers.delete_failure",{userId:o.get("user").id,errorCode:I.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,I.error))}catch(f){throw f}}),r.patch("/me/password",c,ue,async o=>{let w=Date.now();try{let f=await q(o),{currentPassword:y,newPassword:I}=f;if(typeof y!="string"||y.length===0)throw g("currentPassword is required");if(typeof I!="string"||I.length<xe)throw g(`newPassword must be at least ${xe} characters`);if(I.length>V)throw g(`newPassword must not exceed ${V} characters`);if(y===I)throw g("newPassword must be different from currentPassword");let m=await Or(o.get("user").id,y,I);return m.success?(i.info(p(u,"auth.password.changed",{userId:o.get("user").id,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Password updated successfully. All sessions have been revoked.",null)):(i.warn(p(u,"auth.password.change_failure",{userId:o.get("user").id,errorCode:m.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,m.error))}catch(f){throw f}}),r.post("/users/:userId/roles",c,d("admin"),async o=>{let w=Date.now();try{let f=await q(o),{roles:y}=f,I=o.req.param("userId");if(!I)throw g("userId is required");if(!Array.isArray(y)||y.length===0)throw g("roles must be a non-empty array of strings");if(!y.every(v=>typeof v=="string"))throw g("each role must be a string");let m=await ae(I,y);return m.success?(i.info(p(u,"auth.roles.assigned",{targetUserId:I,roles:y,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Roles assigned successfully",{user:m.user})):(i.warn(p(u,"auth.roles.assign_failure",{targetUserId:I,errorCode:m.error.code,duration_ms:Date.now()-w,...R(o)})),C(o,m.error))}catch(f){throw f}}),r.get("/sessions",L(e),async o=>{let w=Date.now(),f=o.get("user").id,I=await(e.router?.getSessions??(m=>vr(m,t)))(f);return i.info(p(u,"auth.sessions.get",{userId:f,duration_ms:Date.now()-w,...R(o)})),x(o,200,"OK",{sessions:I})}),r.delete("/sessions/:id",L(e),async o=>{let w=Date.now(),f=o.get("user").id,y=o.req.param("id");return await(e.router?.revokeSession??((m,v)=>Ar(m,v,t)))(f,y),i.info(p(u,"auth.sessions.revoke",{userId:f,sessionId:y,duration_ms:Date.now()-w,...R(o)})),x(o,200,"Session revoked",null)}),r}n(br,"createAuthRouter");function zs(e){if(e.mode==="client"){let a={mode:"client",keyUri:e.keyUri,...e.validRoles!==void 0&&{validRoles:e.validRoles},...e.logger!==void 0&&{logger:e.logger},...e.loggerService!==void 0&&{loggerService:e.loggerService}};Be(a);let h=a.logger??N,D=a.loggerService??"sentri",b=ne(h,D),T=oe(h,D);return {protect:n(()=>L(a),"protect"),authorize:n((..._)=>b(..._),"authorize"),permit:n(_=>T(_),"permit"),errorHandler:n(_=>ie(_),"errorHandler")}}let{privateKey:r}=generateKeyPairSync("rsa",{modulusLength:2048,privateKeyEncoding:{type:"pkcs8",format:"pem"},publicKeyEncoding:{type:"spki",format:"pem"}}),t={mode:"server",dialect:e.dialect,secret:r,algorithm:"RS256",validRoles:e.validRoles,...e.accessExpiresIn!==void 0&&{accessExpiresIn:e.accessExpiresIn},...e.refreshExpiresIn!==void 0&&{refreshExpiresIn:e.refreshExpiresIn},...e.saltRounds!==void 0&&{saltRounds:e.saltRounds},...e.apiKey!==void 0&&{apiKey:e.apiKey},...e.cookie!==void 0&&{cookie:e.cookie},...e.accessCookie!==void 0&&{accessCookie:e.accessCookie},...e.hooks!==void 0&&{hooks:e.hooks},...e.router!==void 0&&{router:e.router},...e.isTokenRevoked!==void 0&&{isTokenRevoked:e.isTokenRevoked},...e.redisUrl!==void 0&&{redisUrl:e.redisUrl},...e.logger!==void 0&&{logger:e.logger},...e.loggerService!==void 0&&{loggerService:e.loggerService}},s=t.logger??N,i=t.loggerService??"sentri",u=k(t),c=ne(s,i),d=oe(s,i);return {protect:n(()=>L(t),"protect"),authorize:n((...a)=>c(...a),"authorize"),permit:n(a=>d(a),"permit"),errorHandler:n(a=>ie(a),"errorHandler"),router:n(()=>br(t),"router"),migrate:n(()=>ze(E(t.dialect)),"migrate"),getCurrentAccessToken:n(a=>Te(a,t),"getCurrentAccessToken"),hashPassword:n(a=>G(a,u.saltRounds),"hashPassword"),verifyPassword:n((a,h)=>W(a,h),"verifyPassword"),signAccessToken:n(a=>J(a,t),"signAccessToken"),signRefreshToken:n(a=>Y(a,t),"signRefreshToken"),verifyAccessToken:n(a=>de(a,t),"verifyAccessToken"),verifyRefreshToken:n(a=>Q(a,t),"verifyRefreshToken"),register:n(a=>we(a,t),"register"),login:n(a=>pe(a,t),"login"),refresh:n(a=>$(a,t),"refresh"),logout:n(a=>me(a,t),"logout"),logoutAll:n(a=>ye(a,t),"logoutAll"),getUser:n(a=>Ie(a,t),"getUser"),changePassword:n((a,h,D)=>ge(a,h,D,t),"changePassword"),assignRoles:n((a,h)=>_e(a,h,t),"assignRoles"),bulkCreateIdentifiers:n((a,h)=>Re(a,h,t),"bulkCreateIdentifiers"),bulkUpdateIdentifiers:n((a,h)=>ke(a,h,t),"bulkUpdateIdentifiers"),bulkDeleteIdentifiers:n((a,h)=>ve(a,h,t),"bulkDeleteIdentifiers")}}n(zs,"createAuthHono");export{Ke as SENTRI_ERROR_STATUS,l as SentriError,Wr as authorize,zs as createAuthHono,br as createAuthRouter,ne as createAuthorize,ie as createErrorHandler,oe as createPermit,Te as getCurrentAccessToken,Jr as permit,L as protect};
@@ -0,0 +1 @@
1
+ 'use strict';var crypto=require('crypto'),kysely=require('kysely'),Ge=require('bcrypt'),W=require('jsonwebtoken'),Gr=require('@koa/router');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Ge__default=/*#__PURE__*/_interopDefault(Ge);var W__default=/*#__PURE__*/_interopDefault(W);var Gr__default=/*#__PURE__*/_interopDefault(Gr);var Ur=Object.defineProperty;var n=(e,r)=>Ur(e,"name",{value:r,configurable:true});var $e=Object.assign(Object.create(null),{UNAUTHORIZED:401,TOKEN_EXPIRED:401,TOKEN_INVALID:401,INVALID_CREDENTIALS:401,FORBIDDEN:403,USER_NOT_FOUND:404,IDENTIFIER_NOT_FOUND:404,USER_ALREADY_EXISTS:409,IDENTIFIER_ALREADY_EXISTS:409,INVALID_ROLE:400,VALIDATION_ERROR:400,CONFIGURATION_ERROR:500,TOKEN_REUSE:401}),l=class extends Error{static{n(this,"SentriError");}code;statusCode;constructor(r,t,s){super(t),this.name="SentriError",this.code=r,this.statusCode=s??$e[r]??500;}};var He=new WeakMap,Ve=32,Me=10,Be=31;function Ce(e){if(e.mode==="client"){if(!e.keyUri||e.keyUri.trim().length===0)throw new l("CONFIGURATION_ERROR","keyUri must not be empty");return}if(!e.secret||e.secret.trim().length===0)throw new l("CONFIGURATION_ERROR","secret must not be empty");if((e.algorithm??"HS256").startsWith("HS")&&e.secret.length<Ve)throw new l("CONFIGURATION_ERROR",`secret must be at least ${Ve} characters for HMAC algorithms`);let s=e.saltRounds??12;if(!Number.isInteger(s)||s<Me||s>Be)throw new l("CONFIGURATION_ERROR",`saltRounds must be an integer between ${Me} and ${Be}`);if(!e.validRoles||e.validRoles.length===0)throw new l("CONFIGURATION_ERROR","validRoles must contain at least one role");if(!e.dialect)throw new l("CONFIGURATION_ERROR","dialect is required in server mode")}n(Ce,"validateConfig");function A(e){let r=He.get(e);if(r)return r;let t={algorithm:e.algorithm??"HS256",accessExpiresIn:e.accessExpiresIn??"15m",refreshExpiresIn:e.refreshExpiresIn??"7d",saltRounds:e.saltRounds??12,validRoles:e.validRoles,validRolesSet:new Set(e.validRoles),cookieName:e.cookie?.name??"refresh_token",accessCookieName:e.accessCookie?.name??"access_token"};return He.set(e,t),t}n(A,"resolveServerConfig");var Lr=/^(\d+)([smhdw])$/,xr={s:1e3,m:6e4,h:36e5,d:864e5,w:6048e5},je=new Map;function K(e){if(typeof e=="number")return e*1e3;let r=je.get(e);if(r!==void 0)return r;let t=Lr.exec(e);if(!t?.[1]||!t?.[2])throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let s=xr[t[2]];if(s===void 0)throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let i=parseInt(t[1],10)*s;return je.set(e,i),i}n(K,"parseExpiry");var b={info:n(()=>{},"info"),warn:n(()=>{},"warn"),error:n(()=>{},"error")};function h(e,r,t){return {service:e,event:r,...t}}n(h,"buildLogData");async function ze(e){await e.schema.createTable("sentri_users").ifNotExists().addColumn("id","varchar(36)",r=>r.primaryKey().notNull()).addColumn("password_hash","varchar(255)",r=>r.notNull()).addColumn("roles","text",r=>r.notNull().defaultTo("[]")).addColumn("created_at","timestamp",r=>r.notNull().defaultTo(kysely.sql`CURRENT_TIMESTAMP`)).execute(),await e.schema.createTable("sentri_sessions").ifNotExists().addColumn("id","varchar(36)",r=>r.primaryKey().notNull()).addColumn("user_id","varchar(36)",r=>r.notNull().references("sentri_users.id").onDelete("cascade")).addColumn("expires_at","timestamp",r=>r.notNull()).addColumn("ip_address","varchar(45)").addColumn("user_agent","text").addColumn("replaced_by","varchar(36)").addColumn("created_at","timestamp",r=>r.notNull().defaultTo(kysely.sql`CURRENT_TIMESTAMP`)).execute(),await e.schema.createTable("sentri_identifiers").ifNotExists().addColumn("id","varchar(36)",r=>r.primaryKey().notNull()).addColumn("user_id","varchar(36)",r=>r.notNull().references("sentri_users.id").onDelete("cascade")).addColumn("type","varchar(100)",r=>r.notNull()).addColumn("value","varchar(255)",r=>r.notNull().unique()).addColumn("created_at","timestamp",r=>r.notNull().defaultTo(kysely.sql`CURRENT_TIMESTAMP`)).execute(),await e.schema.createIndex("idx_sentri_sessions_user_id").ifNotExists().on("sentri_sessions").column("user_id").execute(),await e.schema.createIndex("idx_sentri_identifiers_user_id").ifNotExists().on("sentri_identifiers").column("user_id").execute();}n(ze,"runMigrations");var Xe=new Map;function T(e){let r=Xe.get(e);return r||(r=new kysely.Kysely({dialect:e}),Xe.set(e,r)),r}n(T,"getDatabase");async function X(e,r=12){return Ge__default.default.hash(e,r)}n(X,"hashPassword");async function G(e,r){return Ge__default.default.compare(e,r)}n(G,"verifyPassword");var We=new Map,Ze=new Map,Kr=3600*1e3;function Ye(e){let r=We.get(e);if(!r){let t=crypto.createPrivateKey(e),s=crypto.createPublicKey(t),i=s.export({format:"jwk"}),c=crypto.createHash("sha256").update(JSON.stringify({e:i.e,kty:i.kty,n:i.n})).digest("base64url"),d={...i,use:"sig",kid:c};r={kid:c,publicKey:s,jwk:d},We.set(e,r);}return r}n(Ye,"getOrBuildKey");function Qe(e){let{jwk:r}=Ye(e);return {keys:[r]}}n(Qe,"buildJwks");function er(e){return Ye(e).publicKey}n(er,"getPublicKeyFromPrivate");async function rr(e){let r=Date.now(),t=Ze.get(e);if(t&&r-t.fetchedAt<Kr)return t.publicKey;let s=await fetch(e);if(!s.ok)throw new l("CONFIGURATION_ERROR",`Failed to fetch public key from ${e}: HTTP ${s.status}`);let i=await s.json();if(!i.keys||i.keys.length===0)throw new l("CONFIGURATION_ERROR",`No keys found in JWKS response from ${e}`);let a=i.keys[0],c=crypto.createPublicKey({key:a,format:"jwk"});return Ze.set(e,{publicKey:c,fetchedAt:r}),c}n(rr,"fetchPublicKey");var tr=new Map,sr=new Map,nr=new Map;function or(e){return e.startsWith("RS")||e.startsWith("PS")}n(or,"isRSA");function ir(e){let r=tr.get(e);return r||(r={access:`${e}:access`,refresh:`${e}:refresh`},tr.set(e,r)),r}n(ir,"getHsSecrets");function Hr(e){let r=sr.get(e);return r||(r=crypto.createPrivateKey(e),sr.set(e,r)),r}n(Hr,"getRsPrivateKey");function ar(e){let r=A(e);if(or(r.algorithm)){let i=Hr(e.secret);return {accessKey:i,refreshKey:i}}let{access:t,refresh:s}=ir(e.secret);return {accessKey:t,refreshKey:s}}n(ar,"getSigningKeys");function ur(e,r){let t=A(e);if(or(t.algorithm))return er(e.secret);let{access:s,refresh:i}=ir(e.secret);return r==="access"?s:i}n(ur,"getVerifyKey");function dr(e,r,t,s){let i=`${t}:${s}`,a=nr.get(i);return a||(a={expiresIn:t,algorithm:s},nr.set(i,a)),W__default.default.sign(e,r,a)}n(dr,"sign");function cr(e,r,t){try{let s=W__default.default.verify(e,r,{algorithms:[t]});if(typeof s=="string"||s===null)throw new l("TOKEN_INVALID","Token payload is not an object");return s}catch(s){throw s instanceof l?s:s instanceof W__default.default.TokenExpiredError?new l("TOKEN_EXPIRED","Token has expired"):new l("TOKEN_INVALID","Token is invalid or malformed")}}n(cr,"verify");function Z(e,r){let t=A(r),{accessKey:s}=ar(r);return dr(e,s,t.accessExpiresIn,t.algorithm)}n(Z,"signAccessToken");function J(e,r){let t=A(r),{refreshKey:s}=ar(r);return dr({sessionId:e},s,t.refreshExpiresIn,t.algorithm)}n(J,"signRefreshToken");function de(e,r){let t=A(r),s=ur(r,"access");return cr(e,s,t.algorithm)}n(de,"verifyAccessToken");function Y(e,r){let t=A(r),s=ur(r,"refresh");return cr(e,s,t.algorithm)}n(Y,"verifyRefreshToken");function lr(e,r){try{let t=W__default.default.verify(e,r,{algorithms:["RS256","RS384","RS512","PS256","PS384","PS512"]});if(typeof t=="string"||t===null)throw new l("TOKEN_INVALID","Token payload is not an object");return t}catch(t){throw t instanceof l?t:t instanceof W__default.default.TokenExpiredError?new l("TOKEN_EXPIRED","Token has expired"):new l("TOKEN_INVALID","Token is invalid or malformed")}}n(lr,"verifyTokenWithPublicKey");function Oe(e){try{return JSON.parse(e)}catch{return []}}n(Oe,"parseRoles");function Vr(e){return JSON.stringify(e)}n(Vr,"serializeRoles");function hr(e){return {id:e.id,userId:e.user_id,type:e.type,value:e.value,createdAt:new Date(e.created_at)}}n(hr,"mapIdentifier");async function Q(e,r){let t=await e.selectFrom("sentri_identifiers as i").innerJoin("sentri_users as u","u.id","i.user_id").select(["u.id","u.password_hash","u.roles"]).where("i.value","=",r).executeTakeFirst();return t?{id:t.id,passwordHash:t.password_hash,roles:Oe(t.roles)}:null}n(Q,"findUserByIdentifierValue");async function ce(e,r){let t=await e.selectFrom("sentri_users").select(["id","password_hash","roles"]).where("id","=",r).executeTakeFirst();return t?{id:t.id,passwordHash:t.password_hash,roles:Oe(t.roles)}:null}n(ce,"findUserById");async function pr(e,r,t){let s=t.map(i=>({id:crypto.randomUUID(),user_id:r,type:i.type,value:i.value}));return await e.insertInto("sentri_identifiers").values(s).execute(),s.map(i=>({id:i.id,userId:i.user_id,type:i.type,value:i.value,createdAt:new Date}))}n(pr,"createIdentifiers");async function ee(e,r){return (await e.selectFrom("sentri_identifiers").selectAll().where("user_id","=",r).orderBy("created_at","asc").execute()).map(hr)}n(ee,"findIdentifiersByUserId");async function Ue(e,r,t){let s=await e.selectFrom("sentri_identifiers").selectAll().where("id","=",r).where("user_id","=",t).executeTakeFirst();return s?hr(s):null}n(Ue,"findIdentifierById");async function wr(e,r){let t=await e.selectFrom("sentri_identifiers").select(s=>s.fn.countAll().as("count")).where("user_id","=",r).executeTakeFirst();return Number(t?.count??0)}n(wr,"countIdentifiersByUserId");async function mr(e,r,t,s){await e.updateTable("sentri_identifiers").set({type:s.type,value:s.value}).where("id","=",r).where("user_id","=",t).execute();}n(mr,"updateIdentifier");async function yr(e,r,t){await e.deleteFrom("sentri_identifiers").where("user_id","=",r).where("id","in",t).execute();}n(yr,"deleteIdentifiers");async function Ir(e,r,t){await e.updateTable("sentri_users").set({password_hash:t}).where("id","=",r).execute();}n(Ir,"updateUserPassword");async function gr(e,r,t){await e.updateTable("sentri_users").set({roles:Vr(t)}).where("id","=",r).execute();}n(gr,"updateUserRoles");async function Le(e,r){let t=crypto.randomUUID();return await e.insertInto("sentri_sessions").values({id:t,user_id:r.userId,expires_at:r.expiresAt.toISOString(),ip_address:r.ipAddress??null,user_agent:r.userAgent??null}).execute(),{id:t}}n(Le,"createSession");async function le(e,r){let t=await e.selectFrom("sentri_sessions as s").innerJoin("sentri_users as u","u.id","s.user_id").select(["s.id as session_id","s.user_id","s.expires_at","s.ip_address","s.user_agent","s.replaced_by","s.created_at as session_created_at","u.id as user_id_col","u.password_hash","u.roles"]).where("s.id","=",r).executeTakeFirst();return t?{id:t.session_id,userId:t.user_id,expiresAt:new Date(t.expires_at),ipAddress:t.ip_address,userAgent:t.user_agent,replacedBy:t.replaced_by,createdAt:new Date(t.session_created_at),user:{id:t.user_id_col,passwordHash:t.password_hash,roles:Oe(t.roles)}}:null}n(le,"findSessionById");async function fe(e,r){await e.deleteFrom("sentri_sessions").where("id","=",r).execute();}n(fe,"deleteSession");async function _r(e,r,t){await e.updateTable("sentri_sessions").set({replaced_by:t}).where("id","=",r).execute();}n(_r,"markSessionReplaced");async function he(e,r){await e.deleteFrom("sentri_sessions").where("user_id","=",r).execute();}n(he,"deleteAllSessionsForUser");async function Rr(e,r){return (await e.selectFrom("sentri_sessions").selectAll().where("user_id","=",r).where("replaced_by","is",null).where("expires_at",">",new Date).execute()).map(s=>({id:s.id,userId:s.user_id,expiresAt:new Date(s.expires_at),ipAddress:s.ip_address,userAgent:s.user_agent,replacedBy:s.replaced_by,createdAt:new Date(s.created_at)}))}n(Rr,"findSessionsByUserId");async function pe(e,r,t){let s=A(r),i=T(r.dialect),a=e.roles??[],c=a.filter(R=>!s.validRolesSet.has(R));if(c.length>0)return {success:false,error:new l("INVALID_ROLE",`Invalid roles: ${c.join(", ")}`)};if(!e.identifiers||e.identifiers.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one identifier is required")};let d=e.identifiers.map(R=>({type:R.type.trim(),value:R.value.trim()}));if(new Set(d.map(R=>R.value)).size!==d.length)return {success:false,error:new l("VALIDATION_ERROR","Duplicate identifier values in request")};for(let R of d)if(await Q(i,R.value))return {success:false,error:new l("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${R.value}`)};let p=await X(e.password,s.saltRounds),{userId:O,identifierRows:L}=await i.transaction().execute(async R=>{let F=crypto.randomUUID();await R.insertInto("sentri_users").values({id:F,password_hash:p,roles:JSON.stringify(a)}).execute();let ie=d.map(ae=>({id:crypto.randomUUID(),user_id:F,type:ae.type,value:ae.value}));return await R.insertInto("sentri_identifiers").values(ie).execute(),{userId:F,identifierRows:ie}}),N={success:true,user:{id:O,roles:a,identifiers:L.map(R=>({id:R.id,type:R.type,value:R.value}))}};return r.hooks?.onRegister&&await r.hooks.onRegister(N.user),N}n(pe,"register");async function we(e,r,t){let s=A(r),i=T(r.dialect),a=await Q(i,e.identifier.trim());if(!a){let N=new l("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,N,{ip:t?.ip??""}),{success:false,error:N}}if(!await G(e.password,a.passwordHash)){let N=new l("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,N,{ip:t?.ip??""}),{success:false,error:N}}let d=new Date(Date.now()+K(s.refreshExpiresIn)),u=await Le(i,{userId:a.id,expiresAt:d,ipAddress:t?.ip??null,userAgent:t?.userAgent??null}),p={id:a.id,roles:a.roles},O=Z({id:a.id,roles:a.roles,sessionId:u.id},r),L=J(u.id,r);return r.hooks?.onLoginSuccess&&await r.hooks.onLoginSuccess(p,{ip:t?.ip??"",userAgent:t?.userAgent??""}),{success:true,accessToken:O,refreshToken:L,user:p}}n(we,"login");async function $(e,r,t){let s=A(r),i=T(r.dialect),a;try{({sessionId:a}=Y(e,r));}catch(N){return N instanceof l?{success:false,error:N}:{success:false,error:new l("TOKEN_INVALID","Invalid refresh token")}}let c=await le(i,a);if(!c)return {success:false,error:new l("UNAUTHORIZED","Session not found or revoked")};if(c.replacedBy)return await he(i,c.userId),{success:false,error:new l("TOKEN_REUSE","Token reuse detected. All sessions revoked.")};if(c.expiresAt.getTime()<Date.now())return await fe(i,a),{success:false,error:new l("TOKEN_EXPIRED","Session has expired")};let d=new Date(Date.now()+K(s.refreshExpiresIn)),u=await Le(i,{userId:c.userId,expiresAt:d,ipAddress:t?.ip??null,userAgent:t?.userAgent??null});await _r(i,a,u.id);let p={id:c.user.id,roles:c.user.roles},O=Z({...p,sessionId:u.id},r),L=J(u.id,r);return {success:true,accessToken:O,refreshToken:L,user:p}}n($,"refresh");async function me(e,r){let t=T(r.dialect),s;try{({sessionId:s}=Y(e,r));}catch{return}let i=await le(t,s);i&&(await fe(t,s),r.hooks?.onLogout&&await r.hooks.onLogout(i.userId));}n(me,"logout");async function ye(e,r){let t=T(r.dialect);await he(t,e),r.hooks?.onLogout&&await r.hooks.onLogout(e);}n(ye,"logoutAll");async function Ie(e,r){let t=T(r.dialect),s=await ce(t,e);if(!s)return {success:false,error:new l("USER_NOT_FOUND","User not found")};let i=await ee(t,e);return {success:true,user:{id:s.id,roles:s.roles,identifiers:i.map(a=>({id:a.id,type:a.type,value:a.value}))}}}n(Ie,"getUser");async function ge(e,r,t,s){let i=A(s),a=T(s.dialect),c=await ce(a,e);if(!c)return {success:false,error:new l("USER_NOT_FOUND","User not found")};if(!await G(r,c.passwordHash))return {success:false,error:new l("INVALID_CREDENTIALS","Invalid credentials")};let u=await X(t,i.saltRounds);return await Ir(a,e,u),await he(a,e),s.hooks?.onPasswordChanged&&await s.hooks.onPasswordChanged(e),{success:true}}n(ge,"changePassword");async function _e(e,r,t){let s=A(t),i=T(t.dialect),a=r.filter(p=>!s.validRolesSet.has(p));if(a.length>0)return {success:false,error:new l("INVALID_ROLE",`Invalid roles: ${a.join(", ")}`)};let c=await ce(i,e);if(!c)return {success:false,error:new l("USER_NOT_FOUND","User not found")};let d=new Set(c.roles);for(let p of r)d.add(p);let u=Array.from(d);return await gr(i,e,u),{success:true,user:{id:c.id,roles:u}}}n(_e,"assignRoles");async function Re(e,r,t){let s=T(t.dialect);if(r.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one identifier is required")};let i=r.map(d=>({type:d.type.trim(),value:d.value.trim()}));if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new l("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i)if(await Q(s,d.value))return {success:false,error:new l("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)};return await pr(s,e,i),{success:true,identifiers:(await ee(s,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}n(Re,"bulkCreateIdentifiers");async function ke(e,r,t){let s=T(t.dialect);if(r.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one update is required")};let i=r.map(d=>({id:d.id,type:d.type.trim(),value:d.value.trim()}));if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new l("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i){let u=await Ue(s,d.id,e);if(!u)return {success:false,error:new l("IDENTIFIER_NOT_FOUND",`Identifier not found: ${d.id}`)};if(u.value!==d.value&&await Q(s,d.value))return {success:false,error:new l("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)}}return await s.transaction().execute(async d=>{for(let u of i)await mr(d,u.id,e,{type:u.type,value:u.value});}),{success:true,identifiers:(await ee(s,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}n(ke,"bulkUpdateIdentifiers");async function Ae(e,r,t){let s=T(t.dialect);if(r.length===0)return {success:false,error:new l("VALIDATION_ERROR","At least one ID is required")};let i=Array.from(new Set(r));for(let d of i)if(!await Ue(s,d,e))return {success:false,error:new l("IDENTIFIER_NOT_FOUND",`Identifier not found: ${d}`)};return await wr(s,e)-i.length<1?{success:false,error:new l("VALIDATION_ERROR","Cannot delete all identifiers \u2014 at least one must remain")}:(await yr(s,e,i),{success:true,identifiers:(await ee(s,e)).map(d=>({id:d.id,type:d.type,value:d.value}))})}n(Ae,"bulkDeleteIdentifiers");async function Ar(e,r){let t=T(r.dialect);return (await Rr(t,e)).map(i=>({id:i.id,ipAddress:i.ipAddress,userAgent:i.userAgent,createdAt:i.createdAt,expiresAt:i.expiresAt}))}n(Ar,"getSessions");async function vr(e,r,t){let s=T(t.dialect),i=await le(s,r);i&&i.userId===e&&await fe(s,r);}n(vr,"revokeSession");function x(e){return A(e).cookieName}n(x,"getCookieName");function ve(e){return A(e).accessCookieName}n(ve,"getAccessCookieName");function re(e,r,t){let s=t.cookie??{},i=K(A(t).refreshExpiresIn);e.cookies.set(x(t),r,{httpOnly:s.httpOnly??true,secure:s.secure??false,sameSite:s.sameSite??"strict",path:s.path??"/",maxAge:i});}n(re,"setCookieFromConfig");function Ee(e,r){let t=r.cookie??{};e.cookies.set(x(r),null,{path:t.path??"/"});}n(Ee,"clearCookieFromConfig");function te(e,r,t){if(!t.accessCookie)return;let s=t.accessCookie,i=K(A(t).accessExpiresIn);e.cookies.set(ve(t),r,{httpOnly:false,secure:s.secure??false,sameSite:s.sameSite??"strict",path:s.path??"/",maxAge:i});}n(te,"setAccessCookieFromConfig");function xe(e,r){if(!r.accessCookie)return;let t=r.accessCookie;e.cookies.set(ve(r),null,{path:t.path??"/"});}n(xe,"clearAccessCookieFromConfig");function Te(e,r){let t=e.get("Authorization");return t?.startsWith("Bearer ")?t.slice(7):e.cookies.get(ve(r))}n(Te,"getCurrentAccessToken");function D(e){let r=e.logger??b,t=e.loggerService??"sentri";return e.mode==="client"?Mr(e.keyUri,r,t):Br(e,r,t)}n(D,"protect");function Mr(e,r,t){return async(s,i)=>{let a=s.get("Authorization"),c=a?.startsWith("Bearer ")?a.slice(7):void 0,d=s.requestId??s.state?.requestId;if(!c)throw r.warn(h(t,"auth.protect.failure",{mode:"client",errorCode:"UNAUTHORIZED",...d!==void 0&&{requestId:d}})),new l("UNAUTHORIZED","Missing or malformed Authorization header");try{let u=await rr(e),p=lr(c,u);s.state.user={id:p.id,roles:p.roles},r.info(h(t,"auth.protect.success",{mode:"client",userId:p.id,...d!==void 0&&{requestId:d}})),await i();}catch(u){let p=u instanceof l?u.code:"TOKEN_INVALID";throw r.warn(h(t,"auth.protect.failure",{mode:"client",errorCode:p,...d!==void 0&&{requestId:d}})),u}}}n(Mr,"protectClient");function Br(e,r,t){return async(s,i)=>{let a=Te(s,e),c=s.requestId??s.state?.requestId;if(!a)throw r.warn(h(t,"auth.protect.failure",{mode:"server",errorCode:"UNAUTHORIZED",...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Missing or malformed Authorization header");try{let d=de(a,e);if(e.isTokenRevoked&&await e.isTokenRevoked(d.sessionId))throw r.warn(h(t,"auth.protect.token_revoked",{mode:"server",userId:d.id,...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Token has been revoked");s.state.user={id:d.id,roles:d.roles},r.info(h(t,"auth.protect.success",{mode:"server",userId:d.id,...c!==void 0&&{requestId:c}})),await i();}catch(d){if(d instanceof l&&d.code==="TOKEN_EXPIRED"){let u=s.cookies.get(x(e));if(!u)throw r.warn(h(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED",...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Token expired. Please login again.");try{let p=await $(u,e);if(!p.success)throw r.warn(h(t,"auth.protect.failure",{mode:"server",errorCode:p.error.code,...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Session expired. Please login again.");re(s,p.refreshToken,e),te(s,p.accessToken,e),s.set("X-New-Access-Token",p.accessToken),s.state.user=p.user,r.info(h(t,"auth.protect.auto_refresh",{mode:"server",userId:p.user.id,...c!==void 0&&{requestId:c}})),await i();}catch{throw r.warn(h(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED",...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Session expired. Please login again.")}}else {let u=d instanceof l?d.code:"TOKEN_INVALID";throw r.warn(h(t,"auth.protect.failure",{mode:"server",errorCode:u,...c!==void 0&&{requestId:c}})),d}}}}n(Br,"protectServer");function Er(e,r,t){let s=`Requires one of roles: ${e.join(", ")}`;return async(i,a)=>{let c=i.requestId??i.state?.requestId;if(!i.state.user)throw r.warn(h(t,"auth.authorize.unauthenticated",{requiredRoles:e,...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Not authenticated");let d=i.state.user.roles;if(!e.some(u=>d.includes(u)))throw r.warn(h(t,"auth.authorize.denied",{userId:i.state.user.id,userRoles:[...d],requiredRoles:e,...c!==void 0&&{requestId:c}})),new l("FORBIDDEN",s);r.info(h(t,"auth.authorize.passed",{userId:i.state.user.id,userRoles:[...d],requiredRoles:e,...c!==void 0&&{requestId:c}})),await a();}}n(Er,"createAuthorizeHandler");function se(e,r){return n(function(...s){return Er(s,e,r)},"authorize")}n(se,"createAuthorize");function jr(...e){return Er(e,b,"sentri")}n(jr,"authorize");var zr=new l("FORBIDDEN","You do not have permission to perform this action");function Tr(e,r,t){return async(s,i)=>{let a=s.state?.user,c=s.requestId??s.state?.requestId;if(!a)throw r.warn(h(t,"auth.permit.unauthenticated",{...c!==void 0&&{requestId:c}})),new l("UNAUTHORIZED","Not authenticated");if(e.roles&&e.roles.length>0){let d=a.roles;if(e.roles.some(u=>d.includes(u)))return r.info(h(t,"auth.permit.role_bypass",{userId:a.id,bypassedByRole:true,...c!==void 0&&{requestId:c}})),await i()}try{let d=e.check(s);if(d instanceof Promise?await d:d)r.info(h(t,"auth.permit.passed",{userId:a.id,...c!==void 0&&{requestId:c}})),await i();else throw r.warn(h(t,"auth.permit.denied",{userId:a.id,...c!==void 0&&{requestId:c}})),zr}catch(d){throw d}}}n(Tr,"createPermitHandler");function ne(e,r){return n(function(s){return Tr(typeof s=="function"?{check:s}:s,e,r)},"permit")}n(ne,"createPermit");function Xr(e){return Tr(typeof e=="function"?{check:e}:e,b,"sentri")}n(Xr,"permit");function oe(e={}){return async(r,t)=>{try{await t();}catch(s){s instanceof l?(r.status=s.statusCode,r.body={error:true,statusCode:s.statusCode,code:s.code,message:s.message,data:null}):(r.status=s.status||s.statusCode||500,r.body={error:true,statusCode:r.status,code:s.code||"INTERNAL_SERVER_ERROR",message:s.message||"Internal Server Error",data:null},r.app.emit("error",s,r));}}}n(oe,"createErrorHandler");var Fe=class{static{n(this,"InMemoryRateLimiter");}store=new Map;constructor(){setInterval(()=>{let r=Date.now();for(let[t,s]of this.store.entries())s.expiresAt<r&&this.store.delete(t);},6e4).unref();}async consume(r,t,s){let i=Date.now(),a=this.store.get(r);if((!a||a.expiresAt<i)&&(a={count:0,expiresAt:i+s},this.store.set(r,a)),a.count>t){let c=Math.ceil((a.expiresAt-i)/1e3);throw new Error(`Rate limit exceeded. Try again in ${c} seconds.`)}a.count++;}},Pe=class{static{n(this,"RedisRateLimiter");}redis;logger;constructor(r,t){this.redis=r,this.logger=t;}async consume(r,t,s){let i=`sentri:rl:${r}`,a=Math.ceil(s/1e3);try{let c=await this.redis.multi().incr(i).expire(i,a,"NX").exec();if(!c||c.length===0)return;if(c[0][1]>t)throw new Error("Rate limit exceeded. Try again later.")}catch(c){if(c instanceof Error&&c.message.includes("Rate limit exceeded"))throw c;this.logger.error({msg:"Redis RateLimiter failed, bypassing limit",error:c});}}},H=null;async function Nr(e,r){if(H)return H;if(e)try{let{Redis:t}=await import('ioredis'),s=new t(e,{lazyConnect:!0,maxRetriesPerRequest:1});return await s.connect(),r?.info({msg:"Connected to Redis for Rate Limiting"}),H=new Pe(s,r??b),H}catch(t){r?.warn({msg:"Failed to connect to Redis for Rate Limiting, falling back to InMemoryRateLimiter",error:t});}return H=new Fe,H}n(Nr,"getRateLimiter");var Ne=8,V=72,De=255,Dr=100,M=50;function g(e){return new l("VALIDATION_ERROR",e)}n(g,"badRequest");function S(e,r,t,s){e.status=r,e.body={error:false,statusCode:r,message:t,data:s};}n(S,"ok");function U(e,r){e.status=r.statusCode,e.body={error:true,statusCode:r.statusCode,code:r.code,message:r.message,data:null};}n(U,"fail");function P(e){let r=e.request.body;if(r==null||typeof r!="object"||Array.isArray(r))throw new l("VALIDATION_ERROR","Request body is missing or not a JSON object. Did you apply koa-bodyparser?");return r}n(P,"parseBody");function Wr(e,r){if(!r.apiKey)return;let t=e.get("x-api-key");if(typeof t!="string"||t!==r.apiKey)throw new l("UNAUTHORIZED","Invalid or missing API key")}n(Wr,"validateApiKey");function qe(e){if(e)try{let r=e();r instanceof Promise&&r.catch(()=>{});}catch{}}n(qe,"fireHook");function Zr(e){return e.startsWith("RS")||e.startsWith("PS")}n(Zr,"isRSA");function Ke(e,r){if(typeof e!="object"||e===null||Array.isArray(e))throw g(`identifiers[${r}] must be an object`);let t=e;if(typeof t.type!="string"||t.type.trim().length===0)throw g(`identifiers[${r}].type is required and must be a non-empty string`);if(t.type.length>Dr)throw g(`identifiers[${r}].type must not exceed ${Dr} characters`);if(typeof t.value!="string"||t.value.trim().length===0)throw g(`identifiers[${r}].value is required and must be a non-empty string`);if(t.value.length>De)throw g(`identifiers[${r}].value must not exceed ${De} characters`);return {type:t.type,value:t.value}}n(Ke,"validateIdentifierInput");function k(e){let r=e.requestId??e.state?.requestId;return r!==void 0?{requestId:r}:{}}n(k,"reqId");function Sr(e){let r=new Gr__default.default,t=e,s=A(t),i=e.logger??b,a=e.loggerService??"sentri",c=se(i,a),d=ne(i,a),u=Nr(e.redisUrl,i),p=e.router?.register??(o=>pe(o,t)),O=e.router?.login??(o=>we(o,t)),L=e.router?.refresh??(o=>$(o,t)),N=e.router?.logout??(o=>o!==void 0?me(o,t):Promise.resolve()),R=e.router?.logoutAll??(o=>ye(o,t)),F=e.router?.getUser??(o=>Ie(o,t)),ie=e.router?.assignRoles??((o,_)=>_e(o,_,t)),ae=e.router?.changePassword??((o,_,y)=>ge(o,_,y,t)),Cr=e.router?.bulkCreateIdentifiers??((o,_)=>Re(o,_,t)),br=e.router?.bulkUpdateIdentifiers??((o,_)=>ke(o,_,t)),Or=e.router?.bulkDeleteIdentifiers??((o,_)=>Ae(o,_,t));Zr(s.algorithm)&&r.get("/keys",o=>{o.set("Cache-Control","public, max-age=3600"),o.body=Qe(e.secret);}),r.post("/register",async(o,_)=>{let y=Date.now();try{Wr(o,e);let f=P(o),{identifiers:m,password:I,roles:w}=f;if(!Array.isArray(m)||m.length===0)throw g("identifiers is required and must be a non-empty array");if(m.length>M)throw g(`identifiers must not exceed ${M} entries`);let v=m.map((z,Se)=>Ke(z,Se));if(typeof I!="string"||I.length<Ne)throw g(`password is required and must be at least ${Ne} characters`);if(I.length>V)throw g(`password must not exceed ${V} characters`);if(w!==void 0&&!Array.isArray(w))throw g("roles must be an array of strings when provided");if(Array.isArray(w)&&!w.every(z=>typeof z=="string"))throw g("each role must be a string");let C=Array.isArray(w)?w:void 0,E=C!==void 0?{identifiers:v,password:I,roles:C}:{identifiers:v,password:I},q=o.ip||"127.0.0.1",B=o.get("user-agent")||"Unknown";if(e.rateLimit!==!1){let z=await u,Se=typeof e.rateLimit=="object"?e.rateLimit.maxRegisterAttempts??5:5;await z.consume(`register:${q}`,Se,900*1e3);}let j=await p(E,{ip:q,userAgent:B});if(!j.success){i.warn(h(a,"auth.register.failure",{errorCode:j.error.code,duration_ms:Date.now()-y,...k(o)})),U(o,j.error);return}i.info(h(a,"auth.register.success",{userId:j.user.id,duration_ms:Date.now()-y,...k(o)})),S(o,201,"User registered successfully",{user:j.user});}catch(f){throw f}}),r.post("/login",async(o,_)=>{let y=Date.now();try{let f=P(o),{identifier:m,password:I}=f;if(typeof m!="string"||m.trim().length===0)throw g("identifier is required and must be a non-empty string");if(m.length>De)throw g(`identifier must not exceed ${De} characters`);if(typeof I!="string"||I.length===0)throw g("password is required");if(I.length>V)throw g(`password must not exceed ${V} characters`);let w=m.trim(),v=o.ip||"127.0.0.1",C=o.get("user-agent")||"Unknown";if(e.rateLimit!==!1){let q=await u,B=typeof e.rateLimit=="object"?e.rateLimit.maxLoginAttempts??5:5;await q.consume(`login:${v}`,B,900*1e3);}let E=await O({identifier:w,password:I},{ip:v,userAgent:C});if(!E.success){qe(()=>e.hooks?.onLoginFailed?.(w,E.error,{ip:v})),i.warn(h(a,"auth.login.failure",{errorCode:E.error.code,duration_ms:Date.now()-y,...k(o)})),U(o,E.error);return}qe(()=>e.hooks?.onLoginSuccess?.(E.user,{ip:v,userAgent:C})),re(o,E.refreshToken,e),te(o,E.accessToken,e),i.info(h(a,"auth.login.success",{userId:E.user.id,duration_ms:Date.now()-y,...k(o)})),S(o,200,"Login successful",{accessToken:E.accessToken,user:E.user});}catch(f){throw f}}),r.post("/refresh",async(o,_)=>{let y=Date.now();try{let f=o.cookies.get(x(e));if(!f)throw new l("UNAUTHORIZED","Refresh token cookie is missing");let m=o.ip||"127.0.0.1",I=o.get("user-agent")||"Unknown",w=await L(f,{ip:m,userAgent:I});if(!w.success){Ee(o,e),i.warn(h(a,"auth.refresh.failure",{errorCode:w.error.code,duration_ms:Date.now()-y,...k(o)})),U(o,w.error);return}re(o,w.refreshToken,e),te(o,w.accessToken,e),i.info(h(a,"auth.refresh.success",{userId:w.user.id,duration_ms:Date.now()-y,...k(o)})),S(o,200,"Token refreshed",{accessToken:w.accessToken});}catch(f){throw f}}),r.post("/logout",async(o,_)=>{let y=Date.now();try{let f=o.cookies.get(x(e));await N(f),Ee(o,e),xe(o,e),i.info(h(a,"auth.logout",{duration_ms:Date.now()-y,...k(o)})),S(o,200,"Logged out",null);}catch(f){throw f}}),r.post("/logout-all",D(e),async(o,_)=>{let y=Date.now();try{let f=o.state.user.id;await R(f),qe(()=>e.hooks?.onLogout?.(f)),Ee(o,e),xe(o,e),i.info(h(a,"auth.logout_all",{userId:f,duration_ms:Date.now()-y,...k(o)})),S(o,200,"All sessions revoked",null);}catch(f){throw f}}),r.get("/me",D(e),async(o,_)=>{let y=Date.now();try{let f=await F(o.state.user.id);if(!f.success){i.warn(h(a,"auth.me.failure",{userId:o.state.user.id,errorCode:f.error.code,duration_ms:Date.now()-y,...k(o)})),U(o,f.error);return}i.info(h(a,"auth.me.success",{userId:f.user.id,duration_ms:Date.now()-y,...k(o)})),S(o,200,"OK",f.user);}catch(f){throw f}}),r.get("/me/identifiers",D(e),async(o,_)=>{let y=Date.now();try{let f=await F(o.state.user.id);if(!f.success){i.warn(h(a,"auth.me.identifiers.failure",{userId:o.state.user.id,errorCode:f.error.code,duration_ms:Date.now()-y,...k(o)})),U(o,f.error);return}i.info(h(a,"auth.me.identifiers.success",{userId:f.user.id,count:f.user.identifiers?.length??0,duration_ms:Date.now()-y,...k(o)})),S(o,200,"OK",{identifiers:f.user.identifiers??[]});}catch(f){throw f}});let ue=d(o=>!!o.state.user);return r.post("/me/identifiers",D(e),ue,async(o,_)=>{let y=Date.now();try{let f=P(o),{identifiers:m}=f;if(!Array.isArray(m)||m.length===0)throw g("identifiers is required and must be a non-empty array");if(m.length>M)throw g(`identifiers must not exceed ${M} entries`);let I=m.map((v,C)=>Ke(v,C)),w=await Cr(o.state.user.id,I);if(!w.success){i.warn(h(a,"auth.identifiers.create_failure",{userId:o.state.user.id,errorCode:w.error.code,duration_ms:Date.now()-y,...k(o)})),U(o,w.error);return}i.info(h(a,"auth.identifiers.created",{userId:o.state.user.id,count:w.identifiers.length,duration_ms:Date.now()-y,...k(o)})),S(o,201,"Identifiers added successfully",{identifiers:w.identifiers});}catch(f){throw f}}),r.put("/me/identifiers",D(e),ue,async(o,_)=>{let y=Date.now();try{let f=P(o),{identifiers:m}=f;if(!Array.isArray(m)||m.length===0)throw g("identifiers is required and must be a non-empty array");if(m.length>M)throw g(`identifiers must not exceed ${M} entries`);let I=m.map((v,C)=>{if(typeof v!="object"||v===null||Array.isArray(v))throw g(`identifiers[${C}] must be an object`);let E=v;if(typeof E.id!="string"||E.id.trim().length===0)throw g(`identifiers[${C}].id is required and must be a non-empty string`);let{type:q,value:B}=Ke(v,C);return {id:E.id,type:q,value:B}}),w=await br(o.state.user.id,I);if(!w.success){i.warn(h(a,"auth.identifiers.update_failure",{userId:o.state.user.id,errorCode:w.error.code,duration_ms:Date.now()-y,...k(o)})),U(o,w.error);return}i.info(h(a,"auth.identifiers.updated",{userId:o.state.user.id,count:w.identifiers.length,duration_ms:Date.now()-y,...k(o)})),S(o,200,"Identifiers updated successfully",{identifiers:w.identifiers});}catch(f){throw f}}),r.delete("/me/identifiers",D(e),ue,async(o,_)=>{let y=Date.now();try{let f=P(o),{ids:m}=f;if(!Array.isArray(m)||m.length===0)throw g("ids is required and must be a non-empty array of strings");if(!m.every(w=>typeof w=="string"))throw g("each id must be a string");let I=await Or(o.state.user.id,m);if(!I.success){i.warn(h(a,"auth.identifiers.delete_failure",{userId:o.state.user.id,errorCode:I.error.code,duration_ms:Date.now()-y,...k(o)})),U(o,I.error);return}i.info(h(a,"auth.identifiers.deleted",{userId:o.state.user.id,duration_ms:Date.now()-y,...k(o)})),S(o,200,"Identifiers deleted successfully",{identifiers:I.identifiers});}catch(f){throw f}}),r.patch("/me/password",D(e),ue,async(o,_)=>{let y=Date.now();try{let f=P(o),{currentPassword:m,newPassword:I}=f;if(typeof m!="string"||m.length===0)throw g("currentPassword is required");if(typeof I!="string"||I.length<Ne)throw g(`newPassword must be at least ${Ne} characters`);if(I.length>V)throw g(`newPassword must not exceed ${V} characters`);if(m===I)throw g("newPassword must be different from currentPassword");let w=await ae(o.state.user.id,m,I);if(!w.success){i.warn(h(a,"auth.password.change_failure",{userId:o.state.user.id,errorCode:w.error.code,duration_ms:Date.now()-y,...k(o)})),U(o,w.error);return}i.info(h(a,"auth.password.changed",{userId:o.state.user.id,duration_ms:Date.now()-y,...k(o)})),S(o,200,"Password updated successfully. All sessions have been revoked.",null);}catch(f){throw f}}),r.post("/users/:userId/roles",D(e),c("admin"),async(o,_)=>{let y=Date.now();try{let f=P(o),{roles:m}=f,I=o.params.userId,w=typeof I=="string"?I:void 0;if(!w)throw g("userId is required");if(!Array.isArray(m)||m.length===0)throw g("roles must be a non-empty array of strings");if(!m.every(C=>typeof C=="string"))throw g("each role must be a string");let v=await ie(w,m);if(!v.success){i.warn(h(a,"auth.roles.assign_failure",{targetUserId:w,errorCode:v.error.code,duration_ms:Date.now()-y,...k(o)})),U(o,v.error);return}i.info(h(a,"auth.roles.assigned",{targetUserId:w,roles:m,duration_ms:Date.now()-y,...k(o)})),S(o,200,"Roles assigned successfully",{user:v.user});}catch(f){throw f}}),r.use(oe()),r.get("/sessions",D(e),async(o,_)=>{let y=Date.now();try{let f=o.state.user.id,I=await(e.router?.getSessions??(w=>Ar(w,t)))(f);i.info(h(a,"auth.sessions.get",{userId:f,duration_ms:Date.now()-y,...k(o)})),S(o,200,"OK",{sessions:I});}catch(f){throw await _(),f}}),r.delete("/sessions/:id",D(e),async(o,_)=>{let y=Date.now();try{let f=o.state.user.id,m=o.params.id;await(e.router?.revokeSession??((w,v)=>vr(w,v,t)))(f,m),i.info(h(a,"auth.sessions.revoke",{userId:f,sessionId:m,duration_ms:Date.now()-y,...k(o)})),S(o,200,"Session revoked",null);}catch(f){throw await _(),f}}),r}n(Sr,"createAuthRouter");function Vs(e){if(e.mode==="client"){let u={mode:"client",keyUri:e.keyUri,...e.validRoles!==void 0&&{validRoles:e.validRoles},...e.logger!==void 0&&{logger:e.logger},...e.loggerService!==void 0&&{loggerService:e.loggerService}};Ce(u);let p=u.logger??b,O=u.loggerService??"sentri",L=se(p,O),N=ne(p,O);return {protect:n(()=>D(u),"protect"),authorize:n((...R)=>L(...R),"authorize"),permit:n(R=>N(R),"permit"),errorHandler:n(R=>oe(R),"errorHandler")}}let{privateKey:r}=crypto.generateKeyPairSync("rsa",{modulusLength:2048,privateKeyEncoding:{type:"pkcs8",format:"pem"},publicKeyEncoding:{type:"spki",format:"pem"}}),t={mode:"server",dialect:e.dialect,secret:r,algorithm:"RS256",validRoles:e.validRoles,...e.accessExpiresIn!==void 0&&{accessExpiresIn:e.accessExpiresIn},...e.refreshExpiresIn!==void 0&&{refreshExpiresIn:e.refreshExpiresIn},...e.saltRounds!==void 0&&{saltRounds:e.saltRounds},...e.apiKey!==void 0&&{apiKey:e.apiKey},...e.cookie!==void 0&&{cookie:e.cookie},...e.accessCookie!==void 0&&{accessCookie:e.accessCookie},...e.hooks!==void 0&&{hooks:e.hooks},...e.router!==void 0&&{router:e.router},...e.isTokenRevoked!==void 0&&{isTokenRevoked:e.isTokenRevoked},...e.redisUrl!==void 0&&{redisUrl:e.redisUrl},...e.logger!==void 0&&{logger:e.logger},...e.loggerService!==void 0&&{loggerService:e.loggerService}};Ce(t);let s=t.logger??b,i=t.loggerService??"sentri",a=A(t),c=se(s,i),d=ne(s,i);return {protect:n(()=>D(t),"protect"),authorize:n((...u)=>c(...u),"authorize"),permit:n(u=>d(u),"permit"),errorHandler:n(u=>oe(u),"errorHandler"),migrate:n(async()=>{let u=T(t.dialect);await ze(u);},"migrate"),router:n(()=>Sr(t),"router"),register:n(u=>pe(u,t),"register"),login:n(u=>we(u,t),"login"),refresh:n(u=>$(u,t),"refresh"),logout:n(u=>me(u,t),"logout"),logoutAll:n(u=>ye(u,t),"logoutAll"),getUser:n(u=>Ie(u,t),"getUser"),changePassword:n((u,p,O)=>ge(u,p,O,t),"changePassword"),assignRoles:n((u,p)=>_e(u,p,t),"assignRoles"),bulkCreateIdentifiers:n((u,p)=>Re(u,p,t),"bulkCreateIdentifiers"),bulkUpdateIdentifiers:n((u,p)=>ke(u,p,t),"bulkUpdateIdentifiers"),bulkDeleteIdentifiers:n((u,p)=>Ae(u,p,t),"bulkDeleteIdentifiers"),hashPassword:n(u=>X(u,a.saltRounds),"hashPassword"),verifyPassword:n((u,p)=>G(u,p),"verifyPassword"),signAccessToken:n(u=>Z(u,t),"signAccessToken"),signRefreshToken:n(u=>J(u,t),"signRefreshToken"),verifyAccessToken:n(u=>de(u,t),"verifyAccessToken"),verifyRefreshToken:n(u=>Y(u,t),"verifyRefreshToken"),getCurrentAccessToken:n(u=>Te(u,t),"getCurrentAccessToken")}}n(Vs,"createAuthKoa");exports.SENTRI_ERROR_STATUS=$e;exports.SentriError=l;exports.authorize=jr;exports.createAuthKoa=Vs;exports.createAuthRouter=Sr;exports.createAuthorize=se;exports.createErrorHandler=oe;exports.createPermit=ne;exports.getCurrentAccessToken=Te;exports.permit=Xr;exports.protect=D;