sentri 5.0.1 → 5.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -350,6 +350,7 @@ const auth = createAuthServer({
350
350
  saltRounds: 12,
351
351
  apiKey: process.env.REGISTER_API_KEY,
352
352
  redisUrl: process.env.REDIS_URL, // enables Redis-backed idempotency cache
353
+ rateLimit: { maxLoginAttempts: 5, maxRegisterAttempts: 5 }, // default 5 attempts per 15 min
353
354
  });
354
355
  ```
355
356
 
@@ -369,7 +370,8 @@ createAuth({
369
370
  refreshExpiresIn: "7d", // default: '7d'
370
371
  saltRounds: 12, // default: 12 (bcrypt rounds, 10–31)
371
372
  apiKey: process.env.REGISTER_API_KEY, // restricts POST /register
372
- redisUrl: process.env.REDIS_URL, // Redis URL for idempotency cache
373
+ redisUrl: process.env.REDIS_URL, // Redis URL for idempotency cache & rate limiter
374
+ rateLimit: { maxLoginAttempts: 5, maxRegisterAttempts: 5 }, // optional rate limiting config
373
375
  cookie: { secure: true }, // httpOnly refresh token cookie
374
376
  accessCookie: { secure: true }, // non-httpOnly access token cookie (SPA)
375
377
  hooks: { onLogin, onFailedLogin, onLogout },
@@ -751,6 +753,7 @@ app.onError(auth.errorHandler());
751
753
  | `FORBIDDEN` | 403 | Authenticated but missing required role |
752
754
  | `INVALID_ROLE` | 400 | Role not in `validRoles` |
753
755
  | `VALIDATION_ERROR` | 400 | Missing or invalid input |
756
+ | `RATE_LIMIT_EXCEEDED` | 429 | Too many requests to an endpoint |
754
757
  | `CONFIGURATION_ERROR` | 500 | Invalid `createAuth` config |
755
758
 
756
759
  ### Extending `SentriError`
@@ -1 +1 @@
1
- 'use strict';var crypto=require('crypto'),kysely=require('kysely'),Ze=require('bcrypt'),ee=require('jsonwebtoken'),elysia=require('elysia');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Ze__default=/*#__PURE__*/_interopDefault(Ze);var ee__default=/*#__PURE__*/_interopDefault(ee);var Cr=Object.defineProperty;var o=(e,r)=>Cr(e,"name",{value:r,configurable:true});var He=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}),a=class extends Error{static{o(this,"SentriError");}code;statusCode;constructor(r,t,s){super(t),this.name="SentriError",this.code=r,this.statusCode=s??He[r]??500;}};var Ve=new WeakMap,Me=32,qe=10,Be=31;function ze(e){if(e.mode==="client"){if(!e.keyUri||e.keyUri.trim().length===0)throw new a("CONFIGURATION_ERROR","keyUri must not be empty");return}if(!e.secret||e.secret.trim().length===0)throw new a("CONFIGURATION_ERROR","secret must not be empty");if((e.algorithm??"HS256").startsWith("HS")&&e.secret.length<Me)throw new a("CONFIGURATION_ERROR",`secret must be at least ${Me} characters for HMAC algorithms`);let s=e.saltRounds??12;if(!Number.isInteger(s)||s<qe||s>Be)throw new a("CONFIGURATION_ERROR",`saltRounds must be an integer between ${qe} and ${Be}`);if(!e.validRoles||e.validRoles.length===0)throw new a("CONFIGURATION_ERROR","validRoles must contain at least one role");if(e.validIdentifiers!==void 0&&e.validIdentifiers.length===0)throw new a("CONFIGURATION_ERROR","validIdentifiers must contain at least one identifier type");if(!e.dialect)throw new a("CONFIGURATION_ERROR","dialect is required in server mode")}o(ze,"validateConfig");function k(e){let r=Ve.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),validIdentifiers:e.validIdentifiers??["email","username"],validIdentifiersSet:new Set(e.validIdentifiers??["email","username"]),cookieName:e.cookie?.name??"refresh_token",accessCookieName:e.accessCookie?.name??"access_token"};return Ve.set(e,t),t}o(k,"resolveServerConfig");var Dr=/^(\d+)([smhdw])$/,Sr={s:1e3,m:6e4,h:36e5,d:864e5,w:6048e5},je=new Map;function M(e){if(typeof e=="number")return e*1e3;let r=je.get(e);if(r!==void 0)return r;let t=Dr.exec(e);if(!t?.[1]||!t?.[2])throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let s=Sr[t[2]];if(s===void 0)throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let n=parseInt(t[1],10)*s;return je.set(e,n),n}o(M,"parseExpiry");var S={info:o(()=>{},"info"),warn:o(()=>{},"warn"),error:o(()=>{},"error")};function m(e,r,t){return {service:e,event:r,...t}}o(m,"buildLogData");async function Xe(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();}o(Xe,"runMigrations");var Ge=new Map;function D(e){let r=Ge.get(e);return r||(r=new kysely.Kysely({dialect:e}),Ge.set(e,r)),r}o(D,"getDatabase");async function Y(e,r=12){return Ze__default.default.hash(e,r)}o(Y,"hashPassword");async function Q(e,r){return Ze__default.default.compare(e,r)}o(Q,"verifyPassword");var We=new Map,Je=new Map,Lr=3600*1e3;function Qe(e){let r=We.get(e);if(!r){let t=crypto.createPrivateKey(e),s=crypto.createPublicKey(t),n=s.export({format:"jwk"}),c=crypto.createHash("sha256").update(JSON.stringify({e:n.e,kty:n.kty,n:n.n})).digest("base64url"),h={...n,use:"sig",kid:c};r={kid:c,publicKey:s,jwk:h},We.set(e,r);}return r}o(Qe,"getOrBuildKey");function er(e){let{jwk:r}=Qe(e);return {keys:[r]}}o(er,"buildJwks");function rr(e){return Qe(e).publicKey}o(rr,"getPublicKeyFromPrivate");async function tr(e){let r=Date.now(),t=Je.get(e);if(t&&r-t.fetchedAt<Lr)return t.publicKey;let s=await fetch(e);if(!s.ok)throw new a("CONFIGURATION_ERROR",`Failed to fetch public key from ${e}: HTTP ${s.status}`);let n=await s.json();if(!n.keys||n.keys.length===0)throw new a("CONFIGURATION_ERROR",`No keys found in JWKS response from ${e}`);let i=n.keys[0],c=crypto.createPublicKey({key:i,format:"jwk"});return Je.set(e,{publicKey:c,fetchedAt:r}),c}o(tr,"fetchPublicKey");var sr=new Map,or=new Map,nr=new Map;function ir(e){return e.startsWith("RS")||e.startsWith("PS")}o(ir,"isRSA");function ar(e){let r=sr.get(e);return r||(r={access:`${e}:access`,refresh:`${e}:refresh`},sr.set(e,r)),r}o(ar,"getHsSecrets");function Pr(e){let r=or.get(e);return r||(r=crypto.createPrivateKey(e),or.set(e,r)),r}o(Pr,"getRsPrivateKey");function ur(e){let r=k(e);if(ir(r.algorithm)){let n=Pr(e.secret);return {accessKey:n,refreshKey:n}}let{access:t,refresh:s}=ar(e.secret);return {accessKey:t,refreshKey:s}}o(ur,"getSigningKeys");function dr(e,r){let t=k(e);if(ir(t.algorithm))return rr(e.secret);let{access:s,refresh:n}=ar(e.secret);return r==="access"?s:n}o(dr,"getVerifyKey");function cr(e,r,t,s){let n=`${t}:${s}`,i=nr.get(n);return i||(i={expiresIn:t,algorithm:s},nr.set(n,i)),ee__default.default.sign(e,r,i)}o(cr,"sign");function lr(e,r,t){try{let s=ee__default.default.verify(e,r,{algorithms:[t]});if(typeof s=="string"||s===null)throw new a("TOKEN_INVALID","Token payload is not an object");return s}catch(s){throw s instanceof a?s:s instanceof ee__default.default.TokenExpiredError?new a("TOKEN_EXPIRED","Token has expired"):new a("TOKEN_INVALID","Token is invalid or malformed")}}o(lr,"verify");function re(e,r){let t=k(r),{accessKey:s}=ur(r);return cr(e,s,t.accessExpiresIn,t.algorithm)}o(re,"signAccessToken");function te(e,r){let t=k(r),{refreshKey:s}=ur(r);return cr({sessionId:e},s,t.refreshExpiresIn,t.algorithm)}o(te,"signRefreshToken");function q(e,r){let t=k(r),s=dr(r,"access");return lr(e,s,t.algorithm)}o(q,"verifyAccessToken");function se(e,r){let t=k(r),s=dr(r,"refresh");return lr(e,s,t.algorithm)}o(se,"verifyRefreshToken");function fr(e,r){try{let t=ee__default.default.verify(e,r,{algorithms:["RS256","RS384","RS512","PS256","PS384","PS512"]});if(typeof t=="string"||t===null)throw new a("TOKEN_INVALID","Token payload is not an object");return t}catch(t){throw t instanceof a?t:t instanceof ee__default.default.TokenExpiredError?new a("TOKEN_EXPIRED","Token has expired"):new a("TOKEN_INVALID","Token is invalid or malformed")}}o(fr,"verifyTokenWithPublicKey");function xe(e){try{return JSON.parse(e)}catch{return []}}o(xe,"parseRoles");function Kr(e){return JSON.stringify(e)}o(Kr,"serializeRoles");function mr(e){return {id:e.id,userId:e.user_id,type:e.type,value:e.value,createdAt:new Date(e.created_at)}}o(mr,"mapIdentifier");async function oe(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:xe(t.roles)}:null}o(oe,"findUserByIdentifierValue");async function de(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:xe(t.roles)}:null}o(de,"findUserById");async function pr(e,r,t){let s=t.map(n=>({id:crypto.randomUUID(),user_id:r,type:n.type,value:n.value}));return await e.insertInto("sentri_identifiers").values(s).execute(),s.map(n=>({id:n.id,userId:n.user_id,type:n.type,value:n.value,createdAt:new Date}))}o(pr,"createIdentifiers");async function ne(e,r){return (await e.selectFrom("sentri_identifiers").selectAll().where("user_id","=",r).orderBy("created_at","asc").execute()).map(mr)}o(ne,"findIdentifiersByUserId");async function Ne(e,r,t){let s=await e.selectFrom("sentri_identifiers").selectAll().where("id","=",r).where("user_id","=",t).executeTakeFirst();return s?mr(s):null}o(Ne,"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)}o(wr,"countIdentifiersByUserId");async function gr(e,r,t,s){await e.updateTable("sentri_identifiers").set({type:s.type,value:s.value}).where("id","=",r).where("user_id","=",t).execute();}o(gr,"updateIdentifier");async function yr(e,r,t){await e.deleteFrom("sentri_identifiers").where("user_id","=",r).where("id","in",t).execute();}o(yr,"deleteIdentifiers");async function Ir(e,r,t){await e.updateTable("sentri_users").set({password_hash:t}).where("id","=",r).execute();}o(Ir,"updateUserPassword");async function Rr(e,r,t){await e.updateTable("sentri_users").set({roles:Kr(t)}).where("id","=",r).execute();}o(Rr,"updateUserRoles");async function Ce(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}}o(Ce,"createSession");async function De(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:xe(t.roles)}}:null}o(De,"findSessionById");async function Se(e,r){await e.deleteFrom("sentri_sessions").where("id","=",r).execute();}o(Se,"deleteSession");async function _r(e,r,t){await e.updateTable("sentri_sessions").set({replaced_by:t}).where("id","=",r).execute();}o(_r,"markSessionReplaced");async function ce(e,r){await e.deleteFrom("sentri_sessions").where("user_id","=",r).execute();}o(ce,"deleteAllSessionsForUser");async function le(e,r,t){let s=k(r),n=D(r.dialect),i=e.roles??[],c=i.filter(E=>!s.validRolesSet.has(E));if(c.length>0)return {success:false,error:new a("INVALID_ROLE",`Invalid roles: ${c.join(", ")}`)};if(!e.identifiers||e.identifiers.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one identifier is required")};let h=e.identifiers.map(E=>({type:E.type.trim(),value:E.value.trim()})),u=h.filter(E=>!s.validIdentifiersSet.has(E.type));if(u.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${u.map(E=>E.type).join(", ")}`)};if(new Set(h.map(E=>E.value)).size!==h.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let E of h)if(await oe(n,E.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${E.value}`)};let v=await Y(e.password,s.saltRounds),{userId:N,identifierRows:C}=await n.transaction().execute(async E=>{let H=crypto.randomUUID();await E.insertInto("sentri_users").values({id:H,password_hash:v,roles:JSON.stringify(i)}).execute();let Z=h.map(V=>({id:crypto.randomUUID(),user_id:H,type:V.type,value:V.value}));return await E.insertInto("sentri_identifiers").values(Z).execute(),{userId:H,identifierRows:Z}}),b={success:true,user:{id:N,roles:i,identifiers:C.map(E=>({id:E.id,type:E.type,value:E.value}))}};return r.hooks?.onRegister&&await r.hooks.onRegister(b.user),b}o(le,"register");async function fe(e,r,t){let s=k(r),n=D(r.dialect),i=await oe(n,e.identifier.trim());if(!i){let C=new a("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,C,{ip:t?.ip??""}),{success:false,error:C}}if(!await Q(e.password,i.passwordHash)){let C=new a("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,C,{ip:t?.ip??""}),{success:false,error:C}}let h=new Date(Date.now()+M(s.refreshExpiresIn)),u=await Ce(n,{userId:i.id,expiresAt:h,ipAddress:t?.ip??null,userAgent:t?.userAgent??null}),d={id:i.id,roles:i.roles},v=re({id:i.id,roles:i.roles,sessionId:u.id},r),N=te(u.id,r);return r.hooks?.onLoginSuccess&&await r.hooks.onLoginSuccess(d,{ip:t?.ip??"",userAgent:t?.userAgent??""}),{success:true,accessToken:v,refreshToken:N,user:d}}o(fe,"login");async function B(e,r,t){let s=k(r),n=D(r.dialect),i;try{({sessionId:i}=se(e,r));}catch(C){return C instanceof a?{success:false,error:C}:{success:false,error:new a("TOKEN_INVALID","Invalid refresh token")}}let c=await De(n,i);if(!c)return {success:false,error:new a("UNAUTHORIZED","Session not found or revoked")};if(c.replacedBy)return await ce(n,c.userId),{success:false,error:new a("TOKEN_REUSE","Token reuse detected. All sessions revoked.")};if(c.expiresAt.getTime()<Date.now())return await Se(n,i),{success:false,error:new a("TOKEN_EXPIRED","Session has expired")};let h=new Date(Date.now()+M(s.refreshExpiresIn)),u=await Ce(n,{userId:c.userId,expiresAt:h,ipAddress:t?.ip??null,userAgent:t?.userAgent??null});await _r(n,i,u.id);let d={id:c.user.id,roles:c.user.roles},v=re({...d,sessionId:u.id},r),N=te(u.id,r);return {success:true,accessToken:v,refreshToken:N,user:d}}o(B,"refresh");async function he(e,r){let t=D(r.dialect),s;try{({sessionId:s}=se(e,r));}catch{return}let n=await De(t,s);n&&(await Se(t,s),r.hooks?.onLogout&&await r.hooks.onLogout(n.userId));}o(he,"logout");async function me(e,r){let t=D(r.dialect);await ce(t,e),r.hooks?.onLogout&&await r.hooks.onLogout(e);}o(me,"logoutAll");async function pe(e,r){let t=D(r.dialect),s=await de(t,e);if(!s)return {success:false,error:new a("USER_NOT_FOUND","User not found")};let n=await ne(t,e);return {success:true,user:{id:s.id,roles:s.roles,identifiers:n.map(i=>({id:i.id,type:i.type,value:i.value}))}}}o(pe,"getUser");async function we(e,r,t,s){let n=k(s),i=D(s.dialect),c=await de(i,e);if(!c)return {success:false,error:new a("USER_NOT_FOUND","User not found")};if(!await Q(r,c.passwordHash))return {success:false,error:new a("INVALID_CREDENTIALS","Invalid credentials")};let u=await Y(t,n.saltRounds);return await Ir(i,e,u),await ce(i,e),s.hooks?.onPasswordChanged&&await s.hooks.onPasswordChanged(e),{success:true}}o(we,"changePassword");async function ge(e,r,t){let s=k(t),n=D(t.dialect),i=r.filter(d=>!s.validRolesSet.has(d));if(i.length>0)return {success:false,error:new a("INVALID_ROLE",`Invalid roles: ${i.join(", ")}`)};let c=await de(n,e);if(!c)return {success:false,error:new a("USER_NOT_FOUND","User not found")};let h=new Set(c.roles);for(let d of r)h.add(d);let u=Array.from(h);return await Rr(n,e,u),{success:true,user:{id:c.id,roles:u}}}o(ge,"assignRoles");async function ye(e,r,t){let s=k(t),n=D(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one identifier is required")};let i=r.map(d=>({type:d.type.trim(),value:d.value.trim()})),c=i.filter(d=>!s.validIdentifiersSet.has(d.type));if(c.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${c.map(d=>d.type).join(", ")}`)};if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i)if(await oe(n,d.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)};return await pr(n,e,i),{success:true,identifiers:(await ne(n,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}o(ye,"bulkCreateIdentifiers");async function Ie(e,r,t){let s=k(t),n=D(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one update is required")};let i=r.map(d=>({id:d.id,type:d.type.trim(),value:d.value.trim()})),c=i.filter(d=>!s.validIdentifiersSet.has(d.type));if(c.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${c.map(d=>d.type).join(", ")}`)};if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i){let v=await Ne(n,d.id,e);if(!v)return {success:false,error:new a("IDENTIFIER_NOT_FOUND",`Identifier not found: ${d.id}`)};if(v.value!==d.value&&await oe(n,d.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)}}return await n.transaction().execute(async d=>{for(let v of i)await gr(d,v.id,e,{type:v.type,value:v.value});}),{success:true,identifiers:(await ne(n,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}o(Ie,"bulkUpdateIdentifiers");async function Re(e,r,t){let s=D(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one ID is required")};let n=Array.from(new Set(r));for(let h of n)if(!await Ne(s,h,e))return {success:false,error:new a("IDENTIFIER_NOT_FOUND",`Identifier not found: ${h}`)};return await wr(s,e)-n.length<1?{success:false,error:new a("VALIDATION_ERROR","Cannot delete all identifiers \u2014 at least one must remain")}:(await yr(s,e,n),{success:true,identifiers:(await ne(s,e)).map(h=>({id:h.id,type:h.type,value:h.value}))})}o(Re,"bulkDeleteIdentifiers");function F(e){return k(e).cookieName}o(F,"getCookieName");function _e(e){return k(e).accessCookieName}o(_e,"getAccessCookieName");function K(e,r){if(!e)return;let t=`${r}=`,s=0;for(;s<e.length;){for(;s<e.length&&e[s]===" ";)s++;let n=e.indexOf(";",s),i=n===-1?e.length:n;if(e.startsWith(t,s))return e.slice(s+t.length,i);s=i+1;}}o(K,"readCookie");function ie(e,r,t){let s=t.cookie??{},n=M(k(t).refreshExpiresIn)/1e3;e.cookie??={},e.cookie[F(t)]={value:r,httpOnly:s.httpOnly??true,secure:s.secure??false,sameSite:s.sameSite??"strict",path:s.path??"/",maxAge:n};}o(ie,"setCookieFromConfig");function ve(e,r){let t=r.cookie??{};e.cookie??={},e.cookie[F(r)]={value:"",httpOnly:t.httpOnly??true,path:t.path??"/",maxAge:0};}o(ve,"clearCookieFromConfig");function ae(e,r,t){if(!t.accessCookie)return;let s=t.accessCookie,n=M(k(t).accessExpiresIn)/1e3;e.cookie??={},e.cookie[_e(t)]={value:r,httpOnly:false,secure:s.secure??false,sameSite:s.sameSite??"strict",path:s.path??"/",maxAge:n};}o(ae,"setAccessCookieFromConfig");function Oe(e,r){if(!r.accessCookie)return;let t=r.accessCookie;e.cookie??={},e.cookie[_e(r)]={value:"",httpOnly:false,path:t.path??"/",maxAge:0};}o(Oe,"clearAccessCookieFromConfig");function j(e,r){let t=e.headers.get("authorization");if(t?.startsWith("Bearer "))return t.slice(7);let s=e.headers.get("cookie");return s?K(s,_e(r)):void 0}o(j,"getCurrentAccessToken");function be(e){let r=e.logger??S,t=e.loggerService??"sentri";if(e.mode==="client"){let n=e;return new elysia.Elysia().derive({as:"global"},async({request:i})=>{let c=i.headers.get("authorization"),h=c?.startsWith("Bearer ")?c.slice(7):void 0;if(!h)throw r.warn(m(t,"auth.protect.failure",{mode:"client",errorCode:"UNAUTHORIZED"})),new a("UNAUTHORIZED","Missing or malformed Authorization header");try{let u=await tr(n.keyUri),d=fr(h,u),v={id:d.id,roles:d.roles};return r.info(m(t,"auth.protect.success",{mode:"client",userId:d.id})),{user:v}}catch(u){let d=u instanceof a?u.code:"TOKEN_INVALID";throw r.warn(m(t,"auth.protect.failure",{mode:"client",errorCode:d})),u}})}let s=e;return new elysia.Elysia().derive({as:"global"},async({request:n,set:i})=>{let c=j(n,s);if(!c)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"UNAUTHORIZED"})),new a("UNAUTHORIZED","Missing or malformed Authorization header");try{let h=q(c,s);if(s.isTokenRevoked&&await s.isTokenRevoked(h.sessionId))throw r.warn(m(t,"auth.protect.token_revoked",{mode:"server",userId:h.id})),new a("UNAUTHORIZED","Token has been revoked");let u={id:h.id,roles:h.roles};return r.info(m(t,"auth.protect.success",{mode:"server",userId:h.id})),{user:u}}catch(h){if(h instanceof a&&h.code==="TOKEN_EXPIRED"){let d=n.headers.get("cookie"),v=d?K(d,F(s)):void 0;if(!v)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED"})),new a("UNAUTHORIZED","Token expired. Please login again.");let N;try{N=await B(v,s);}catch{throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED"})),new a("UNAUTHORIZED","Session expired. Please login again.")}if(!N.success)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:N.error.code})),new a("UNAUTHORIZED","Session expired. Please login again.");return ie(i,N.refreshToken,s),ae(i,N.accessToken,s),i.headers["X-New-Access-Token"]=N.accessToken,r.info(m(t,"auth.protect.auto_refresh",{mode:"server",userId:N.user.id})),{user:N.user}}let u=h instanceof a?h.code:"TOKEN_INVALID";throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:u})),h}})}o(be,"protect");function Ar(e,r,t){let s=`Requires one of roles: ${e.join(", ")}`;return ({user:n})=>{if(!n)throw r.warn(m(t,"auth.authorize.unauthenticated",{requiredRoles:e})),new a("UNAUTHORIZED","Not authenticated");let i=n.roles;if(!e.some(c=>i.includes(c)))throw r.warn(m(t,"auth.authorize.denied",{userId:n.id,userRoles:[...i],requiredRoles:e})),new a("FORBIDDEN",s);r.info(m(t,"auth.authorize.passed",{userId:n.id,userRoles:[...i],requiredRoles:e}));}}o(Ar,"createAuthorizeHandler");function Ue(e,r){return o(function(...s){return Ar(s,e,r)},"authorize")}o(Ue,"createAuthorize");function $r(...e){return Ar(e,S,"sentri")}o($r,"authorize");var Hr=new a("FORBIDDEN","You do not have permission to perform this action");function Er(e,r,t){return async s=>{let n=s.user;if(!n)throw r.warn(m(t,"auth.permit.unauthenticated",{})),new a("UNAUTHORIZED","Not authenticated");if(e.roles&&e.roles.length>0){let i=n.roles;if(e.roles.some(c=>i.includes(c))){r.info(m(t,"auth.permit.role_bypass",{userId:n.id,bypassedByRole:true}));return}}try{let i=e.check(s);if(i instanceof Promise?await i:i)r.info(m(t,"auth.permit.passed",{userId:n.id}));else throw r.warn(m(t,"auth.permit.denied",{userId:n.id})),Hr}catch(i){throw i}}}o(Er,"createPermitHandler");function Le(e,r){return o(function(s){return Er(typeof s=="function"?{check:s}:s,e,r)},"permit")}o(Le,"createPermit");function Vr(e){return Er(typeof e=="function"?{check:e}:e,S,"sentri")}o(Vr,"permit");function ue(e){return ({error:r,set:t})=>r instanceof a?(t.status=r.statusCode,{error:true,statusCode:r.statusCode,code:r.code,message:r.message,data:null}):(e?.onUnhandled?.(r),t.status=500,{error:true,statusCode:500,code:"INTERNAL_SERVER_ERROR",message:"Internal server error",data:null})}o(ue,"createErrorHandler");var Fe=class{static{o(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 n=Date.now(),i=this.store.get(r);if((!i||i.expiresAt<n)&&(i={count:0,expiresAt:n+s},this.store.set(r,i)),i.count>t){let c=Math.ceil((i.expiresAt-n)/1e3);throw new Error(`Rate limit exceeded. Try again in ${c} seconds.`)}i.count++;}},Pe=class{static{o(this,"RedisRateLimiter");}redis;logger;constructor(r,t){this.redis=r,this.logger=t;}async consume(r,t,s){let n=`sentri:rl:${r}`,i=Math.ceil(s/1e3);try{let c=await this.redis.multi().incr(n).expire(n,i,"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});}}},z=null;async function Tr(e,r){if(z)return z;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"}),z=new Pe(s,r??S),z}catch(t){r?.warn({msg:"Failed to connect to Redis for Rate Limiting, falling back to InMemoryRateLimiter",error:t});}return z=new Fe,z}o(Tr,"getRateLimiter");var ke=8,X=72,Ae=255,xr=100,G=50;function _(e){return new a("VALIDATION_ERROR",e)}o(_,"badRequest");function O(e,r,t,s){return e.status=r,{error:false,statusCode:r,message:t,data:s}}o(O,"ok");function $(e){if(e==null||typeof e!="object"||Array.isArray(e))throw new a("VALIDATION_ERROR","Request body is missing or not a JSON object. Did you set Content-Type: application/json?");return e}o($,"parseBody");function qr(e,r){if(!r.apiKey)return;let t=e.headers.get("x-api-key");if(typeof t!="string"||t!==r.apiKey)throw new a("UNAUTHORIZED","Invalid or missing API key")}o(qr,"validateApiKey");function Ke(e){if(e)try{let r=e();r instanceof Promise&&r.catch(()=>{});}catch{}}o(Ke,"fireHook");function Br(e){return e.startsWith("RS")||e.startsWith("PS")}o(Br,"isRSA");function $e(e,r){if(typeof e!="object"||e===null||Array.isArray(e))throw _(`identifiers[${r}] must be an object`);let t=e;if(typeof t.type!="string"||t.type.trim().length===0)throw _(`identifiers[${r}].type is required and must be a non-empty string`);if(t.type.length>xr)throw _(`identifiers[${r}].type must not exceed ${xr} characters`);if(typeof t.value!="string"||t.value.trim().length===0)throw _(`identifiers[${r}].value is required and must be a non-empty string`);if(t.value.length>Ae)throw _(`identifiers[${r}].value must not exceed ${Ae} characters`);return {type:t.type,value:t.value}}o($e,"validateIdentifierInput");async function P(e,r,t){let s=j(e,t);if(!s)throw new a("UNAUTHORIZED","Missing or malformed Authorization header");let n=q(s,t);if(t.isTokenRevoked&&await t.isTokenRevoked(n.sessionId))throw new a("UNAUTHORIZED","Token has been revoked");return {id:n.id,roles:n.roles}}o(P,"requireAuth");function Nr(e){let r=e,t=k(r),s=e.logger??S,n=e.loggerService??"sentri",i=e.router?.register??(l=>le(l,r)),c=e.router?.login??(l=>fe(l,r)),h=e.router?.refresh??(l=>B(l,r)),u=e.router?.logout??(l=>l!==void 0?he(l,r):Promise.resolve()),d=e.router?.logoutAll??(l=>me(l,r)),v=e.router?.getUser??(l=>pe(l,r)),N=e.router?.assignRoles??((l,f)=>ge(l,f,r)),C=e.router?.changePassword??((l,f,g)=>we(l,f,g,r)),b=e.router?.bulkCreateIdentifiers??((l,f)=>ye(l,f,r)),E=e.router?.bulkUpdateIdentifiers??((l,f)=>Ie(l,f,r)),H=e.router?.bulkDeleteIdentifiers??((l,f)=>Re(l,f,r)),Z=Tr(e.redisUrl,s),V=new elysia.Elysia().onError(ue());return Br(t.algorithm)&&V.get("/keys",({set:l})=>(l.headers["Cache-Control"]="public, max-age=3600",er(e.secret))),V.post("/register",async({body:l,request:f,set:g})=>{let I=Date.now();qr(f,r);let p=$(l),{identifiers:x,password:w,roles:R}=p;if(!Array.isArray(x)||x.length===0)throw _("identifiers is required and must be a non-empty array");if(x.length>G)throw _(`identifiers must not exceed ${G} entries`);let y=x.map((J,Ee)=>$e(J,Ee));if(typeof w!="string"||w.length<ke)throw _(`password is required and must be at least ${ke} characters`);if(w.length>X)throw _(`password must not exceed ${X} characters`);if(R!==void 0&&!Array.isArray(R))throw _("roles must be an array of strings when provided");if(Array.isArray(R)&&!R.every(J=>typeof J=="string"))throw _("each role must be a string");let T=Array.isArray(R)?R:void 0,A=T!==void 0?{identifiers:y,password:w,roles:T}:{identifiers:y,password:w},L=f.headers.get("x-forwarded-for")||"127.0.0.1",W=f.headers.get("user-agent")||"Unknown";if(e.rateLimit!==false){let J=await Z,Ee=typeof e.rateLimit=="object"?e.rateLimit.maxRegisterAttempts??5:5;await J.consume(`register:${L}`,Ee,900*1e3);}let U=await i(A,{ip:L,userAgent:W});return U.success?(s.info(m(n,"auth.register.success",{userId:U.user.id,duration_ms:Date.now()-I})),O(g,201,"User registered successfully",{user:U.user})):(s.warn(m(n,"auth.register.failure",{errorCode:U.error.code,duration_ms:Date.now()-I})),g.status=U.error.statusCode,{error:true,statusCode:U.error.statusCode,code:U.error.code,message:U.error.message,data:null})}).post("/login",async({body:l,request:f,set:g})=>{let I=Date.now(),p=$(l),{identifier:x,password:w}=p;if(typeof x!="string"||x.trim().length===0)throw _("identifier is required and must be a non-empty string");if(x.length>Ae)throw _(`identifier must not exceed ${Ae} characters`);if(typeof w!="string"||w.length===0)throw _("password is required");if(w.length>X)throw _(`password must not exceed ${X} characters`);let R=x.trim(),y=f.headers.get("x-forwarded-for")||"127.0.0.1",T=f.headers.get("user-agent")||"Unknown";if(e.rateLimit!==false){let L=await Z,W=typeof e.rateLimit=="object"?e.rateLimit.maxLoginAttempts??5:5;await L.consume(`login:${y}`,W,900*1e3);}let A=await c({identifier:R,password:w},{ip:y,userAgent:T});return A.success?(Ke(()=>r.hooks?.onLoginSuccess?.(A.user,{ip:y,userAgent:T})),ie(g,A.refreshToken,r),ae(g,A.accessToken,r),s.info(m(n,"auth.login.success",{userId:A.user.id,duration_ms:Date.now()-I})),O(g,200,"Login successful",{accessToken:A.accessToken,user:A.user})):(Ke(()=>r.hooks?.onLoginFailed?.(R,A.error,{ip:y})),s.warn(m(n,"auth.login.failure",{errorCode:A.error.code,duration_ms:Date.now()-I})),g.status=A.error.statusCode,{error:true,statusCode:A.error.statusCode,code:A.error.code,message:A.error.message,data:null})}).post("/refresh",async({request:l,set:f})=>{let g=Date.now(),I=K(l.headers.get("cookie")??void 0,F(r));if(!I)throw new a("UNAUTHORIZED","Refresh token cookie is missing");let p=l.headers.get("x-forwarded-for")||"127.0.0.1",x=l.headers.get("user-agent")||"Unknown",w=await h(I,{ip:p,userAgent:x});return w.success?(ie(f,w.refreshToken,r),ae(f,w.accessToken,r),s.info(m(n,"auth.refresh.success",{userId:w.user.id,duration_ms:Date.now()-g})),O(f,200,"Token refreshed",{accessToken:w.accessToken})):(ve(f,r),s.warn(m(n,"auth.refresh.failure",{errorCode:w.error.code,duration_ms:Date.now()-g})),f.status=w.error.statusCode,{error:true,statusCode:w.error.statusCode,code:w.error.code,message:w.error.message,data:null})}).post("/logout",async({request:l,set:f})=>{let g=Date.now(),I=K(l.headers.get("cookie")??void 0,F(r));return await u(I),ve(f,r),Oe(f,r),s.info(m(n,"auth.logout",{duration_ms:Date.now()-g})),O(f,200,"Logged out",null)}).post("/logout-all",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r);return await d(I.id),Ke(()=>r.hooks?.onLogout?.(I.id)),ve(f,r),Oe(f,r),s.info(m(n,"auth.logout_all",{userId:I.id,duration_ms:Date.now()-g})),O(f,200,"All sessions revoked",null)}).get("/me",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r),p=await v(I.id);return p.success?(s.info(m(n,"auth.me.success",{userId:p.user.id,duration_ms:Date.now()-g})),O(f,200,"OK",p.user)):(s.warn(m(n,"auth.me.failure",{userId:I.id,errorCode:p.error.code,duration_ms:Date.now()-g})),f.status=p.error.statusCode,{error:true,statusCode:p.error.statusCode,code:p.error.code,message:p.error.message,data:null})}).get("/me/identifiers",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r),p=await v(I.id);return p.success?(s.info(m(n,"auth.me.identifiers.success",{userId:p.user.id,count:p.user.identifiers?.length??0,duration_ms:Date.now()-g})),O(f,200,"OK",{identifiers:p.user.identifiers??[]})):(s.warn(m(n,"auth.me.identifiers.failure",{userId:I.id,errorCode:p.error.code,duration_ms:Date.now()-g})),f.status=p.error.statusCode,{error:true,statusCode:p.error.statusCode,code:p.error.code,message:p.error.message,data:null})}).post("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{identifiers:w}=x;if(!Array.isArray(w)||w.length===0)throw _("identifiers is required and must be a non-empty array");if(w.length>G)throw _(`identifiers must not exceed ${G} entries`);let R=w.map((T,A)=>$e(T,A)),y=await b(p.id,R);return y.success?(s.info(m(n,"auth.identifiers.created",{userId:p.id,count:y.identifiers.length,duration_ms:Date.now()-I})),O(g,201,"Identifiers added successfully",{identifiers:y.identifiers})):(s.warn(m(n,"auth.identifiers.create_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).put("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{identifiers:w}=x;if(!Array.isArray(w)||w.length===0)throw _("identifiers is required and must be a non-empty array");if(w.length>G)throw _(`identifiers must not exceed ${G} entries`);let R=w.map((T,A)=>{if(typeof T!="object"||T===null||Array.isArray(T))throw _(`identifiers[${A}] must be an object`);let L=T;if(typeof L.id!="string"||L.id.trim().length===0)throw _(`identifiers[${A}].id is required and must be a non-empty string`);let{type:W,value:U}=$e(T,A);return {id:L.id,type:W,value:U}}),y=await E(p.id,R);return y.success?(s.info(m(n,"auth.identifiers.updated",{userId:p.id,count:y.identifiers.length,duration_ms:Date.now()-I})),O(g,200,"Identifiers updated successfully",{identifiers:y.identifiers})):(s.warn(m(n,"auth.identifiers.update_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).delete("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{ids:w}=x;if(!Array.isArray(w)||w.length===0)throw _("ids is required and must be a non-empty array of strings");if(!w.every(y=>typeof y=="string"))throw _("each id must be a string");let R=await H(p.id,w);return R.success?(s.info(m(n,"auth.identifiers.deleted",{userId:p.id,duration_ms:Date.now()-I})),O(g,200,"Identifiers deleted successfully",{identifiers:R.identifiers})):(s.warn(m(n,"auth.identifiers.delete_failure",{userId:p.id,errorCode:R.error.code,duration_ms:Date.now()-I})),g.status=R.error.statusCode,{error:true,statusCode:R.error.statusCode,code:R.error.code,message:R.error.message,data:null})}).patch("/me/password",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{currentPassword:w,newPassword:R}=x;if(typeof w!="string"||w.length===0)throw _("currentPassword is required");if(typeof R!="string"||R.length<ke)throw _(`newPassword must be at least ${ke} characters`);if(R.length>X)throw _(`newPassword must not exceed ${X} characters`);if(w===R)throw _("newPassword must be different from currentPassword");let y=await C(p.id,w,R);return y.success?(s.info(m(n,"auth.password.changed",{userId:p.id,duration_ms:Date.now()-I})),O(g,200,"Password updated successfully. All sessions have been revoked.",null)):(s.warn(m(n,"auth.password.change_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).post("/users/:userId/roles",async({body:l,request:f,set:g,params:I})=>{let p=Date.now();if(!(await P(f,g,r)).roles.includes("admin"))throw new a("FORBIDDEN","Requires one of roles: admin");let w=$(l),{roles:R}=w,{userId:y}=I;if(!y)throw _("userId is required");if(!Array.isArray(R)||R.length===0)throw _("roles must be a non-empty array of strings");if(!R.every(A=>typeof A=="string"))throw _("each role must be a string");let T=await N(y,R);return T.success?(s.info(m(n,"auth.roles.assigned",{targetUserId:y,roles:R,duration_ms:Date.now()-p})),O(g,200,"Roles assigned successfully",{user:T.user})):(s.warn(m(n,"auth.roles.assign_failure",{targetUserId:y,errorCode:T.error.code,duration_ms:Date.now()-p})),g.status=T.error.statusCode,{error:true,statusCode:T.error.statusCode,code:T.error.code,message:T.error.message,data:null})}),V}o(Nr,"createAuthRouter");function Ls(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}};ze(u);let d=u.logger??S,v=u.loggerService??"sentri",N=Ue(d,v),C=Le(d,v);return {protect:o(()=>be(u),"protect"),authorize:o((...b)=>N(...b),"authorize"),permit:o(b=>C(b),"permit"),errorHandler:o(b=>ue(b),"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.validIdentifiers!==void 0&&{validIdentifiers:e.validIdentifiers},...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??S,n=t.loggerService??"sentri",i=k(t),c=Ue(s,n),h=Le(s,n);return {protect:o(()=>be(t),"protect"),authorize:o((...u)=>c(...u),"authorize"),permit:o(u=>h(u),"permit"),errorHandler:o(u=>ue(u),"errorHandler"),router:o(()=>Nr(t),"router"),migrate:o(()=>Xe(D(t.dialect)),"migrate"),getCurrentAccessToken:o(u=>j(u,t),"getCurrentAccessToken"),hashPassword:o(u=>Y(u,i.saltRounds),"hashPassword"),verifyPassword:o((u,d)=>Q(u,d),"verifyPassword"),signAccessToken:o(u=>re(u,t),"signAccessToken"),signRefreshToken:o(u=>te(u,t),"signRefreshToken"),verifyAccessToken:o(u=>q(u,t),"verifyAccessToken"),verifyRefreshToken:o(u=>se(u,t),"verifyRefreshToken"),register:o(u=>le(u,t),"register"),login:o(u=>fe(u,t),"login"),refresh:o(u=>B(u,t),"refresh"),logout:o(u=>he(u,t),"logout"),logoutAll:o(u=>me(u,t),"logoutAll"),getUser:o(u=>pe(u,t),"getUser"),changePassword:o((u,d,v)=>we(u,d,v,t),"changePassword"),assignRoles:o((u,d)=>ge(u,d,t),"assignRoles"),bulkCreateIdentifiers:o((u,d)=>ye(u,d,t),"bulkCreateIdentifiers"),bulkUpdateIdentifiers:o((u,d)=>Ie(u,d,t),"bulkUpdateIdentifiers"),bulkDeleteIdentifiers:o((u,d)=>Re(u,d,t),"bulkDeleteIdentifiers")}}o(Ls,"createAuthElysia");exports.SENTRI_ERROR_STATUS=He;exports.SentriError=a;exports.authorize=$r;exports.createAuthElysia=Ls;exports.createAuthRouter=Nr;exports.createAuthorize=Ue;exports.createErrorHandler=ue;exports.createPermit=Le;exports.getCurrentAccessToken=j;exports.permit=Vr;exports.protect=be;
1
+ 'use strict';var crypto=require('crypto'),kysely=require('kysely'),Ze=require('bcrypt'),ee=require('jsonwebtoken'),elysia=require('elysia');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Ze__default=/*#__PURE__*/_interopDefault(Ze);var ee__default=/*#__PURE__*/_interopDefault(ee);var Cr=Object.defineProperty;var o=(e,r)=>Cr(e,"name",{value:r,configurable:true});var He=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,RATE_LIMIT_EXCEEDED:429}),a=class extends Error{static{o(this,"SentriError");}code;statusCode;constructor(r,t,s){super(t),this.name="SentriError",this.code=r,this.statusCode=s??He[r]??500;}};var Me=new WeakMap,Ve=32,qe=10,Be=31;function Xe(e){if(e.mode==="client"){if(!e.keyUri||e.keyUri.trim().length===0)throw new a("CONFIGURATION_ERROR","keyUri must not be empty");return}if(!e.secret||e.secret.trim().length===0)throw new a("CONFIGURATION_ERROR","secret must not be empty");if((e.algorithm??"HS256").startsWith("HS")&&e.secret.length<Ve)throw new a("CONFIGURATION_ERROR",`secret must be at least ${Ve} characters for HMAC algorithms`);let s=e.saltRounds??12;if(!Number.isInteger(s)||s<qe||s>Be)throw new a("CONFIGURATION_ERROR",`saltRounds must be an integer between ${qe} and ${Be}`);if(!e.validRoles||e.validRoles.length===0)throw new a("CONFIGURATION_ERROR","validRoles must contain at least one role");if(e.validIdentifiers!==void 0&&e.validIdentifiers.length===0)throw new a("CONFIGURATION_ERROR","validIdentifiers must contain at least one identifier type");if(!e.dialect)throw new a("CONFIGURATION_ERROR","dialect is required in server mode")}o(Xe,"validateConfig");function A(e){let r=Me.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),validIdentifiers:e.validIdentifiers??["email","username"],validIdentifiersSet:new Set(e.validIdentifiers??["email","username"]),cookieName:e.cookie?.name??"refresh_token",accessCookieName:e.accessCookie?.name??"access_token"};return Me.set(e,t),t}o(A,"resolveServerConfig");var Nr=/^(\d+)([smhdw])$/,Sr={s:1e3,m:6e4,h:36e5,d:864e5,w:6048e5},je=new Map;function V(e){if(typeof e=="number")return e*1e3;let r=je.get(e);if(r!==void 0)return r;let t=Nr.exec(e);if(!t?.[1]||!t?.[2])throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let s=Sr[t[2]];if(s===void 0)throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let n=parseInt(t[1],10)*s;return je.set(e,n),n}o(V,"parseExpiry");var S={info:o(()=>{},"info"),warn:o(()=>{},"warn"),error:o(()=>{},"error")};function m(e,r,t){return {service:e,event:r,...t}}o(m,"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();}o(ze,"runMigrations");var Ge=new Map;function N(e){let r=Ge.get(e);return r||(r=new kysely.Kysely({dialect:e}),Ge.set(e,r)),r}o(N,"getDatabase");async function Y(e,r=12){return Ze__default.default.hash(e,r)}o(Y,"hashPassword");async function Q(e,r){return Ze__default.default.compare(e,r)}o(Q,"verifyPassword");var We=new Map,Je=new Map,Lr=3600*1e3;function Qe(e){let r=We.get(e);if(!r){let t=crypto.createPrivateKey(e),s=crypto.createPublicKey(t),n=s.export({format:"jwk"}),c=crypto.createHash("sha256").update(JSON.stringify({e:n.e,kty:n.kty,n:n.n})).digest("base64url"),h={...n,use:"sig",kid:c};r={kid:c,publicKey:s,jwk:h},We.set(e,r);}return r}o(Qe,"getOrBuildKey");function er(e){let{jwk:r}=Qe(e);return {keys:[r]}}o(er,"buildJwks");function rr(e){return Qe(e).publicKey}o(rr,"getPublicKeyFromPrivate");async function tr(e){let r=Date.now(),t=Je.get(e);if(t&&r-t.fetchedAt<Lr)return t.publicKey;let s=await fetch(e);if(!s.ok)throw new a("CONFIGURATION_ERROR",`Failed to fetch public key from ${e}: HTTP ${s.status}`);let n=await s.json();if(!n.keys||n.keys.length===0)throw new a("CONFIGURATION_ERROR",`No keys found in JWKS response from ${e}`);let i=n.keys[0],c=crypto.createPublicKey({key:i,format:"jwk"});return Je.set(e,{publicKey:c,fetchedAt:r}),c}o(tr,"fetchPublicKey");var sr=new Map,or=new Map,nr=new Map;function ir(e){return e.startsWith("RS")||e.startsWith("PS")}o(ir,"isRSA");function ar(e){let r=sr.get(e);return r||(r={access:`${e}:access`,refresh:`${e}:refresh`},sr.set(e,r)),r}o(ar,"getHsSecrets");function Pr(e){let r=or.get(e);return r||(r=crypto.createPrivateKey(e),or.set(e,r)),r}o(Pr,"getRsPrivateKey");function ur(e){let r=A(e);if(ir(r.algorithm)){let n=Pr(e.secret);return {accessKey:n,refreshKey:n}}let{access:t,refresh:s}=ar(e.secret);return {accessKey:t,refreshKey:s}}o(ur,"getSigningKeys");function dr(e,r){let t=A(e);if(ir(t.algorithm))return rr(e.secret);let{access:s,refresh:n}=ar(e.secret);return r==="access"?s:n}o(dr,"getVerifyKey");function cr(e,r,t,s){let n=`${t}:${s}`,i=nr.get(n);return i||(i={expiresIn:t,algorithm:s},nr.set(n,i)),ee__default.default.sign(e,r,i)}o(cr,"sign");function lr(e,r,t){try{let s=ee__default.default.verify(e,r,{algorithms:[t]});if(typeof s=="string"||s===null)throw new a("TOKEN_INVALID","Token payload is not an object");return s}catch(s){throw s instanceof a?s:s instanceof ee__default.default.TokenExpiredError?new a("TOKEN_EXPIRED","Token has expired"):new a("TOKEN_INVALID","Token is invalid or malformed")}}o(lr,"verify");function re(e,r){let t=A(r),{accessKey:s}=ur(r);return cr(e,s,t.accessExpiresIn,t.algorithm)}o(re,"signAccessToken");function te(e,r){let t=A(r),{refreshKey:s}=ur(r);return cr({sessionId:e},s,t.refreshExpiresIn,t.algorithm)}o(te,"signRefreshToken");function q(e,r){let t=A(r),s=dr(r,"access");return lr(e,s,t.algorithm)}o(q,"verifyAccessToken");function se(e,r){let t=A(r),s=dr(r,"refresh");return lr(e,s,t.algorithm)}o(se,"verifyRefreshToken");function fr(e,r){try{let t=ee__default.default.verify(e,r,{algorithms:["RS256","RS384","RS512","PS256","PS384","PS512"]});if(typeof t=="string"||t===null)throw new a("TOKEN_INVALID","Token payload is not an object");return t}catch(t){throw t instanceof a?t:t instanceof ee__default.default.TokenExpiredError?new a("TOKEN_EXPIRED","Token has expired"):new a("TOKEN_INVALID","Token is invalid or malformed")}}o(fr,"verifyTokenWithPublicKey");function xe(e){try{return JSON.parse(e)}catch{return []}}o(xe,"parseRoles");function Kr(e){return JSON.stringify(e)}o(Kr,"serializeRoles");function mr(e){return {id:e.id,userId:e.user_id,type:e.type,value:e.value,createdAt:new Date(e.created_at)}}o(mr,"mapIdentifier");async function oe(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:xe(t.roles)}:null}o(oe,"findUserByIdentifierValue");async function de(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:xe(t.roles)}:null}o(de,"findUserById");async function pr(e,r,t){let s=t.map(n=>({id:crypto.randomUUID(),user_id:r,type:n.type,value:n.value}));return await e.insertInto("sentri_identifiers").values(s).execute(),s.map(n=>({id:n.id,userId:n.user_id,type:n.type,value:n.value,createdAt:new Date}))}o(pr,"createIdentifiers");async function ne(e,r){return (await e.selectFrom("sentri_identifiers").selectAll().where("user_id","=",r).orderBy("created_at","asc").execute()).map(mr)}o(ne,"findIdentifiersByUserId");async function De(e,r,t){let s=await e.selectFrom("sentri_identifiers").selectAll().where("id","=",r).where("user_id","=",t).executeTakeFirst();return s?mr(s):null}o(De,"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)}o(wr,"countIdentifiersByUserId");async function gr(e,r,t,s){await e.updateTable("sentri_identifiers").set({type:s.type,value:s.value}).where("id","=",r).where("user_id","=",t).execute();}o(gr,"updateIdentifier");async function yr(e,r,t){await e.deleteFrom("sentri_identifiers").where("user_id","=",r).where("id","in",t).execute();}o(yr,"deleteIdentifiers");async function Ir(e,r,t){await e.updateTable("sentri_users").set({password_hash:t}).where("id","=",r).execute();}o(Ir,"updateUserPassword");async function Rr(e,r,t){await e.updateTable("sentri_users").set({roles:Kr(t)}).where("id","=",r).execute();}o(Rr,"updateUserRoles");async function Ce(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}}o(Ce,"createSession");async function Ne(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:xe(t.roles)}}:null}o(Ne,"findSessionById");async function Se(e,r){await e.deleteFrom("sentri_sessions").where("id","=",r).execute();}o(Se,"deleteSession");async function _r(e,r,t){await e.updateTable("sentri_sessions").set({replaced_by:t}).where("id","=",r).execute();}o(_r,"markSessionReplaced");async function ce(e,r){await e.deleteFrom("sentri_sessions").where("user_id","=",r).execute();}o(ce,"deleteAllSessionsForUser");async function le(e,r,t){let s=A(r),n=N(r.dialect),i=e.roles??[],c=i.filter(k=>!s.validRolesSet.has(k));if(c.length>0)return {success:false,error:new a("INVALID_ROLE",`Invalid roles: ${c.join(", ")}`)};if(!e.identifiers||e.identifiers.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one identifier is required")};let h=e.identifiers.map(k=>({type:k.type.trim(),value:k.value.trim()})),u=h.filter(k=>!s.validIdentifiersSet.has(k.type));if(u.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${u.map(k=>k.type).join(", ")}`)};if(new Set(h.map(k=>k.value)).size!==h.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let k of h)if(await oe(n,k.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${k.value}`)};let v=await Y(e.password,s.saltRounds),{userId:D,identifierRows:C}=await n.transaction().execute(async k=>{let H=crypto.randomUUID();await k.insertInto("sentri_users").values({id:H,password_hash:v,roles:JSON.stringify(i)}).execute();let Z=h.map(M=>({id:crypto.randomUUID(),user_id:H,type:M.type,value:M.value}));return await k.insertInto("sentri_identifiers").values(Z).execute(),{userId:H,identifierRows:Z}}),b={success:true,user:{id:D,roles:i,identifiers:C.map(k=>({id:k.id,type:k.type,value:k.value}))}};return r.hooks?.onRegister&&await r.hooks.onRegister(b.user),b}o(le,"register");async function fe(e,r,t){let s=A(r),n=N(r.dialect),i=await oe(n,e.identifier.trim());if(!i){let C=new a("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,C,{ip:t?.ip??""}),{success:false,error:C}}if(!await Q(e.password,i.passwordHash)){let C=new a("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,C,{ip:t?.ip??""}),{success:false,error:C}}let h=new Date(Date.now()+V(s.refreshExpiresIn)),u=await Ce(n,{userId:i.id,expiresAt:h,ipAddress:t?.ip??null,userAgent:t?.userAgent??null}),d={id:i.id,roles:i.roles},v=re({id:i.id,roles:i.roles,sessionId:u.id},r),D=te(u.id,r);return r.hooks?.onLoginSuccess&&await r.hooks.onLoginSuccess(d,{ip:t?.ip??"",userAgent:t?.userAgent??""}),{success:true,accessToken:v,refreshToken:D,user:d}}o(fe,"login");async function B(e,r,t){let s=A(r),n=N(r.dialect),i;try{({sessionId:i}=se(e,r));}catch(C){return C instanceof a?{success:false,error:C}:{success:false,error:new a("TOKEN_INVALID","Invalid refresh token")}}let c=await Ne(n,i);if(!c)return {success:false,error:new a("UNAUTHORIZED","Session not found or revoked")};if(c.replacedBy)return await ce(n,c.userId),{success:false,error:new a("TOKEN_REUSE","Token reuse detected. All sessions revoked.")};if(c.expiresAt.getTime()<Date.now())return await Se(n,i),{success:false,error:new a("TOKEN_EXPIRED","Session has expired")};let h=new Date(Date.now()+V(s.refreshExpiresIn)),u=await Ce(n,{userId:c.userId,expiresAt:h,ipAddress:t?.ip??null,userAgent:t?.userAgent??null});await _r(n,i,u.id);let d={id:c.user.id,roles:c.user.roles},v=re({...d,sessionId:u.id},r),D=te(u.id,r);return {success:true,accessToken:v,refreshToken:D,user:d}}o(B,"refresh");async function he(e,r){let t=N(r.dialect),s;try{({sessionId:s}=se(e,r));}catch{return}let n=await Ne(t,s);n&&(await Se(t,s),r.hooks?.onLogout&&await r.hooks.onLogout(n.userId));}o(he,"logout");async function me(e,r){let t=N(r.dialect);await ce(t,e),r.hooks?.onLogout&&await r.hooks.onLogout(e);}o(me,"logoutAll");async function pe(e,r){let t=N(r.dialect),s=await de(t,e);if(!s)return {success:false,error:new a("USER_NOT_FOUND","User not found")};let n=await ne(t,e);return {success:true,user:{id:s.id,roles:s.roles,identifiers:n.map(i=>({id:i.id,type:i.type,value:i.value}))}}}o(pe,"getUser");async function we(e,r,t,s){let n=A(s),i=N(s.dialect),c=await de(i,e);if(!c)return {success:false,error:new a("USER_NOT_FOUND","User not found")};if(!await Q(r,c.passwordHash))return {success:false,error:new a("INVALID_CREDENTIALS","Invalid credentials")};let u=await Y(t,n.saltRounds);return await Ir(i,e,u),await ce(i,e),s.hooks?.onPasswordChanged&&await s.hooks.onPasswordChanged(e),{success:true}}o(we,"changePassword");async function ge(e,r,t){let s=A(t),n=N(t.dialect),i=r.filter(d=>!s.validRolesSet.has(d));if(i.length>0)return {success:false,error:new a("INVALID_ROLE",`Invalid roles: ${i.join(", ")}`)};let c=await de(n,e);if(!c)return {success:false,error:new a("USER_NOT_FOUND","User not found")};let h=new Set(c.roles);for(let d of r)h.add(d);let u=Array.from(h);return await Rr(n,e,u),{success:true,user:{id:c.id,roles:u}}}o(ge,"assignRoles");async function ye(e,r,t){let s=A(t),n=N(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one identifier is required")};let i=r.map(d=>({type:d.type.trim(),value:d.value.trim()})),c=i.filter(d=>!s.validIdentifiersSet.has(d.type));if(c.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${c.map(d=>d.type).join(", ")}`)};if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i)if(await oe(n,d.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)};return await pr(n,e,i),{success:true,identifiers:(await ne(n,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}o(ye,"bulkCreateIdentifiers");async function Ie(e,r,t){let s=A(t),n=N(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one update is required")};let i=r.map(d=>({id:d.id,type:d.type.trim(),value:d.value.trim()})),c=i.filter(d=>!s.validIdentifiersSet.has(d.type));if(c.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${c.map(d=>d.type).join(", ")}`)};if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i){let v=await De(n,d.id,e);if(!v)return {success:false,error:new a("IDENTIFIER_NOT_FOUND",`Identifier not found: ${d.id}`)};if(v.value!==d.value&&await oe(n,d.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)}}return await n.transaction().execute(async d=>{for(let v of i)await gr(d,v.id,e,{type:v.type,value:v.value});}),{success:true,identifiers:(await ne(n,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}o(Ie,"bulkUpdateIdentifiers");async function Re(e,r,t){let s=N(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one ID is required")};let n=Array.from(new Set(r));for(let h of n)if(!await De(s,h,e))return {success:false,error:new a("IDENTIFIER_NOT_FOUND",`Identifier not found: ${h}`)};return await wr(s,e)-n.length<1?{success:false,error:new a("VALIDATION_ERROR","Cannot delete all identifiers \u2014 at least one must remain")}:(await yr(s,e,n),{success:true,identifiers:(await ne(s,e)).map(h=>({id:h.id,type:h.type,value:h.value}))})}o(Re,"bulkDeleteIdentifiers");function F(e){return A(e).cookieName}o(F,"getCookieName");function _e(e){return A(e).accessCookieName}o(_e,"getAccessCookieName");function K(e,r){if(!e)return;let t=`${r}=`,s=0;for(;s<e.length;){for(;s<e.length&&e[s]===" ";)s++;let n=e.indexOf(";",s),i=n===-1?e.length:n;if(e.startsWith(t,s))return e.slice(s+t.length,i);s=i+1;}}o(K,"readCookie");function ie(e,r,t){let s=t.cookie??{},n=V(A(t).refreshExpiresIn)/1e3;e.cookie??={},e.cookie[F(t)]={value:r,httpOnly:s.httpOnly??true,secure:s.secure??false,sameSite:s.sameSite??"strict",path:s.path??"/",maxAge:n};}o(ie,"setCookieFromConfig");function ve(e,r){let t=r.cookie??{};e.cookie??={},e.cookie[F(r)]={value:"",httpOnly:t.httpOnly??true,path:t.path??"/",maxAge:0};}o(ve,"clearCookieFromConfig");function ae(e,r,t){if(!t.accessCookie)return;let s=t.accessCookie,n=V(A(t).accessExpiresIn)/1e3;e.cookie??={},e.cookie[_e(t)]={value:r,httpOnly:false,secure:s.secure??false,sameSite:s.sameSite??"strict",path:s.path??"/",maxAge:n};}o(ae,"setAccessCookieFromConfig");function Oe(e,r){if(!r.accessCookie)return;let t=r.accessCookie;e.cookie??={},e.cookie[_e(r)]={value:"",httpOnly:false,path:t.path??"/",maxAge:0};}o(Oe,"clearAccessCookieFromConfig");function j(e,r){let t=e.headers.get("authorization");if(t?.startsWith("Bearer "))return t.slice(7);let s=e.headers.get("cookie");return s?K(s,_e(r)):void 0}o(j,"getCurrentAccessToken");function be(e){let r=e.logger??S,t=e.loggerService??"sentri";if(e.mode==="client"){let n=e;return new elysia.Elysia().derive({as:"global"},async({request:i})=>{let c=i.headers.get("authorization"),h=c?.startsWith("Bearer ")?c.slice(7):void 0;if(!h)throw r.warn(m(t,"auth.protect.failure",{mode:"client",errorCode:"UNAUTHORIZED"})),new a("UNAUTHORIZED","Missing or malformed Authorization header");try{let u=await tr(n.keyUri),d=fr(h,u),v={id:d.id,roles:d.roles};return r.info(m(t,"auth.protect.success",{mode:"client",userId:d.id})),{user:v}}catch(u){let d=u instanceof a?u.code:"TOKEN_INVALID";throw r.warn(m(t,"auth.protect.failure",{mode:"client",errorCode:d})),u}})}let s=e;return new elysia.Elysia().derive({as:"global"},async({request:n,set:i})=>{let c=j(n,s);if(!c)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"UNAUTHORIZED"})),new a("UNAUTHORIZED","Missing or malformed Authorization header");try{let h=q(c,s);if(s.isTokenRevoked&&await s.isTokenRevoked(h.sessionId))throw r.warn(m(t,"auth.protect.token_revoked",{mode:"server",userId:h.id})),new a("UNAUTHORIZED","Token has been revoked");let u={id:h.id,roles:h.roles};return r.info(m(t,"auth.protect.success",{mode:"server",userId:h.id})),{user:u}}catch(h){if(h instanceof a&&h.code==="TOKEN_EXPIRED"){let d=n.headers.get("cookie"),v=d?K(d,F(s)):void 0;if(!v)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED"})),new a("UNAUTHORIZED","Token expired. Please login again.");let D;try{D=await B(v,s);}catch{throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED"})),new a("UNAUTHORIZED","Session expired. Please login again.")}if(!D.success)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:D.error.code})),new a("UNAUTHORIZED","Session expired. Please login again.");return ie(i,D.refreshToken,s),ae(i,D.accessToken,s),i.headers["X-New-Access-Token"]=D.accessToken,r.info(m(t,"auth.protect.auto_refresh",{mode:"server",userId:D.user.id})),{user:D.user}}let u=h instanceof a?h.code:"TOKEN_INVALID";throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:u})),h}})}o(be,"protect");function Er(e,r,t){let s=`Requires one of roles: ${e.join(", ")}`;return ({user:n})=>{if(!n)throw r.warn(m(t,"auth.authorize.unauthenticated",{requiredRoles:e})),new a("UNAUTHORIZED","Not authenticated");let i=n.roles;if(!e.some(c=>i.includes(c)))throw r.warn(m(t,"auth.authorize.denied",{userId:n.id,userRoles:[...i],requiredRoles:e})),new a("FORBIDDEN",s);r.info(m(t,"auth.authorize.passed",{userId:n.id,userRoles:[...i],requiredRoles:e}));}}o(Er,"createAuthorizeHandler");function Ue(e,r){return o(function(...s){return Er(s,e,r)},"authorize")}o(Ue,"createAuthorize");function $r(...e){return Er(e,S,"sentri")}o($r,"authorize");var Hr=new a("FORBIDDEN","You do not have permission to perform this action");function kr(e,r,t){return async s=>{let n=s.user;if(!n)throw r.warn(m(t,"auth.permit.unauthenticated",{})),new a("UNAUTHORIZED","Not authenticated");if(e.roles&&e.roles.length>0){let i=n.roles;if(e.roles.some(c=>i.includes(c))){r.info(m(t,"auth.permit.role_bypass",{userId:n.id,bypassedByRole:true}));return}}try{let i=e.check(s);if(i instanceof Promise?await i:i)r.info(m(t,"auth.permit.passed",{userId:n.id}));else throw r.warn(m(t,"auth.permit.denied",{userId:n.id})),Hr}catch(i){throw i}}}o(kr,"createPermitHandler");function Le(e,r){return o(function(s){return kr(typeof s=="function"?{check:s}:s,e,r)},"permit")}o(Le,"createPermit");function Mr(e){return kr(typeof e=="function"?{check:e}:e,S,"sentri")}o(Mr,"permit");function ue(e){return ({error:r,set:t})=>r instanceof a?(t.status=r.statusCode,{error:true,statusCode:r.statusCode,code:r.code,message:r.message,data:null}):(e?.onUnhandled?.(r),t.status=500,{error:true,statusCode:500,code:"INTERNAL_SERVER_ERROR",message:"Internal server error",data:null})}o(ue,"createErrorHandler");var Fe=class{static{o(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 n=Date.now(),i=this.store.get(r);if((!i||i.expiresAt<n)&&(i={count:0,expiresAt:n+s},this.store.set(r,i)),i.count>=t){let c=Math.ceil((i.expiresAt-n)/1e3);throw new a("RATE_LIMIT_EXCEEDED",`Rate limit exceeded. Try again in ${c} seconds.`)}i.count++;}},Pe=class{static{o(this,"RedisRateLimiter");}redis;logger;constructor(r,t){this.redis=r,this.logger=t;}async consume(r,t,s){let n=`sentri:rl:${r}`,i=Math.ceil(s/1e3);try{let c=await this.redis.multi().incr(n).expire(n,i,"NX").exec();if(!c||c.length===0)return;if(c[0][1]>t)throw new a("RATE_LIMIT_EXCEEDED","Rate limit exceeded. Try again later.")}catch(c){if(c instanceof a&&c.code==="RATE_LIMIT_EXCEEDED")throw c;this.logger.error({msg:"Redis RateLimiter failed, bypassing limit",error:c});}}},X=null;async function Tr(e,r){if(X)return X;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"}),X=new Pe(s,r??S),X}catch(t){r?.warn({msg:"Failed to connect to Redis for Rate Limiting, falling back to InMemoryRateLimiter",error:t});}return X=new Fe,X}o(Tr,"getRateLimiter");var Ae=8,z=72,Ee=255,xr=100,G=50;function _(e){return new a("VALIDATION_ERROR",e)}o(_,"badRequest");function O(e,r,t,s){return e.status=r,{error:false,statusCode:r,message:t,data:s}}o(O,"ok");function $(e){if(e==null||typeof e!="object"||Array.isArray(e))throw new a("VALIDATION_ERROR","Request body is missing or not a JSON object. Did you set Content-Type: application/json?");return e}o($,"parseBody");function qr(e,r){if(!r.apiKey)return;let t=e.headers.get("x-api-key");if(typeof t!="string"||t!==r.apiKey)throw new a("UNAUTHORIZED","Invalid or missing API key")}o(qr,"validateApiKey");function Ke(e){if(e)try{let r=e();r instanceof Promise&&r.catch(()=>{});}catch{}}o(Ke,"fireHook");function Br(e){return e.startsWith("RS")||e.startsWith("PS")}o(Br,"isRSA");function $e(e,r){if(typeof e!="object"||e===null||Array.isArray(e))throw _(`identifiers[${r}] must be an object`);let t=e;if(typeof t.type!="string"||t.type.trim().length===0)throw _(`identifiers[${r}].type is required and must be a non-empty string`);if(t.type.length>xr)throw _(`identifiers[${r}].type must not exceed ${xr} characters`);if(typeof t.value!="string"||t.value.trim().length===0)throw _(`identifiers[${r}].value is required and must be a non-empty string`);if(t.value.length>Ee)throw _(`identifiers[${r}].value must not exceed ${Ee} characters`);return {type:t.type,value:t.value}}o($e,"validateIdentifierInput");async function P(e,r,t){let s=j(e,t);if(!s)throw new a("UNAUTHORIZED","Missing or malformed Authorization header");let n=q(s,t);if(t.isTokenRevoked&&await t.isTokenRevoked(n.sessionId))throw new a("UNAUTHORIZED","Token has been revoked");return {id:n.id,roles:n.roles}}o(P,"requireAuth");function Dr(e){let r=e,t=A(r),s=e.logger??S,n=e.loggerService??"sentri",i=e.router?.register??(l=>le(l,r)),c=e.router?.login??(l=>fe(l,r)),h=e.router?.refresh??(l=>B(l,r)),u=e.router?.logout??(l=>l!==void 0?he(l,r):Promise.resolve()),d=e.router?.logoutAll??(l=>me(l,r)),v=e.router?.getUser??(l=>pe(l,r)),D=e.router?.assignRoles??((l,f)=>ge(l,f,r)),C=e.router?.changePassword??((l,f,g)=>we(l,f,g,r)),b=e.router?.bulkCreateIdentifiers??((l,f)=>ye(l,f,r)),k=e.router?.bulkUpdateIdentifiers??((l,f)=>Ie(l,f,r)),H=e.router?.bulkDeleteIdentifiers??((l,f)=>Re(l,f,r)),Z=Tr(e.redisUrl,s),M=new elysia.Elysia().onError(ue());return Br(t.algorithm)&&M.get("/keys",({set:l})=>(l.headers["Cache-Control"]="public, max-age=3600",er(e.secret))),M.post("/register",async({body:l,request:f,set:g})=>{let I=Date.now();qr(f,r);let p=$(l),{identifiers:x,password:w,roles:R}=p;if(!Array.isArray(x)||x.length===0)throw _("identifiers is required and must be a non-empty array");if(x.length>G)throw _(`identifiers must not exceed ${G} entries`);let y=x.map((J,ke)=>$e(J,ke));if(typeof w!="string"||w.length<Ae)throw _(`password is required and must be at least ${Ae} characters`);if(w.length>z)throw _(`password must not exceed ${z} characters`);if(R!==void 0&&!Array.isArray(R))throw _("roles must be an array of strings when provided");if(Array.isArray(R)&&!R.every(J=>typeof J=="string"))throw _("each role must be a string");let T=Array.isArray(R)?R:void 0,E=T!==void 0?{identifiers:y,password:w,roles:T}:{identifiers:y,password:w},L=f.headers.get("x-forwarded-for")||"127.0.0.1",W=f.headers.get("user-agent")||"Unknown";if(e.rateLimit!==false){let J=await Z,ke=typeof e.rateLimit=="object"?e.rateLimit.maxRegisterAttempts??5:5;await J.consume(`register:${L}`,ke,900*1e3);}let U=await i(E,{ip:L,userAgent:W});return U.success?(s.info(m(n,"auth.register.success",{userId:U.user.id,duration_ms:Date.now()-I})),O(g,201,"User registered successfully",{user:U.user})):(s.warn(m(n,"auth.register.failure",{errorCode:U.error.code,duration_ms:Date.now()-I})),g.status=U.error.statusCode,{error:true,statusCode:U.error.statusCode,code:U.error.code,message:U.error.message,data:null})}).post("/login",async({body:l,request:f,set:g})=>{let I=Date.now(),p=$(l),{identifier:x,password:w}=p;if(typeof x!="string"||x.trim().length===0)throw _("identifier is required and must be a non-empty string");if(x.length>Ee)throw _(`identifier must not exceed ${Ee} characters`);if(typeof w!="string"||w.length===0)throw _("password is required");if(w.length>z)throw _(`password must not exceed ${z} characters`);let R=x.trim(),y=f.headers.get("x-forwarded-for")||"127.0.0.1",T=f.headers.get("user-agent")||"Unknown";if(e.rateLimit!==false){let L=await Z,W=typeof e.rateLimit=="object"?e.rateLimit.maxLoginAttempts??5:5;await L.consume(`login:${y}`,W,900*1e3);}let E=await c({identifier:R,password:w},{ip:y,userAgent:T});return E.success?(Ke(()=>r.hooks?.onLoginSuccess?.(E.user,{ip:y,userAgent:T})),ie(g,E.refreshToken,r),ae(g,E.accessToken,r),s.info(m(n,"auth.login.success",{userId:E.user.id,duration_ms:Date.now()-I})),O(g,200,"Login successful",{accessToken:E.accessToken,user:E.user})):(Ke(()=>r.hooks?.onLoginFailed?.(R,E.error,{ip:y})),s.warn(m(n,"auth.login.failure",{errorCode:E.error.code,duration_ms:Date.now()-I})),g.status=E.error.statusCode,{error:true,statusCode:E.error.statusCode,code:E.error.code,message:E.error.message,data:null})}).post("/refresh",async({request:l,set:f})=>{let g=Date.now(),I=K(l.headers.get("cookie")??void 0,F(r));if(!I)throw new a("UNAUTHORIZED","Refresh token cookie is missing");let p=l.headers.get("x-forwarded-for")||"127.0.0.1",x=l.headers.get("user-agent")||"Unknown",w=await h(I,{ip:p,userAgent:x});return w.success?(ie(f,w.refreshToken,r),ae(f,w.accessToken,r),s.info(m(n,"auth.refresh.success",{userId:w.user.id,duration_ms:Date.now()-g})),O(f,200,"Token refreshed",{accessToken:w.accessToken})):(ve(f,r),s.warn(m(n,"auth.refresh.failure",{errorCode:w.error.code,duration_ms:Date.now()-g})),f.status=w.error.statusCode,{error:true,statusCode:w.error.statusCode,code:w.error.code,message:w.error.message,data:null})}).post("/logout",async({request:l,set:f})=>{let g=Date.now(),I=K(l.headers.get("cookie")??void 0,F(r));return await u(I),ve(f,r),Oe(f,r),s.info(m(n,"auth.logout",{duration_ms:Date.now()-g})),O(f,200,"Logged out",null)}).post("/logout-all",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r);return await d(I.id),Ke(()=>r.hooks?.onLogout?.(I.id)),ve(f,r),Oe(f,r),s.info(m(n,"auth.logout_all",{userId:I.id,duration_ms:Date.now()-g})),O(f,200,"All sessions revoked",null)}).get("/me",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r),p=await v(I.id);return p.success?(s.info(m(n,"auth.me.success",{userId:p.user.id,duration_ms:Date.now()-g})),O(f,200,"OK",p.user)):(s.warn(m(n,"auth.me.failure",{userId:I.id,errorCode:p.error.code,duration_ms:Date.now()-g})),f.status=p.error.statusCode,{error:true,statusCode:p.error.statusCode,code:p.error.code,message:p.error.message,data:null})}).get("/me/identifiers",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r),p=await v(I.id);return p.success?(s.info(m(n,"auth.me.identifiers.success",{userId:p.user.id,count:p.user.identifiers?.length??0,duration_ms:Date.now()-g})),O(f,200,"OK",{identifiers:p.user.identifiers??[]})):(s.warn(m(n,"auth.me.identifiers.failure",{userId:I.id,errorCode:p.error.code,duration_ms:Date.now()-g})),f.status=p.error.statusCode,{error:true,statusCode:p.error.statusCode,code:p.error.code,message:p.error.message,data:null})}).post("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{identifiers:w}=x;if(!Array.isArray(w)||w.length===0)throw _("identifiers is required and must be a non-empty array");if(w.length>G)throw _(`identifiers must not exceed ${G} entries`);let R=w.map((T,E)=>$e(T,E)),y=await b(p.id,R);return y.success?(s.info(m(n,"auth.identifiers.created",{userId:p.id,count:y.identifiers.length,duration_ms:Date.now()-I})),O(g,201,"Identifiers added successfully",{identifiers:y.identifiers})):(s.warn(m(n,"auth.identifiers.create_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).put("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{identifiers:w}=x;if(!Array.isArray(w)||w.length===0)throw _("identifiers is required and must be a non-empty array");if(w.length>G)throw _(`identifiers must not exceed ${G} entries`);let R=w.map((T,E)=>{if(typeof T!="object"||T===null||Array.isArray(T))throw _(`identifiers[${E}] must be an object`);let L=T;if(typeof L.id!="string"||L.id.trim().length===0)throw _(`identifiers[${E}].id is required and must be a non-empty string`);let{type:W,value:U}=$e(T,E);return {id:L.id,type:W,value:U}}),y=await k(p.id,R);return y.success?(s.info(m(n,"auth.identifiers.updated",{userId:p.id,count:y.identifiers.length,duration_ms:Date.now()-I})),O(g,200,"Identifiers updated successfully",{identifiers:y.identifiers})):(s.warn(m(n,"auth.identifiers.update_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).delete("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{ids:w}=x;if(!Array.isArray(w)||w.length===0)throw _("ids is required and must be a non-empty array of strings");if(!w.every(y=>typeof y=="string"))throw _("each id must be a string");let R=await H(p.id,w);return R.success?(s.info(m(n,"auth.identifiers.deleted",{userId:p.id,duration_ms:Date.now()-I})),O(g,200,"Identifiers deleted successfully",{identifiers:R.identifiers})):(s.warn(m(n,"auth.identifiers.delete_failure",{userId:p.id,errorCode:R.error.code,duration_ms:Date.now()-I})),g.status=R.error.statusCode,{error:true,statusCode:R.error.statusCode,code:R.error.code,message:R.error.message,data:null})}).patch("/me/password",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{currentPassword:w,newPassword:R}=x;if(typeof w!="string"||w.length===0)throw _("currentPassword is required");if(typeof R!="string"||R.length<Ae)throw _(`newPassword must be at least ${Ae} characters`);if(R.length>z)throw _(`newPassword must not exceed ${z} characters`);if(w===R)throw _("newPassword must be different from currentPassword");let y=await C(p.id,w,R);return y.success?(s.info(m(n,"auth.password.changed",{userId:p.id,duration_ms:Date.now()-I})),O(g,200,"Password updated successfully. All sessions have been revoked.",null)):(s.warn(m(n,"auth.password.change_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).post("/users/:userId/roles",async({body:l,request:f,set:g,params:I})=>{let p=Date.now();if(!(await P(f,g,r)).roles.includes("admin"))throw new a("FORBIDDEN","Requires one of roles: admin");let w=$(l),{roles:R}=w,{userId:y}=I;if(!y)throw _("userId is required");if(!Array.isArray(R)||R.length===0)throw _("roles must be a non-empty array of strings");if(!R.every(E=>typeof E=="string"))throw _("each role must be a string");let T=await D(y,R);return T.success?(s.info(m(n,"auth.roles.assigned",{targetUserId:y,roles:R,duration_ms:Date.now()-p})),O(g,200,"Roles assigned successfully",{user:T.user})):(s.warn(m(n,"auth.roles.assign_failure",{targetUserId:y,errorCode:T.error.code,duration_ms:Date.now()-p})),g.status=T.error.statusCode,{error:true,statusCode:T.error.statusCode,code:T.error.code,message:T.error.message,data:null})}),M}o(Dr,"createAuthRouter");function Fs(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}};Xe(u);let d=u.logger??S,v=u.loggerService??"sentri",D=Ue(d,v),C=Le(d,v);return {protect:o(()=>be(u),"protect"),authorize:o((...b)=>D(...b),"authorize"),permit:o(b=>C(b),"permit"),errorHandler:o(b=>ue(b),"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.validIdentifiers!==void 0&&{validIdentifiers:e.validIdentifiers},...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.rateLimit!==void 0&&{rateLimit:e.rateLimit},...e.redisUrl!==void 0&&{redisUrl:e.redisUrl},...e.logger!==void 0&&{logger:e.logger},...e.loggerService!==void 0&&{loggerService:e.loggerService}},s=t.logger??S,n=t.loggerService??"sentri",i=A(t),c=Ue(s,n),h=Le(s,n);return {protect:o(()=>be(t),"protect"),authorize:o((...u)=>c(...u),"authorize"),permit:o(u=>h(u),"permit"),errorHandler:o(u=>ue(u),"errorHandler"),router:o(()=>Dr(t),"router"),migrate:o(()=>ze(N(t.dialect)),"migrate"),getCurrentAccessToken:o(u=>j(u,t),"getCurrentAccessToken"),hashPassword:o(u=>Y(u,i.saltRounds),"hashPassword"),verifyPassword:o((u,d)=>Q(u,d),"verifyPassword"),signAccessToken:o(u=>re(u,t),"signAccessToken"),signRefreshToken:o(u=>te(u,t),"signRefreshToken"),verifyAccessToken:o(u=>q(u,t),"verifyAccessToken"),verifyRefreshToken:o(u=>se(u,t),"verifyRefreshToken"),register:o(u=>le(u,t),"register"),login:o(u=>fe(u,t),"login"),refresh:o(u=>B(u,t),"refresh"),logout:o(u=>he(u,t),"logout"),logoutAll:o(u=>me(u,t),"logoutAll"),getUser:o(u=>pe(u,t),"getUser"),changePassword:o((u,d,v)=>we(u,d,v,t),"changePassword"),assignRoles:o((u,d)=>ge(u,d,t),"assignRoles"),bulkCreateIdentifiers:o((u,d)=>ye(u,d,t),"bulkCreateIdentifiers"),bulkUpdateIdentifiers:o((u,d)=>Ie(u,d,t),"bulkUpdateIdentifiers"),bulkDeleteIdentifiers:o((u,d)=>Re(u,d,t),"bulkDeleteIdentifiers")}}o(Fs,"createAuthElysia");exports.SENTRI_ERROR_STATUS=He;exports.SentriError=a;exports.authorize=$r;exports.createAuthElysia=Fs;exports.createAuthRouter=Dr;exports.createAuthorize=Ue;exports.createErrorHandler=ue;exports.createPermit=Le;exports.getCurrentAccessToken=j;exports.permit=Mr;exports.protect=be;
@@ -1,8 +1,8 @@
1
+ import { a as AuthConfig, A as AuthUser, S as SentriLogger, b as ServerAuthConfig, C as CookieConfig, c as AccessCookieConfig, d as AuthHooks, R as RouterHandlers, e as RateLimitOptions, f as RegisterInput, g as RegisterResult, L as LoginInput, h as AuthResult, i as RefreshResult, G as GetUserResult, j as ChangePasswordResult, k as AssignRolesResult, I as IdentifierInput, B as BulkIdentifiersResult } from '../../index-CVb3-EnK.cjs';
2
+ export { n as ApiResponse, o as ClientAuthConfig, p as IdentifierRecord, l as SENTRI_ERROR_STATUS, m as SentriError, q as SentriErrorCode } from '../../index-CVb3-EnK.cjs';
1
3
  import * as kysely from 'kysely';
2
4
  import * as elysia from 'elysia';
3
5
  import { Elysia } from 'elysia';
4
- import { AuthConfig, AuthUser, SentriLogger, ServerAuthConfig, CookieConfig, AccessCookieConfig, AuthHooks, RouterHandlers, RegisterInput, RegisterResult, LoginInput, AuthResult, RefreshResult, GetUserResult, ChangePasswordResult, AssignRolesResult, IdentifierInput, BulkIdentifiersResult } from '../../core/index.cjs';
5
- export { ApiResponse, ClientAuthConfig, IdentifierRecord, SENTRI_ERROR_STATUS, SentriError, SentriErrorCode } from '../../core/index.cjs';
6
6
 
7
7
  declare function protect(config: AuthConfig): Elysia<"", {
8
8
  decorator: {};
@@ -88,6 +88,7 @@ interface CreateElysiaServerOptions<TRole extends string = string> {
88
88
  hooks?: AuthHooks;
89
89
  router?: RouterHandlers;
90
90
  isTokenRevoked?: (sessionId: string) => boolean | Promise<boolean>;
91
+ rateLimit?: RateLimitOptions | boolean;
91
92
  redisUrl?: string;
92
93
  logger?: SentriLogger;
93
94
  loggerService?: string;
@@ -1,8 +1,8 @@
1
+ import { a as AuthConfig, A as AuthUser, S as SentriLogger, b as ServerAuthConfig, C as CookieConfig, c as AccessCookieConfig, d as AuthHooks, R as RouterHandlers, e as RateLimitOptions, f as RegisterInput, g as RegisterResult, L as LoginInput, h as AuthResult, i as RefreshResult, G as GetUserResult, j as ChangePasswordResult, k as AssignRolesResult, I as IdentifierInput, B as BulkIdentifiersResult } from '../../index-CVb3-EnK.js';
2
+ export { n as ApiResponse, o as ClientAuthConfig, p as IdentifierRecord, l as SENTRI_ERROR_STATUS, m as SentriError, q as SentriErrorCode } from '../../index-CVb3-EnK.js';
1
3
  import * as kysely from 'kysely';
2
4
  import * as elysia from 'elysia';
3
5
  import { Elysia } from 'elysia';
4
- import { AuthConfig, AuthUser, SentriLogger, ServerAuthConfig, CookieConfig, AccessCookieConfig, AuthHooks, RouterHandlers, RegisterInput, RegisterResult, LoginInput, AuthResult, RefreshResult, GetUserResult, ChangePasswordResult, AssignRolesResult, IdentifierInput, BulkIdentifiersResult } from '../../core/index.js';
5
- export { ApiResponse, ClientAuthConfig, IdentifierRecord, SENTRI_ERROR_STATUS, SentriError, SentriErrorCode } from '../../core/index.js';
6
6
 
7
7
  declare function protect(config: AuthConfig): Elysia<"", {
8
8
  decorator: {};
@@ -88,6 +88,7 @@ interface CreateElysiaServerOptions<TRole extends string = string> {
88
88
  hooks?: AuthHooks;
89
89
  router?: RouterHandlers;
90
90
  isTokenRevoked?: (sessionId: string) => boolean | Promise<boolean>;
91
+ rateLimit?: RateLimitOptions | boolean;
91
92
  redisUrl?: string;
92
93
  logger?: SentriLogger;
93
94
  loggerService?: string;
@@ -1 +1 @@
1
- import {generateKeyPairSync,createPrivateKey,createPublicKey,createHash,randomUUID}from'crypto';import {sql,Kysely}from'kysely';import Ze from'bcrypt';import ee from'jsonwebtoken';import {Elysia}from'elysia';var Cr=Object.defineProperty;var o=(e,r)=>Cr(e,"name",{value:r,configurable:true});var He=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}),a=class extends Error{static{o(this,"SentriError");}code;statusCode;constructor(r,t,s){super(t),this.name="SentriError",this.code=r,this.statusCode=s??He[r]??500;}};var Ve=new WeakMap,Me=32,qe=10,Be=31;function ze(e){if(e.mode==="client"){if(!e.keyUri||e.keyUri.trim().length===0)throw new a("CONFIGURATION_ERROR","keyUri must not be empty");return}if(!e.secret||e.secret.trim().length===0)throw new a("CONFIGURATION_ERROR","secret must not be empty");if((e.algorithm??"HS256").startsWith("HS")&&e.secret.length<Me)throw new a("CONFIGURATION_ERROR",`secret must be at least ${Me} characters for HMAC algorithms`);let s=e.saltRounds??12;if(!Number.isInteger(s)||s<qe||s>Be)throw new a("CONFIGURATION_ERROR",`saltRounds must be an integer between ${qe} and ${Be}`);if(!e.validRoles||e.validRoles.length===0)throw new a("CONFIGURATION_ERROR","validRoles must contain at least one role");if(e.validIdentifiers!==void 0&&e.validIdentifiers.length===0)throw new a("CONFIGURATION_ERROR","validIdentifiers must contain at least one identifier type");if(!e.dialect)throw new a("CONFIGURATION_ERROR","dialect is required in server mode")}o(ze,"validateConfig");function k(e){let r=Ve.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),validIdentifiers:e.validIdentifiers??["email","username"],validIdentifiersSet:new Set(e.validIdentifiers??["email","username"]),cookieName:e.cookie?.name??"refresh_token",accessCookieName:e.accessCookie?.name??"access_token"};return Ve.set(e,t),t}o(k,"resolveServerConfig");var Dr=/^(\d+)([smhdw])$/,Sr={s:1e3,m:6e4,h:36e5,d:864e5,w:6048e5},je=new Map;function M(e){if(typeof e=="number")return e*1e3;let r=je.get(e);if(r!==void 0)return r;let t=Dr.exec(e);if(!t?.[1]||!t?.[2])throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let s=Sr[t[2]];if(s===void 0)throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let n=parseInt(t[1],10)*s;return je.set(e,n),n}o(M,"parseExpiry");var S={info:o(()=>{},"info"),warn:o(()=>{},"warn"),error:o(()=>{},"error")};function m(e,r,t){return {service:e,event:r,...t}}o(m,"buildLogData");async function Xe(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();}o(Xe,"runMigrations");var Ge=new Map;function D(e){let r=Ge.get(e);return r||(r=new Kysely({dialect:e}),Ge.set(e,r)),r}o(D,"getDatabase");async function Y(e,r=12){return Ze.hash(e,r)}o(Y,"hashPassword");async function Q(e,r){return Ze.compare(e,r)}o(Q,"verifyPassword");var We=new Map,Je=new Map,Lr=3600*1e3;function Qe(e){let r=We.get(e);if(!r){let t=createPrivateKey(e),s=createPublicKey(t),n=s.export({format:"jwk"}),c=createHash("sha256").update(JSON.stringify({e:n.e,kty:n.kty,n:n.n})).digest("base64url"),h={...n,use:"sig",kid:c};r={kid:c,publicKey:s,jwk:h},We.set(e,r);}return r}o(Qe,"getOrBuildKey");function er(e){let{jwk:r}=Qe(e);return {keys:[r]}}o(er,"buildJwks");function rr(e){return Qe(e).publicKey}o(rr,"getPublicKeyFromPrivate");async function tr(e){let r=Date.now(),t=Je.get(e);if(t&&r-t.fetchedAt<Lr)return t.publicKey;let s=await fetch(e);if(!s.ok)throw new a("CONFIGURATION_ERROR",`Failed to fetch public key from ${e}: HTTP ${s.status}`);let n=await s.json();if(!n.keys||n.keys.length===0)throw new a("CONFIGURATION_ERROR",`No keys found in JWKS response from ${e}`);let i=n.keys[0],c=createPublicKey({key:i,format:"jwk"});return Je.set(e,{publicKey:c,fetchedAt:r}),c}o(tr,"fetchPublicKey");var sr=new Map,or=new Map,nr=new Map;function ir(e){return e.startsWith("RS")||e.startsWith("PS")}o(ir,"isRSA");function ar(e){let r=sr.get(e);return r||(r={access:`${e}:access`,refresh:`${e}:refresh`},sr.set(e,r)),r}o(ar,"getHsSecrets");function Pr(e){let r=or.get(e);return r||(r=createPrivateKey(e),or.set(e,r)),r}o(Pr,"getRsPrivateKey");function ur(e){let r=k(e);if(ir(r.algorithm)){let n=Pr(e.secret);return {accessKey:n,refreshKey:n}}let{access:t,refresh:s}=ar(e.secret);return {accessKey:t,refreshKey:s}}o(ur,"getSigningKeys");function dr(e,r){let t=k(e);if(ir(t.algorithm))return rr(e.secret);let{access:s,refresh:n}=ar(e.secret);return r==="access"?s:n}o(dr,"getVerifyKey");function cr(e,r,t,s){let n=`${t}:${s}`,i=nr.get(n);return i||(i={expiresIn:t,algorithm:s},nr.set(n,i)),ee.sign(e,r,i)}o(cr,"sign");function lr(e,r,t){try{let s=ee.verify(e,r,{algorithms:[t]});if(typeof s=="string"||s===null)throw new a("TOKEN_INVALID","Token payload is not an object");return s}catch(s){throw s instanceof a?s:s instanceof ee.TokenExpiredError?new a("TOKEN_EXPIRED","Token has expired"):new a("TOKEN_INVALID","Token is invalid or malformed")}}o(lr,"verify");function re(e,r){let t=k(r),{accessKey:s}=ur(r);return cr(e,s,t.accessExpiresIn,t.algorithm)}o(re,"signAccessToken");function te(e,r){let t=k(r),{refreshKey:s}=ur(r);return cr({sessionId:e},s,t.refreshExpiresIn,t.algorithm)}o(te,"signRefreshToken");function q(e,r){let t=k(r),s=dr(r,"access");return lr(e,s,t.algorithm)}o(q,"verifyAccessToken");function se(e,r){let t=k(r),s=dr(r,"refresh");return lr(e,s,t.algorithm)}o(se,"verifyRefreshToken");function fr(e,r){try{let t=ee.verify(e,r,{algorithms:["RS256","RS384","RS512","PS256","PS384","PS512"]});if(typeof t=="string"||t===null)throw new a("TOKEN_INVALID","Token payload is not an object");return t}catch(t){throw t instanceof a?t:t instanceof ee.TokenExpiredError?new a("TOKEN_EXPIRED","Token has expired"):new a("TOKEN_INVALID","Token is invalid or malformed")}}o(fr,"verifyTokenWithPublicKey");function xe(e){try{return JSON.parse(e)}catch{return []}}o(xe,"parseRoles");function Kr(e){return JSON.stringify(e)}o(Kr,"serializeRoles");function mr(e){return {id:e.id,userId:e.user_id,type:e.type,value:e.value,createdAt:new Date(e.created_at)}}o(mr,"mapIdentifier");async function oe(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:xe(t.roles)}:null}o(oe,"findUserByIdentifierValue");async function de(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:xe(t.roles)}:null}o(de,"findUserById");async function pr(e,r,t){let s=t.map(n=>({id:randomUUID(),user_id:r,type:n.type,value:n.value}));return await e.insertInto("sentri_identifiers").values(s).execute(),s.map(n=>({id:n.id,userId:n.user_id,type:n.type,value:n.value,createdAt:new Date}))}o(pr,"createIdentifiers");async function ne(e,r){return (await e.selectFrom("sentri_identifiers").selectAll().where("user_id","=",r).orderBy("created_at","asc").execute()).map(mr)}o(ne,"findIdentifiersByUserId");async function Ne(e,r,t){let s=await e.selectFrom("sentri_identifiers").selectAll().where("id","=",r).where("user_id","=",t).executeTakeFirst();return s?mr(s):null}o(Ne,"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)}o(wr,"countIdentifiersByUserId");async function gr(e,r,t,s){await e.updateTable("sentri_identifiers").set({type:s.type,value:s.value}).where("id","=",r).where("user_id","=",t).execute();}o(gr,"updateIdentifier");async function yr(e,r,t){await e.deleteFrom("sentri_identifiers").where("user_id","=",r).where("id","in",t).execute();}o(yr,"deleteIdentifiers");async function Ir(e,r,t){await e.updateTable("sentri_users").set({password_hash:t}).where("id","=",r).execute();}o(Ir,"updateUserPassword");async function Rr(e,r,t){await e.updateTable("sentri_users").set({roles:Kr(t)}).where("id","=",r).execute();}o(Rr,"updateUserRoles");async function Ce(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}}o(Ce,"createSession");async function De(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:xe(t.roles)}}:null}o(De,"findSessionById");async function Se(e,r){await e.deleteFrom("sentri_sessions").where("id","=",r).execute();}o(Se,"deleteSession");async function _r(e,r,t){await e.updateTable("sentri_sessions").set({replaced_by:t}).where("id","=",r).execute();}o(_r,"markSessionReplaced");async function ce(e,r){await e.deleteFrom("sentri_sessions").where("user_id","=",r).execute();}o(ce,"deleteAllSessionsForUser");async function le(e,r,t){let s=k(r),n=D(r.dialect),i=e.roles??[],c=i.filter(E=>!s.validRolesSet.has(E));if(c.length>0)return {success:false,error:new a("INVALID_ROLE",`Invalid roles: ${c.join(", ")}`)};if(!e.identifiers||e.identifiers.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one identifier is required")};let h=e.identifiers.map(E=>({type:E.type.trim(),value:E.value.trim()})),u=h.filter(E=>!s.validIdentifiersSet.has(E.type));if(u.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${u.map(E=>E.type).join(", ")}`)};if(new Set(h.map(E=>E.value)).size!==h.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let E of h)if(await oe(n,E.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${E.value}`)};let v=await Y(e.password,s.saltRounds),{userId:N,identifierRows:C}=await n.transaction().execute(async E=>{let H=randomUUID();await E.insertInto("sentri_users").values({id:H,password_hash:v,roles:JSON.stringify(i)}).execute();let Z=h.map(V=>({id:randomUUID(),user_id:H,type:V.type,value:V.value}));return await E.insertInto("sentri_identifiers").values(Z).execute(),{userId:H,identifierRows:Z}}),b={success:true,user:{id:N,roles:i,identifiers:C.map(E=>({id:E.id,type:E.type,value:E.value}))}};return r.hooks?.onRegister&&await r.hooks.onRegister(b.user),b}o(le,"register");async function fe(e,r,t){let s=k(r),n=D(r.dialect),i=await oe(n,e.identifier.trim());if(!i){let C=new a("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,C,{ip:t?.ip??""}),{success:false,error:C}}if(!await Q(e.password,i.passwordHash)){let C=new a("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,C,{ip:t?.ip??""}),{success:false,error:C}}let h=new Date(Date.now()+M(s.refreshExpiresIn)),u=await Ce(n,{userId:i.id,expiresAt:h,ipAddress:t?.ip??null,userAgent:t?.userAgent??null}),d={id:i.id,roles:i.roles},v=re({id:i.id,roles:i.roles,sessionId:u.id},r),N=te(u.id,r);return r.hooks?.onLoginSuccess&&await r.hooks.onLoginSuccess(d,{ip:t?.ip??"",userAgent:t?.userAgent??""}),{success:true,accessToken:v,refreshToken:N,user:d}}o(fe,"login");async function B(e,r,t){let s=k(r),n=D(r.dialect),i;try{({sessionId:i}=se(e,r));}catch(C){return C instanceof a?{success:false,error:C}:{success:false,error:new a("TOKEN_INVALID","Invalid refresh token")}}let c=await De(n,i);if(!c)return {success:false,error:new a("UNAUTHORIZED","Session not found or revoked")};if(c.replacedBy)return await ce(n,c.userId),{success:false,error:new a("TOKEN_REUSE","Token reuse detected. All sessions revoked.")};if(c.expiresAt.getTime()<Date.now())return await Se(n,i),{success:false,error:new a("TOKEN_EXPIRED","Session has expired")};let h=new Date(Date.now()+M(s.refreshExpiresIn)),u=await Ce(n,{userId:c.userId,expiresAt:h,ipAddress:t?.ip??null,userAgent:t?.userAgent??null});await _r(n,i,u.id);let d={id:c.user.id,roles:c.user.roles},v=re({...d,sessionId:u.id},r),N=te(u.id,r);return {success:true,accessToken:v,refreshToken:N,user:d}}o(B,"refresh");async function he(e,r){let t=D(r.dialect),s;try{({sessionId:s}=se(e,r));}catch{return}let n=await De(t,s);n&&(await Se(t,s),r.hooks?.onLogout&&await r.hooks.onLogout(n.userId));}o(he,"logout");async function me(e,r){let t=D(r.dialect);await ce(t,e),r.hooks?.onLogout&&await r.hooks.onLogout(e);}o(me,"logoutAll");async function pe(e,r){let t=D(r.dialect),s=await de(t,e);if(!s)return {success:false,error:new a("USER_NOT_FOUND","User not found")};let n=await ne(t,e);return {success:true,user:{id:s.id,roles:s.roles,identifiers:n.map(i=>({id:i.id,type:i.type,value:i.value}))}}}o(pe,"getUser");async function we(e,r,t,s){let n=k(s),i=D(s.dialect),c=await de(i,e);if(!c)return {success:false,error:new a("USER_NOT_FOUND","User not found")};if(!await Q(r,c.passwordHash))return {success:false,error:new a("INVALID_CREDENTIALS","Invalid credentials")};let u=await Y(t,n.saltRounds);return await Ir(i,e,u),await ce(i,e),s.hooks?.onPasswordChanged&&await s.hooks.onPasswordChanged(e),{success:true}}o(we,"changePassword");async function ge(e,r,t){let s=k(t),n=D(t.dialect),i=r.filter(d=>!s.validRolesSet.has(d));if(i.length>0)return {success:false,error:new a("INVALID_ROLE",`Invalid roles: ${i.join(", ")}`)};let c=await de(n,e);if(!c)return {success:false,error:new a("USER_NOT_FOUND","User not found")};let h=new Set(c.roles);for(let d of r)h.add(d);let u=Array.from(h);return await Rr(n,e,u),{success:true,user:{id:c.id,roles:u}}}o(ge,"assignRoles");async function ye(e,r,t){let s=k(t),n=D(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one identifier is required")};let i=r.map(d=>({type:d.type.trim(),value:d.value.trim()})),c=i.filter(d=>!s.validIdentifiersSet.has(d.type));if(c.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${c.map(d=>d.type).join(", ")}`)};if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i)if(await oe(n,d.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)};return await pr(n,e,i),{success:true,identifiers:(await ne(n,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}o(ye,"bulkCreateIdentifiers");async function Ie(e,r,t){let s=k(t),n=D(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one update is required")};let i=r.map(d=>({id:d.id,type:d.type.trim(),value:d.value.trim()})),c=i.filter(d=>!s.validIdentifiersSet.has(d.type));if(c.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${c.map(d=>d.type).join(", ")}`)};if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i){let v=await Ne(n,d.id,e);if(!v)return {success:false,error:new a("IDENTIFIER_NOT_FOUND",`Identifier not found: ${d.id}`)};if(v.value!==d.value&&await oe(n,d.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)}}return await n.transaction().execute(async d=>{for(let v of i)await gr(d,v.id,e,{type:v.type,value:v.value});}),{success:true,identifiers:(await ne(n,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}o(Ie,"bulkUpdateIdentifiers");async function Re(e,r,t){let s=D(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one ID is required")};let n=Array.from(new Set(r));for(let h of n)if(!await Ne(s,h,e))return {success:false,error:new a("IDENTIFIER_NOT_FOUND",`Identifier not found: ${h}`)};return await wr(s,e)-n.length<1?{success:false,error:new a("VALIDATION_ERROR","Cannot delete all identifiers \u2014 at least one must remain")}:(await yr(s,e,n),{success:true,identifiers:(await ne(s,e)).map(h=>({id:h.id,type:h.type,value:h.value}))})}o(Re,"bulkDeleteIdentifiers");function F(e){return k(e).cookieName}o(F,"getCookieName");function _e(e){return k(e).accessCookieName}o(_e,"getAccessCookieName");function K(e,r){if(!e)return;let t=`${r}=`,s=0;for(;s<e.length;){for(;s<e.length&&e[s]===" ";)s++;let n=e.indexOf(";",s),i=n===-1?e.length:n;if(e.startsWith(t,s))return e.slice(s+t.length,i);s=i+1;}}o(K,"readCookie");function ie(e,r,t){let s=t.cookie??{},n=M(k(t).refreshExpiresIn)/1e3;e.cookie??={},e.cookie[F(t)]={value:r,httpOnly:s.httpOnly??true,secure:s.secure??false,sameSite:s.sameSite??"strict",path:s.path??"/",maxAge:n};}o(ie,"setCookieFromConfig");function ve(e,r){let t=r.cookie??{};e.cookie??={},e.cookie[F(r)]={value:"",httpOnly:t.httpOnly??true,path:t.path??"/",maxAge:0};}o(ve,"clearCookieFromConfig");function ae(e,r,t){if(!t.accessCookie)return;let s=t.accessCookie,n=M(k(t).accessExpiresIn)/1e3;e.cookie??={},e.cookie[_e(t)]={value:r,httpOnly:false,secure:s.secure??false,sameSite:s.sameSite??"strict",path:s.path??"/",maxAge:n};}o(ae,"setAccessCookieFromConfig");function Oe(e,r){if(!r.accessCookie)return;let t=r.accessCookie;e.cookie??={},e.cookie[_e(r)]={value:"",httpOnly:false,path:t.path??"/",maxAge:0};}o(Oe,"clearAccessCookieFromConfig");function j(e,r){let t=e.headers.get("authorization");if(t?.startsWith("Bearer "))return t.slice(7);let s=e.headers.get("cookie");return s?K(s,_e(r)):void 0}o(j,"getCurrentAccessToken");function be(e){let r=e.logger??S,t=e.loggerService??"sentri";if(e.mode==="client"){let n=e;return new Elysia().derive({as:"global"},async({request:i})=>{let c=i.headers.get("authorization"),h=c?.startsWith("Bearer ")?c.slice(7):void 0;if(!h)throw r.warn(m(t,"auth.protect.failure",{mode:"client",errorCode:"UNAUTHORIZED"})),new a("UNAUTHORIZED","Missing or malformed Authorization header");try{let u=await tr(n.keyUri),d=fr(h,u),v={id:d.id,roles:d.roles};return r.info(m(t,"auth.protect.success",{mode:"client",userId:d.id})),{user:v}}catch(u){let d=u instanceof a?u.code:"TOKEN_INVALID";throw r.warn(m(t,"auth.protect.failure",{mode:"client",errorCode:d})),u}})}let s=e;return new Elysia().derive({as:"global"},async({request:n,set:i})=>{let c=j(n,s);if(!c)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"UNAUTHORIZED"})),new a("UNAUTHORIZED","Missing or malformed Authorization header");try{let h=q(c,s);if(s.isTokenRevoked&&await s.isTokenRevoked(h.sessionId))throw r.warn(m(t,"auth.protect.token_revoked",{mode:"server",userId:h.id})),new a("UNAUTHORIZED","Token has been revoked");let u={id:h.id,roles:h.roles};return r.info(m(t,"auth.protect.success",{mode:"server",userId:h.id})),{user:u}}catch(h){if(h instanceof a&&h.code==="TOKEN_EXPIRED"){let d=n.headers.get("cookie"),v=d?K(d,F(s)):void 0;if(!v)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED"})),new a("UNAUTHORIZED","Token expired. Please login again.");let N;try{N=await B(v,s);}catch{throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED"})),new a("UNAUTHORIZED","Session expired. Please login again.")}if(!N.success)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:N.error.code})),new a("UNAUTHORIZED","Session expired. Please login again.");return ie(i,N.refreshToken,s),ae(i,N.accessToken,s),i.headers["X-New-Access-Token"]=N.accessToken,r.info(m(t,"auth.protect.auto_refresh",{mode:"server",userId:N.user.id})),{user:N.user}}let u=h instanceof a?h.code:"TOKEN_INVALID";throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:u})),h}})}o(be,"protect");function Ar(e,r,t){let s=`Requires one of roles: ${e.join(", ")}`;return ({user:n})=>{if(!n)throw r.warn(m(t,"auth.authorize.unauthenticated",{requiredRoles:e})),new a("UNAUTHORIZED","Not authenticated");let i=n.roles;if(!e.some(c=>i.includes(c)))throw r.warn(m(t,"auth.authorize.denied",{userId:n.id,userRoles:[...i],requiredRoles:e})),new a("FORBIDDEN",s);r.info(m(t,"auth.authorize.passed",{userId:n.id,userRoles:[...i],requiredRoles:e}));}}o(Ar,"createAuthorizeHandler");function Ue(e,r){return o(function(...s){return Ar(s,e,r)},"authorize")}o(Ue,"createAuthorize");function $r(...e){return Ar(e,S,"sentri")}o($r,"authorize");var Hr=new a("FORBIDDEN","You do not have permission to perform this action");function Er(e,r,t){return async s=>{let n=s.user;if(!n)throw r.warn(m(t,"auth.permit.unauthenticated",{})),new a("UNAUTHORIZED","Not authenticated");if(e.roles&&e.roles.length>0){let i=n.roles;if(e.roles.some(c=>i.includes(c))){r.info(m(t,"auth.permit.role_bypass",{userId:n.id,bypassedByRole:true}));return}}try{let i=e.check(s);if(i instanceof Promise?await i:i)r.info(m(t,"auth.permit.passed",{userId:n.id}));else throw r.warn(m(t,"auth.permit.denied",{userId:n.id})),Hr}catch(i){throw i}}}o(Er,"createPermitHandler");function Le(e,r){return o(function(s){return Er(typeof s=="function"?{check:s}:s,e,r)},"permit")}o(Le,"createPermit");function Vr(e){return Er(typeof e=="function"?{check:e}:e,S,"sentri")}o(Vr,"permit");function ue(e){return ({error:r,set:t})=>r instanceof a?(t.status=r.statusCode,{error:true,statusCode:r.statusCode,code:r.code,message:r.message,data:null}):(e?.onUnhandled?.(r),t.status=500,{error:true,statusCode:500,code:"INTERNAL_SERVER_ERROR",message:"Internal server error",data:null})}o(ue,"createErrorHandler");var Fe=class{static{o(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 n=Date.now(),i=this.store.get(r);if((!i||i.expiresAt<n)&&(i={count:0,expiresAt:n+s},this.store.set(r,i)),i.count>t){let c=Math.ceil((i.expiresAt-n)/1e3);throw new Error(`Rate limit exceeded. Try again in ${c} seconds.`)}i.count++;}},Pe=class{static{o(this,"RedisRateLimiter");}redis;logger;constructor(r,t){this.redis=r,this.logger=t;}async consume(r,t,s){let n=`sentri:rl:${r}`,i=Math.ceil(s/1e3);try{let c=await this.redis.multi().incr(n).expire(n,i,"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});}}},z=null;async function Tr(e,r){if(z)return z;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"}),z=new Pe(s,r??S),z}catch(t){r?.warn({msg:"Failed to connect to Redis for Rate Limiting, falling back to InMemoryRateLimiter",error:t});}return z=new Fe,z}o(Tr,"getRateLimiter");var ke=8,X=72,Ae=255,xr=100,G=50;function _(e){return new a("VALIDATION_ERROR",e)}o(_,"badRequest");function O(e,r,t,s){return e.status=r,{error:false,statusCode:r,message:t,data:s}}o(O,"ok");function $(e){if(e==null||typeof e!="object"||Array.isArray(e))throw new a("VALIDATION_ERROR","Request body is missing or not a JSON object. Did you set Content-Type: application/json?");return e}o($,"parseBody");function qr(e,r){if(!r.apiKey)return;let t=e.headers.get("x-api-key");if(typeof t!="string"||t!==r.apiKey)throw new a("UNAUTHORIZED","Invalid or missing API key")}o(qr,"validateApiKey");function Ke(e){if(e)try{let r=e();r instanceof Promise&&r.catch(()=>{});}catch{}}o(Ke,"fireHook");function Br(e){return e.startsWith("RS")||e.startsWith("PS")}o(Br,"isRSA");function $e(e,r){if(typeof e!="object"||e===null||Array.isArray(e))throw _(`identifiers[${r}] must be an object`);let t=e;if(typeof t.type!="string"||t.type.trim().length===0)throw _(`identifiers[${r}].type is required and must be a non-empty string`);if(t.type.length>xr)throw _(`identifiers[${r}].type must not exceed ${xr} characters`);if(typeof t.value!="string"||t.value.trim().length===0)throw _(`identifiers[${r}].value is required and must be a non-empty string`);if(t.value.length>Ae)throw _(`identifiers[${r}].value must not exceed ${Ae} characters`);return {type:t.type,value:t.value}}o($e,"validateIdentifierInput");async function P(e,r,t){let s=j(e,t);if(!s)throw new a("UNAUTHORIZED","Missing or malformed Authorization header");let n=q(s,t);if(t.isTokenRevoked&&await t.isTokenRevoked(n.sessionId))throw new a("UNAUTHORIZED","Token has been revoked");return {id:n.id,roles:n.roles}}o(P,"requireAuth");function Nr(e){let r=e,t=k(r),s=e.logger??S,n=e.loggerService??"sentri",i=e.router?.register??(l=>le(l,r)),c=e.router?.login??(l=>fe(l,r)),h=e.router?.refresh??(l=>B(l,r)),u=e.router?.logout??(l=>l!==void 0?he(l,r):Promise.resolve()),d=e.router?.logoutAll??(l=>me(l,r)),v=e.router?.getUser??(l=>pe(l,r)),N=e.router?.assignRoles??((l,f)=>ge(l,f,r)),C=e.router?.changePassword??((l,f,g)=>we(l,f,g,r)),b=e.router?.bulkCreateIdentifiers??((l,f)=>ye(l,f,r)),E=e.router?.bulkUpdateIdentifiers??((l,f)=>Ie(l,f,r)),H=e.router?.bulkDeleteIdentifiers??((l,f)=>Re(l,f,r)),Z=Tr(e.redisUrl,s),V=new Elysia().onError(ue());return Br(t.algorithm)&&V.get("/keys",({set:l})=>(l.headers["Cache-Control"]="public, max-age=3600",er(e.secret))),V.post("/register",async({body:l,request:f,set:g})=>{let I=Date.now();qr(f,r);let p=$(l),{identifiers:x,password:w,roles:R}=p;if(!Array.isArray(x)||x.length===0)throw _("identifiers is required and must be a non-empty array");if(x.length>G)throw _(`identifiers must not exceed ${G} entries`);let y=x.map((J,Ee)=>$e(J,Ee));if(typeof w!="string"||w.length<ke)throw _(`password is required and must be at least ${ke} characters`);if(w.length>X)throw _(`password must not exceed ${X} characters`);if(R!==void 0&&!Array.isArray(R))throw _("roles must be an array of strings when provided");if(Array.isArray(R)&&!R.every(J=>typeof J=="string"))throw _("each role must be a string");let T=Array.isArray(R)?R:void 0,A=T!==void 0?{identifiers:y,password:w,roles:T}:{identifiers:y,password:w},L=f.headers.get("x-forwarded-for")||"127.0.0.1",W=f.headers.get("user-agent")||"Unknown";if(e.rateLimit!==false){let J=await Z,Ee=typeof e.rateLimit=="object"?e.rateLimit.maxRegisterAttempts??5:5;await J.consume(`register:${L}`,Ee,900*1e3);}let U=await i(A,{ip:L,userAgent:W});return U.success?(s.info(m(n,"auth.register.success",{userId:U.user.id,duration_ms:Date.now()-I})),O(g,201,"User registered successfully",{user:U.user})):(s.warn(m(n,"auth.register.failure",{errorCode:U.error.code,duration_ms:Date.now()-I})),g.status=U.error.statusCode,{error:true,statusCode:U.error.statusCode,code:U.error.code,message:U.error.message,data:null})}).post("/login",async({body:l,request:f,set:g})=>{let I=Date.now(),p=$(l),{identifier:x,password:w}=p;if(typeof x!="string"||x.trim().length===0)throw _("identifier is required and must be a non-empty string");if(x.length>Ae)throw _(`identifier must not exceed ${Ae} characters`);if(typeof w!="string"||w.length===0)throw _("password is required");if(w.length>X)throw _(`password must not exceed ${X} characters`);let R=x.trim(),y=f.headers.get("x-forwarded-for")||"127.0.0.1",T=f.headers.get("user-agent")||"Unknown";if(e.rateLimit!==false){let L=await Z,W=typeof e.rateLimit=="object"?e.rateLimit.maxLoginAttempts??5:5;await L.consume(`login:${y}`,W,900*1e3);}let A=await c({identifier:R,password:w},{ip:y,userAgent:T});return A.success?(Ke(()=>r.hooks?.onLoginSuccess?.(A.user,{ip:y,userAgent:T})),ie(g,A.refreshToken,r),ae(g,A.accessToken,r),s.info(m(n,"auth.login.success",{userId:A.user.id,duration_ms:Date.now()-I})),O(g,200,"Login successful",{accessToken:A.accessToken,user:A.user})):(Ke(()=>r.hooks?.onLoginFailed?.(R,A.error,{ip:y})),s.warn(m(n,"auth.login.failure",{errorCode:A.error.code,duration_ms:Date.now()-I})),g.status=A.error.statusCode,{error:true,statusCode:A.error.statusCode,code:A.error.code,message:A.error.message,data:null})}).post("/refresh",async({request:l,set:f})=>{let g=Date.now(),I=K(l.headers.get("cookie")??void 0,F(r));if(!I)throw new a("UNAUTHORIZED","Refresh token cookie is missing");let p=l.headers.get("x-forwarded-for")||"127.0.0.1",x=l.headers.get("user-agent")||"Unknown",w=await h(I,{ip:p,userAgent:x});return w.success?(ie(f,w.refreshToken,r),ae(f,w.accessToken,r),s.info(m(n,"auth.refresh.success",{userId:w.user.id,duration_ms:Date.now()-g})),O(f,200,"Token refreshed",{accessToken:w.accessToken})):(ve(f,r),s.warn(m(n,"auth.refresh.failure",{errorCode:w.error.code,duration_ms:Date.now()-g})),f.status=w.error.statusCode,{error:true,statusCode:w.error.statusCode,code:w.error.code,message:w.error.message,data:null})}).post("/logout",async({request:l,set:f})=>{let g=Date.now(),I=K(l.headers.get("cookie")??void 0,F(r));return await u(I),ve(f,r),Oe(f,r),s.info(m(n,"auth.logout",{duration_ms:Date.now()-g})),O(f,200,"Logged out",null)}).post("/logout-all",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r);return await d(I.id),Ke(()=>r.hooks?.onLogout?.(I.id)),ve(f,r),Oe(f,r),s.info(m(n,"auth.logout_all",{userId:I.id,duration_ms:Date.now()-g})),O(f,200,"All sessions revoked",null)}).get("/me",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r),p=await v(I.id);return p.success?(s.info(m(n,"auth.me.success",{userId:p.user.id,duration_ms:Date.now()-g})),O(f,200,"OK",p.user)):(s.warn(m(n,"auth.me.failure",{userId:I.id,errorCode:p.error.code,duration_ms:Date.now()-g})),f.status=p.error.statusCode,{error:true,statusCode:p.error.statusCode,code:p.error.code,message:p.error.message,data:null})}).get("/me/identifiers",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r),p=await v(I.id);return p.success?(s.info(m(n,"auth.me.identifiers.success",{userId:p.user.id,count:p.user.identifiers?.length??0,duration_ms:Date.now()-g})),O(f,200,"OK",{identifiers:p.user.identifiers??[]})):(s.warn(m(n,"auth.me.identifiers.failure",{userId:I.id,errorCode:p.error.code,duration_ms:Date.now()-g})),f.status=p.error.statusCode,{error:true,statusCode:p.error.statusCode,code:p.error.code,message:p.error.message,data:null})}).post("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{identifiers:w}=x;if(!Array.isArray(w)||w.length===0)throw _("identifiers is required and must be a non-empty array");if(w.length>G)throw _(`identifiers must not exceed ${G} entries`);let R=w.map((T,A)=>$e(T,A)),y=await b(p.id,R);return y.success?(s.info(m(n,"auth.identifiers.created",{userId:p.id,count:y.identifiers.length,duration_ms:Date.now()-I})),O(g,201,"Identifiers added successfully",{identifiers:y.identifiers})):(s.warn(m(n,"auth.identifiers.create_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).put("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{identifiers:w}=x;if(!Array.isArray(w)||w.length===0)throw _("identifiers is required and must be a non-empty array");if(w.length>G)throw _(`identifiers must not exceed ${G} entries`);let R=w.map((T,A)=>{if(typeof T!="object"||T===null||Array.isArray(T))throw _(`identifiers[${A}] must be an object`);let L=T;if(typeof L.id!="string"||L.id.trim().length===0)throw _(`identifiers[${A}].id is required and must be a non-empty string`);let{type:W,value:U}=$e(T,A);return {id:L.id,type:W,value:U}}),y=await E(p.id,R);return y.success?(s.info(m(n,"auth.identifiers.updated",{userId:p.id,count:y.identifiers.length,duration_ms:Date.now()-I})),O(g,200,"Identifiers updated successfully",{identifiers:y.identifiers})):(s.warn(m(n,"auth.identifiers.update_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).delete("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{ids:w}=x;if(!Array.isArray(w)||w.length===0)throw _("ids is required and must be a non-empty array of strings");if(!w.every(y=>typeof y=="string"))throw _("each id must be a string");let R=await H(p.id,w);return R.success?(s.info(m(n,"auth.identifiers.deleted",{userId:p.id,duration_ms:Date.now()-I})),O(g,200,"Identifiers deleted successfully",{identifiers:R.identifiers})):(s.warn(m(n,"auth.identifiers.delete_failure",{userId:p.id,errorCode:R.error.code,duration_ms:Date.now()-I})),g.status=R.error.statusCode,{error:true,statusCode:R.error.statusCode,code:R.error.code,message:R.error.message,data:null})}).patch("/me/password",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{currentPassword:w,newPassword:R}=x;if(typeof w!="string"||w.length===0)throw _("currentPassword is required");if(typeof R!="string"||R.length<ke)throw _(`newPassword must be at least ${ke} characters`);if(R.length>X)throw _(`newPassword must not exceed ${X} characters`);if(w===R)throw _("newPassword must be different from currentPassword");let y=await C(p.id,w,R);return y.success?(s.info(m(n,"auth.password.changed",{userId:p.id,duration_ms:Date.now()-I})),O(g,200,"Password updated successfully. All sessions have been revoked.",null)):(s.warn(m(n,"auth.password.change_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).post("/users/:userId/roles",async({body:l,request:f,set:g,params:I})=>{let p=Date.now();if(!(await P(f,g,r)).roles.includes("admin"))throw new a("FORBIDDEN","Requires one of roles: admin");let w=$(l),{roles:R}=w,{userId:y}=I;if(!y)throw _("userId is required");if(!Array.isArray(R)||R.length===0)throw _("roles must be a non-empty array of strings");if(!R.every(A=>typeof A=="string"))throw _("each role must be a string");let T=await N(y,R);return T.success?(s.info(m(n,"auth.roles.assigned",{targetUserId:y,roles:R,duration_ms:Date.now()-p})),O(g,200,"Roles assigned successfully",{user:T.user})):(s.warn(m(n,"auth.roles.assign_failure",{targetUserId:y,errorCode:T.error.code,duration_ms:Date.now()-p})),g.status=T.error.statusCode,{error:true,statusCode:T.error.statusCode,code:T.error.code,message:T.error.message,data:null})}),V}o(Nr,"createAuthRouter");function Ls(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}};ze(u);let d=u.logger??S,v=u.loggerService??"sentri",N=Ue(d,v),C=Le(d,v);return {protect:o(()=>be(u),"protect"),authorize:o((...b)=>N(...b),"authorize"),permit:o(b=>C(b),"permit"),errorHandler:o(b=>ue(b),"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.validIdentifiers!==void 0&&{validIdentifiers:e.validIdentifiers},...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??S,n=t.loggerService??"sentri",i=k(t),c=Ue(s,n),h=Le(s,n);return {protect:o(()=>be(t),"protect"),authorize:o((...u)=>c(...u),"authorize"),permit:o(u=>h(u),"permit"),errorHandler:o(u=>ue(u),"errorHandler"),router:o(()=>Nr(t),"router"),migrate:o(()=>Xe(D(t.dialect)),"migrate"),getCurrentAccessToken:o(u=>j(u,t),"getCurrentAccessToken"),hashPassword:o(u=>Y(u,i.saltRounds),"hashPassword"),verifyPassword:o((u,d)=>Q(u,d),"verifyPassword"),signAccessToken:o(u=>re(u,t),"signAccessToken"),signRefreshToken:o(u=>te(u,t),"signRefreshToken"),verifyAccessToken:o(u=>q(u,t),"verifyAccessToken"),verifyRefreshToken:o(u=>se(u,t),"verifyRefreshToken"),register:o(u=>le(u,t),"register"),login:o(u=>fe(u,t),"login"),refresh:o(u=>B(u,t),"refresh"),logout:o(u=>he(u,t),"logout"),logoutAll:o(u=>me(u,t),"logoutAll"),getUser:o(u=>pe(u,t),"getUser"),changePassword:o((u,d,v)=>we(u,d,v,t),"changePassword"),assignRoles:o((u,d)=>ge(u,d,t),"assignRoles"),bulkCreateIdentifiers:o((u,d)=>ye(u,d,t),"bulkCreateIdentifiers"),bulkUpdateIdentifiers:o((u,d)=>Ie(u,d,t),"bulkUpdateIdentifiers"),bulkDeleteIdentifiers:o((u,d)=>Re(u,d,t),"bulkDeleteIdentifiers")}}o(Ls,"createAuthElysia");export{He as SENTRI_ERROR_STATUS,a as SentriError,$r as authorize,Ls as createAuthElysia,Nr as createAuthRouter,Ue as createAuthorize,ue as createErrorHandler,Le as createPermit,j as getCurrentAccessToken,Vr as permit,be as protect};
1
+ import {generateKeyPairSync,createPrivateKey,createPublicKey,createHash,randomUUID}from'crypto';import {sql,Kysely}from'kysely';import Ze from'bcrypt';import ee from'jsonwebtoken';import {Elysia}from'elysia';var Cr=Object.defineProperty;var o=(e,r)=>Cr(e,"name",{value:r,configurable:true});var He=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,RATE_LIMIT_EXCEEDED:429}),a=class extends Error{static{o(this,"SentriError");}code;statusCode;constructor(r,t,s){super(t),this.name="SentriError",this.code=r,this.statusCode=s??He[r]??500;}};var Me=new WeakMap,Ve=32,qe=10,Be=31;function Xe(e){if(e.mode==="client"){if(!e.keyUri||e.keyUri.trim().length===0)throw new a("CONFIGURATION_ERROR","keyUri must not be empty");return}if(!e.secret||e.secret.trim().length===0)throw new a("CONFIGURATION_ERROR","secret must not be empty");if((e.algorithm??"HS256").startsWith("HS")&&e.secret.length<Ve)throw new a("CONFIGURATION_ERROR",`secret must be at least ${Ve} characters for HMAC algorithms`);let s=e.saltRounds??12;if(!Number.isInteger(s)||s<qe||s>Be)throw new a("CONFIGURATION_ERROR",`saltRounds must be an integer between ${qe} and ${Be}`);if(!e.validRoles||e.validRoles.length===0)throw new a("CONFIGURATION_ERROR","validRoles must contain at least one role");if(e.validIdentifiers!==void 0&&e.validIdentifiers.length===0)throw new a("CONFIGURATION_ERROR","validIdentifiers must contain at least one identifier type");if(!e.dialect)throw new a("CONFIGURATION_ERROR","dialect is required in server mode")}o(Xe,"validateConfig");function A(e){let r=Me.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),validIdentifiers:e.validIdentifiers??["email","username"],validIdentifiersSet:new Set(e.validIdentifiers??["email","username"]),cookieName:e.cookie?.name??"refresh_token",accessCookieName:e.accessCookie?.name??"access_token"};return Me.set(e,t),t}o(A,"resolveServerConfig");var Nr=/^(\d+)([smhdw])$/,Sr={s:1e3,m:6e4,h:36e5,d:864e5,w:6048e5},je=new Map;function V(e){if(typeof e=="number")return e*1e3;let r=je.get(e);if(r!==void 0)return r;let t=Nr.exec(e);if(!t?.[1]||!t?.[2])throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let s=Sr[t[2]];if(s===void 0)throw new Error(`Invalid expiresIn: "${e}". Use e.g. "15m", "7d", "1h".`);let n=parseInt(t[1],10)*s;return je.set(e,n),n}o(V,"parseExpiry");var S={info:o(()=>{},"info"),warn:o(()=>{},"warn"),error:o(()=>{},"error")};function m(e,r,t){return {service:e,event:r,...t}}o(m,"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();}o(ze,"runMigrations");var Ge=new Map;function N(e){let r=Ge.get(e);return r||(r=new Kysely({dialect:e}),Ge.set(e,r)),r}o(N,"getDatabase");async function Y(e,r=12){return Ze.hash(e,r)}o(Y,"hashPassword");async function Q(e,r){return Ze.compare(e,r)}o(Q,"verifyPassword");var We=new Map,Je=new Map,Lr=3600*1e3;function Qe(e){let r=We.get(e);if(!r){let t=createPrivateKey(e),s=createPublicKey(t),n=s.export({format:"jwk"}),c=createHash("sha256").update(JSON.stringify({e:n.e,kty:n.kty,n:n.n})).digest("base64url"),h={...n,use:"sig",kid:c};r={kid:c,publicKey:s,jwk:h},We.set(e,r);}return r}o(Qe,"getOrBuildKey");function er(e){let{jwk:r}=Qe(e);return {keys:[r]}}o(er,"buildJwks");function rr(e){return Qe(e).publicKey}o(rr,"getPublicKeyFromPrivate");async function tr(e){let r=Date.now(),t=Je.get(e);if(t&&r-t.fetchedAt<Lr)return t.publicKey;let s=await fetch(e);if(!s.ok)throw new a("CONFIGURATION_ERROR",`Failed to fetch public key from ${e}: HTTP ${s.status}`);let n=await s.json();if(!n.keys||n.keys.length===0)throw new a("CONFIGURATION_ERROR",`No keys found in JWKS response from ${e}`);let i=n.keys[0],c=createPublicKey({key:i,format:"jwk"});return Je.set(e,{publicKey:c,fetchedAt:r}),c}o(tr,"fetchPublicKey");var sr=new Map,or=new Map,nr=new Map;function ir(e){return e.startsWith("RS")||e.startsWith("PS")}o(ir,"isRSA");function ar(e){let r=sr.get(e);return r||(r={access:`${e}:access`,refresh:`${e}:refresh`},sr.set(e,r)),r}o(ar,"getHsSecrets");function Pr(e){let r=or.get(e);return r||(r=createPrivateKey(e),or.set(e,r)),r}o(Pr,"getRsPrivateKey");function ur(e){let r=A(e);if(ir(r.algorithm)){let n=Pr(e.secret);return {accessKey:n,refreshKey:n}}let{access:t,refresh:s}=ar(e.secret);return {accessKey:t,refreshKey:s}}o(ur,"getSigningKeys");function dr(e,r){let t=A(e);if(ir(t.algorithm))return rr(e.secret);let{access:s,refresh:n}=ar(e.secret);return r==="access"?s:n}o(dr,"getVerifyKey");function cr(e,r,t,s){let n=`${t}:${s}`,i=nr.get(n);return i||(i={expiresIn:t,algorithm:s},nr.set(n,i)),ee.sign(e,r,i)}o(cr,"sign");function lr(e,r,t){try{let s=ee.verify(e,r,{algorithms:[t]});if(typeof s=="string"||s===null)throw new a("TOKEN_INVALID","Token payload is not an object");return s}catch(s){throw s instanceof a?s:s instanceof ee.TokenExpiredError?new a("TOKEN_EXPIRED","Token has expired"):new a("TOKEN_INVALID","Token is invalid or malformed")}}o(lr,"verify");function re(e,r){let t=A(r),{accessKey:s}=ur(r);return cr(e,s,t.accessExpiresIn,t.algorithm)}o(re,"signAccessToken");function te(e,r){let t=A(r),{refreshKey:s}=ur(r);return cr({sessionId:e},s,t.refreshExpiresIn,t.algorithm)}o(te,"signRefreshToken");function q(e,r){let t=A(r),s=dr(r,"access");return lr(e,s,t.algorithm)}o(q,"verifyAccessToken");function se(e,r){let t=A(r),s=dr(r,"refresh");return lr(e,s,t.algorithm)}o(se,"verifyRefreshToken");function fr(e,r){try{let t=ee.verify(e,r,{algorithms:["RS256","RS384","RS512","PS256","PS384","PS512"]});if(typeof t=="string"||t===null)throw new a("TOKEN_INVALID","Token payload is not an object");return t}catch(t){throw t instanceof a?t:t instanceof ee.TokenExpiredError?new a("TOKEN_EXPIRED","Token has expired"):new a("TOKEN_INVALID","Token is invalid or malformed")}}o(fr,"verifyTokenWithPublicKey");function xe(e){try{return JSON.parse(e)}catch{return []}}o(xe,"parseRoles");function Kr(e){return JSON.stringify(e)}o(Kr,"serializeRoles");function mr(e){return {id:e.id,userId:e.user_id,type:e.type,value:e.value,createdAt:new Date(e.created_at)}}o(mr,"mapIdentifier");async function oe(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:xe(t.roles)}:null}o(oe,"findUserByIdentifierValue");async function de(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:xe(t.roles)}:null}o(de,"findUserById");async function pr(e,r,t){let s=t.map(n=>({id:randomUUID(),user_id:r,type:n.type,value:n.value}));return await e.insertInto("sentri_identifiers").values(s).execute(),s.map(n=>({id:n.id,userId:n.user_id,type:n.type,value:n.value,createdAt:new Date}))}o(pr,"createIdentifiers");async function ne(e,r){return (await e.selectFrom("sentri_identifiers").selectAll().where("user_id","=",r).orderBy("created_at","asc").execute()).map(mr)}o(ne,"findIdentifiersByUserId");async function De(e,r,t){let s=await e.selectFrom("sentri_identifiers").selectAll().where("id","=",r).where("user_id","=",t).executeTakeFirst();return s?mr(s):null}o(De,"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)}o(wr,"countIdentifiersByUserId");async function gr(e,r,t,s){await e.updateTable("sentri_identifiers").set({type:s.type,value:s.value}).where("id","=",r).where("user_id","=",t).execute();}o(gr,"updateIdentifier");async function yr(e,r,t){await e.deleteFrom("sentri_identifiers").where("user_id","=",r).where("id","in",t).execute();}o(yr,"deleteIdentifiers");async function Ir(e,r,t){await e.updateTable("sentri_users").set({password_hash:t}).where("id","=",r).execute();}o(Ir,"updateUserPassword");async function Rr(e,r,t){await e.updateTable("sentri_users").set({roles:Kr(t)}).where("id","=",r).execute();}o(Rr,"updateUserRoles");async function Ce(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}}o(Ce,"createSession");async function Ne(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:xe(t.roles)}}:null}o(Ne,"findSessionById");async function Se(e,r){await e.deleteFrom("sentri_sessions").where("id","=",r).execute();}o(Se,"deleteSession");async function _r(e,r,t){await e.updateTable("sentri_sessions").set({replaced_by:t}).where("id","=",r).execute();}o(_r,"markSessionReplaced");async function ce(e,r){await e.deleteFrom("sentri_sessions").where("user_id","=",r).execute();}o(ce,"deleteAllSessionsForUser");async function le(e,r,t){let s=A(r),n=N(r.dialect),i=e.roles??[],c=i.filter(k=>!s.validRolesSet.has(k));if(c.length>0)return {success:false,error:new a("INVALID_ROLE",`Invalid roles: ${c.join(", ")}`)};if(!e.identifiers||e.identifiers.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one identifier is required")};let h=e.identifiers.map(k=>({type:k.type.trim(),value:k.value.trim()})),u=h.filter(k=>!s.validIdentifiersSet.has(k.type));if(u.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${u.map(k=>k.type).join(", ")}`)};if(new Set(h.map(k=>k.value)).size!==h.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let k of h)if(await oe(n,k.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${k.value}`)};let v=await Y(e.password,s.saltRounds),{userId:D,identifierRows:C}=await n.transaction().execute(async k=>{let H=randomUUID();await k.insertInto("sentri_users").values({id:H,password_hash:v,roles:JSON.stringify(i)}).execute();let Z=h.map(M=>({id:randomUUID(),user_id:H,type:M.type,value:M.value}));return await k.insertInto("sentri_identifiers").values(Z).execute(),{userId:H,identifierRows:Z}}),b={success:true,user:{id:D,roles:i,identifiers:C.map(k=>({id:k.id,type:k.type,value:k.value}))}};return r.hooks?.onRegister&&await r.hooks.onRegister(b.user),b}o(le,"register");async function fe(e,r,t){let s=A(r),n=N(r.dialect),i=await oe(n,e.identifier.trim());if(!i){let C=new a("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,C,{ip:t?.ip??""}),{success:false,error:C}}if(!await Q(e.password,i.passwordHash)){let C=new a("INVALID_CREDENTIALS","Invalid credentials");return r.hooks?.onLoginFailed&&await r.hooks.onLoginFailed(e.identifier,C,{ip:t?.ip??""}),{success:false,error:C}}let h=new Date(Date.now()+V(s.refreshExpiresIn)),u=await Ce(n,{userId:i.id,expiresAt:h,ipAddress:t?.ip??null,userAgent:t?.userAgent??null}),d={id:i.id,roles:i.roles},v=re({id:i.id,roles:i.roles,sessionId:u.id},r),D=te(u.id,r);return r.hooks?.onLoginSuccess&&await r.hooks.onLoginSuccess(d,{ip:t?.ip??"",userAgent:t?.userAgent??""}),{success:true,accessToken:v,refreshToken:D,user:d}}o(fe,"login");async function B(e,r,t){let s=A(r),n=N(r.dialect),i;try{({sessionId:i}=se(e,r));}catch(C){return C instanceof a?{success:false,error:C}:{success:false,error:new a("TOKEN_INVALID","Invalid refresh token")}}let c=await Ne(n,i);if(!c)return {success:false,error:new a("UNAUTHORIZED","Session not found or revoked")};if(c.replacedBy)return await ce(n,c.userId),{success:false,error:new a("TOKEN_REUSE","Token reuse detected. All sessions revoked.")};if(c.expiresAt.getTime()<Date.now())return await Se(n,i),{success:false,error:new a("TOKEN_EXPIRED","Session has expired")};let h=new Date(Date.now()+V(s.refreshExpiresIn)),u=await Ce(n,{userId:c.userId,expiresAt:h,ipAddress:t?.ip??null,userAgent:t?.userAgent??null});await _r(n,i,u.id);let d={id:c.user.id,roles:c.user.roles},v=re({...d,sessionId:u.id},r),D=te(u.id,r);return {success:true,accessToken:v,refreshToken:D,user:d}}o(B,"refresh");async function he(e,r){let t=N(r.dialect),s;try{({sessionId:s}=se(e,r));}catch{return}let n=await Ne(t,s);n&&(await Se(t,s),r.hooks?.onLogout&&await r.hooks.onLogout(n.userId));}o(he,"logout");async function me(e,r){let t=N(r.dialect);await ce(t,e),r.hooks?.onLogout&&await r.hooks.onLogout(e);}o(me,"logoutAll");async function pe(e,r){let t=N(r.dialect),s=await de(t,e);if(!s)return {success:false,error:new a("USER_NOT_FOUND","User not found")};let n=await ne(t,e);return {success:true,user:{id:s.id,roles:s.roles,identifiers:n.map(i=>({id:i.id,type:i.type,value:i.value}))}}}o(pe,"getUser");async function we(e,r,t,s){let n=A(s),i=N(s.dialect),c=await de(i,e);if(!c)return {success:false,error:new a("USER_NOT_FOUND","User not found")};if(!await Q(r,c.passwordHash))return {success:false,error:new a("INVALID_CREDENTIALS","Invalid credentials")};let u=await Y(t,n.saltRounds);return await Ir(i,e,u),await ce(i,e),s.hooks?.onPasswordChanged&&await s.hooks.onPasswordChanged(e),{success:true}}o(we,"changePassword");async function ge(e,r,t){let s=A(t),n=N(t.dialect),i=r.filter(d=>!s.validRolesSet.has(d));if(i.length>0)return {success:false,error:new a("INVALID_ROLE",`Invalid roles: ${i.join(", ")}`)};let c=await de(n,e);if(!c)return {success:false,error:new a("USER_NOT_FOUND","User not found")};let h=new Set(c.roles);for(let d of r)h.add(d);let u=Array.from(h);return await Rr(n,e,u),{success:true,user:{id:c.id,roles:u}}}o(ge,"assignRoles");async function ye(e,r,t){let s=A(t),n=N(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one identifier is required")};let i=r.map(d=>({type:d.type.trim(),value:d.value.trim()})),c=i.filter(d=>!s.validIdentifiersSet.has(d.type));if(c.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${c.map(d=>d.type).join(", ")}`)};if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i)if(await oe(n,d.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)};return await pr(n,e,i),{success:true,identifiers:(await ne(n,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}o(ye,"bulkCreateIdentifiers");async function Ie(e,r,t){let s=A(t),n=N(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one update is required")};let i=r.map(d=>({id:d.id,type:d.type.trim(),value:d.value.trim()})),c=i.filter(d=>!s.validIdentifiersSet.has(d.type));if(c.length>0)return {success:false,error:new a("VALIDATION_ERROR",`Invalid identifier types: ${c.map(d=>d.type).join(", ")}`)};if(new Set(i.map(d=>d.value)).size!==i.length)return {success:false,error:new a("VALIDATION_ERROR","Duplicate identifier values in request")};for(let d of i){let v=await De(n,d.id,e);if(!v)return {success:false,error:new a("IDENTIFIER_NOT_FOUND",`Identifier not found: ${d.id}`)};if(v.value!==d.value&&await oe(n,d.value))return {success:false,error:new a("IDENTIFIER_ALREADY_EXISTS",`Identifier already taken: ${d.value}`)}}return await n.transaction().execute(async d=>{for(let v of i)await gr(d,v.id,e,{type:v.type,value:v.value});}),{success:true,identifiers:(await ne(n,e)).map(d=>({id:d.id,type:d.type,value:d.value}))}}o(Ie,"bulkUpdateIdentifiers");async function Re(e,r,t){let s=N(t.dialect);if(r.length===0)return {success:false,error:new a("VALIDATION_ERROR","At least one ID is required")};let n=Array.from(new Set(r));for(let h of n)if(!await De(s,h,e))return {success:false,error:new a("IDENTIFIER_NOT_FOUND",`Identifier not found: ${h}`)};return await wr(s,e)-n.length<1?{success:false,error:new a("VALIDATION_ERROR","Cannot delete all identifiers \u2014 at least one must remain")}:(await yr(s,e,n),{success:true,identifiers:(await ne(s,e)).map(h=>({id:h.id,type:h.type,value:h.value}))})}o(Re,"bulkDeleteIdentifiers");function F(e){return A(e).cookieName}o(F,"getCookieName");function _e(e){return A(e).accessCookieName}o(_e,"getAccessCookieName");function K(e,r){if(!e)return;let t=`${r}=`,s=0;for(;s<e.length;){for(;s<e.length&&e[s]===" ";)s++;let n=e.indexOf(";",s),i=n===-1?e.length:n;if(e.startsWith(t,s))return e.slice(s+t.length,i);s=i+1;}}o(K,"readCookie");function ie(e,r,t){let s=t.cookie??{},n=V(A(t).refreshExpiresIn)/1e3;e.cookie??={},e.cookie[F(t)]={value:r,httpOnly:s.httpOnly??true,secure:s.secure??false,sameSite:s.sameSite??"strict",path:s.path??"/",maxAge:n};}o(ie,"setCookieFromConfig");function ve(e,r){let t=r.cookie??{};e.cookie??={},e.cookie[F(r)]={value:"",httpOnly:t.httpOnly??true,path:t.path??"/",maxAge:0};}o(ve,"clearCookieFromConfig");function ae(e,r,t){if(!t.accessCookie)return;let s=t.accessCookie,n=V(A(t).accessExpiresIn)/1e3;e.cookie??={},e.cookie[_e(t)]={value:r,httpOnly:false,secure:s.secure??false,sameSite:s.sameSite??"strict",path:s.path??"/",maxAge:n};}o(ae,"setAccessCookieFromConfig");function Oe(e,r){if(!r.accessCookie)return;let t=r.accessCookie;e.cookie??={},e.cookie[_e(r)]={value:"",httpOnly:false,path:t.path??"/",maxAge:0};}o(Oe,"clearAccessCookieFromConfig");function j(e,r){let t=e.headers.get("authorization");if(t?.startsWith("Bearer "))return t.slice(7);let s=e.headers.get("cookie");return s?K(s,_e(r)):void 0}o(j,"getCurrentAccessToken");function be(e){let r=e.logger??S,t=e.loggerService??"sentri";if(e.mode==="client"){let n=e;return new Elysia().derive({as:"global"},async({request:i})=>{let c=i.headers.get("authorization"),h=c?.startsWith("Bearer ")?c.slice(7):void 0;if(!h)throw r.warn(m(t,"auth.protect.failure",{mode:"client",errorCode:"UNAUTHORIZED"})),new a("UNAUTHORIZED","Missing or malformed Authorization header");try{let u=await tr(n.keyUri),d=fr(h,u),v={id:d.id,roles:d.roles};return r.info(m(t,"auth.protect.success",{mode:"client",userId:d.id})),{user:v}}catch(u){let d=u instanceof a?u.code:"TOKEN_INVALID";throw r.warn(m(t,"auth.protect.failure",{mode:"client",errorCode:d})),u}})}let s=e;return new Elysia().derive({as:"global"},async({request:n,set:i})=>{let c=j(n,s);if(!c)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"UNAUTHORIZED"})),new a("UNAUTHORIZED","Missing or malformed Authorization header");try{let h=q(c,s);if(s.isTokenRevoked&&await s.isTokenRevoked(h.sessionId))throw r.warn(m(t,"auth.protect.token_revoked",{mode:"server",userId:h.id})),new a("UNAUTHORIZED","Token has been revoked");let u={id:h.id,roles:h.roles};return r.info(m(t,"auth.protect.success",{mode:"server",userId:h.id})),{user:u}}catch(h){if(h instanceof a&&h.code==="TOKEN_EXPIRED"){let d=n.headers.get("cookie"),v=d?K(d,F(s)):void 0;if(!v)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED"})),new a("UNAUTHORIZED","Token expired. Please login again.");let D;try{D=await B(v,s);}catch{throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:"TOKEN_EXPIRED"})),new a("UNAUTHORIZED","Session expired. Please login again.")}if(!D.success)throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:D.error.code})),new a("UNAUTHORIZED","Session expired. Please login again.");return ie(i,D.refreshToken,s),ae(i,D.accessToken,s),i.headers["X-New-Access-Token"]=D.accessToken,r.info(m(t,"auth.protect.auto_refresh",{mode:"server",userId:D.user.id})),{user:D.user}}let u=h instanceof a?h.code:"TOKEN_INVALID";throw r.warn(m(t,"auth.protect.failure",{mode:"server",errorCode:u})),h}})}o(be,"protect");function Er(e,r,t){let s=`Requires one of roles: ${e.join(", ")}`;return ({user:n})=>{if(!n)throw r.warn(m(t,"auth.authorize.unauthenticated",{requiredRoles:e})),new a("UNAUTHORIZED","Not authenticated");let i=n.roles;if(!e.some(c=>i.includes(c)))throw r.warn(m(t,"auth.authorize.denied",{userId:n.id,userRoles:[...i],requiredRoles:e})),new a("FORBIDDEN",s);r.info(m(t,"auth.authorize.passed",{userId:n.id,userRoles:[...i],requiredRoles:e}));}}o(Er,"createAuthorizeHandler");function Ue(e,r){return o(function(...s){return Er(s,e,r)},"authorize")}o(Ue,"createAuthorize");function $r(...e){return Er(e,S,"sentri")}o($r,"authorize");var Hr=new a("FORBIDDEN","You do not have permission to perform this action");function kr(e,r,t){return async s=>{let n=s.user;if(!n)throw r.warn(m(t,"auth.permit.unauthenticated",{})),new a("UNAUTHORIZED","Not authenticated");if(e.roles&&e.roles.length>0){let i=n.roles;if(e.roles.some(c=>i.includes(c))){r.info(m(t,"auth.permit.role_bypass",{userId:n.id,bypassedByRole:true}));return}}try{let i=e.check(s);if(i instanceof Promise?await i:i)r.info(m(t,"auth.permit.passed",{userId:n.id}));else throw r.warn(m(t,"auth.permit.denied",{userId:n.id})),Hr}catch(i){throw i}}}o(kr,"createPermitHandler");function Le(e,r){return o(function(s){return kr(typeof s=="function"?{check:s}:s,e,r)},"permit")}o(Le,"createPermit");function Mr(e){return kr(typeof e=="function"?{check:e}:e,S,"sentri")}o(Mr,"permit");function ue(e){return ({error:r,set:t})=>r instanceof a?(t.status=r.statusCode,{error:true,statusCode:r.statusCode,code:r.code,message:r.message,data:null}):(e?.onUnhandled?.(r),t.status=500,{error:true,statusCode:500,code:"INTERNAL_SERVER_ERROR",message:"Internal server error",data:null})}o(ue,"createErrorHandler");var Fe=class{static{o(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 n=Date.now(),i=this.store.get(r);if((!i||i.expiresAt<n)&&(i={count:0,expiresAt:n+s},this.store.set(r,i)),i.count>=t){let c=Math.ceil((i.expiresAt-n)/1e3);throw new a("RATE_LIMIT_EXCEEDED",`Rate limit exceeded. Try again in ${c} seconds.`)}i.count++;}},Pe=class{static{o(this,"RedisRateLimiter");}redis;logger;constructor(r,t){this.redis=r,this.logger=t;}async consume(r,t,s){let n=`sentri:rl:${r}`,i=Math.ceil(s/1e3);try{let c=await this.redis.multi().incr(n).expire(n,i,"NX").exec();if(!c||c.length===0)return;if(c[0][1]>t)throw new a("RATE_LIMIT_EXCEEDED","Rate limit exceeded. Try again later.")}catch(c){if(c instanceof a&&c.code==="RATE_LIMIT_EXCEEDED")throw c;this.logger.error({msg:"Redis RateLimiter failed, bypassing limit",error:c});}}},X=null;async function Tr(e,r){if(X)return X;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"}),X=new Pe(s,r??S),X}catch(t){r?.warn({msg:"Failed to connect to Redis for Rate Limiting, falling back to InMemoryRateLimiter",error:t});}return X=new Fe,X}o(Tr,"getRateLimiter");var Ae=8,z=72,Ee=255,xr=100,G=50;function _(e){return new a("VALIDATION_ERROR",e)}o(_,"badRequest");function O(e,r,t,s){return e.status=r,{error:false,statusCode:r,message:t,data:s}}o(O,"ok");function $(e){if(e==null||typeof e!="object"||Array.isArray(e))throw new a("VALIDATION_ERROR","Request body is missing or not a JSON object. Did you set Content-Type: application/json?");return e}o($,"parseBody");function qr(e,r){if(!r.apiKey)return;let t=e.headers.get("x-api-key");if(typeof t!="string"||t!==r.apiKey)throw new a("UNAUTHORIZED","Invalid or missing API key")}o(qr,"validateApiKey");function Ke(e){if(e)try{let r=e();r instanceof Promise&&r.catch(()=>{});}catch{}}o(Ke,"fireHook");function Br(e){return e.startsWith("RS")||e.startsWith("PS")}o(Br,"isRSA");function $e(e,r){if(typeof e!="object"||e===null||Array.isArray(e))throw _(`identifiers[${r}] must be an object`);let t=e;if(typeof t.type!="string"||t.type.trim().length===0)throw _(`identifiers[${r}].type is required and must be a non-empty string`);if(t.type.length>xr)throw _(`identifiers[${r}].type must not exceed ${xr} characters`);if(typeof t.value!="string"||t.value.trim().length===0)throw _(`identifiers[${r}].value is required and must be a non-empty string`);if(t.value.length>Ee)throw _(`identifiers[${r}].value must not exceed ${Ee} characters`);return {type:t.type,value:t.value}}o($e,"validateIdentifierInput");async function P(e,r,t){let s=j(e,t);if(!s)throw new a("UNAUTHORIZED","Missing or malformed Authorization header");let n=q(s,t);if(t.isTokenRevoked&&await t.isTokenRevoked(n.sessionId))throw new a("UNAUTHORIZED","Token has been revoked");return {id:n.id,roles:n.roles}}o(P,"requireAuth");function Dr(e){let r=e,t=A(r),s=e.logger??S,n=e.loggerService??"sentri",i=e.router?.register??(l=>le(l,r)),c=e.router?.login??(l=>fe(l,r)),h=e.router?.refresh??(l=>B(l,r)),u=e.router?.logout??(l=>l!==void 0?he(l,r):Promise.resolve()),d=e.router?.logoutAll??(l=>me(l,r)),v=e.router?.getUser??(l=>pe(l,r)),D=e.router?.assignRoles??((l,f)=>ge(l,f,r)),C=e.router?.changePassword??((l,f,g)=>we(l,f,g,r)),b=e.router?.bulkCreateIdentifiers??((l,f)=>ye(l,f,r)),k=e.router?.bulkUpdateIdentifiers??((l,f)=>Ie(l,f,r)),H=e.router?.bulkDeleteIdentifiers??((l,f)=>Re(l,f,r)),Z=Tr(e.redisUrl,s),M=new Elysia().onError(ue());return Br(t.algorithm)&&M.get("/keys",({set:l})=>(l.headers["Cache-Control"]="public, max-age=3600",er(e.secret))),M.post("/register",async({body:l,request:f,set:g})=>{let I=Date.now();qr(f,r);let p=$(l),{identifiers:x,password:w,roles:R}=p;if(!Array.isArray(x)||x.length===0)throw _("identifiers is required and must be a non-empty array");if(x.length>G)throw _(`identifiers must not exceed ${G} entries`);let y=x.map((J,ke)=>$e(J,ke));if(typeof w!="string"||w.length<Ae)throw _(`password is required and must be at least ${Ae} characters`);if(w.length>z)throw _(`password must not exceed ${z} characters`);if(R!==void 0&&!Array.isArray(R))throw _("roles must be an array of strings when provided");if(Array.isArray(R)&&!R.every(J=>typeof J=="string"))throw _("each role must be a string");let T=Array.isArray(R)?R:void 0,E=T!==void 0?{identifiers:y,password:w,roles:T}:{identifiers:y,password:w},L=f.headers.get("x-forwarded-for")||"127.0.0.1",W=f.headers.get("user-agent")||"Unknown";if(e.rateLimit!==false){let J=await Z,ke=typeof e.rateLimit=="object"?e.rateLimit.maxRegisterAttempts??5:5;await J.consume(`register:${L}`,ke,900*1e3);}let U=await i(E,{ip:L,userAgent:W});return U.success?(s.info(m(n,"auth.register.success",{userId:U.user.id,duration_ms:Date.now()-I})),O(g,201,"User registered successfully",{user:U.user})):(s.warn(m(n,"auth.register.failure",{errorCode:U.error.code,duration_ms:Date.now()-I})),g.status=U.error.statusCode,{error:true,statusCode:U.error.statusCode,code:U.error.code,message:U.error.message,data:null})}).post("/login",async({body:l,request:f,set:g})=>{let I=Date.now(),p=$(l),{identifier:x,password:w}=p;if(typeof x!="string"||x.trim().length===0)throw _("identifier is required and must be a non-empty string");if(x.length>Ee)throw _(`identifier must not exceed ${Ee} characters`);if(typeof w!="string"||w.length===0)throw _("password is required");if(w.length>z)throw _(`password must not exceed ${z} characters`);let R=x.trim(),y=f.headers.get("x-forwarded-for")||"127.0.0.1",T=f.headers.get("user-agent")||"Unknown";if(e.rateLimit!==false){let L=await Z,W=typeof e.rateLimit=="object"?e.rateLimit.maxLoginAttempts??5:5;await L.consume(`login:${y}`,W,900*1e3);}let E=await c({identifier:R,password:w},{ip:y,userAgent:T});return E.success?(Ke(()=>r.hooks?.onLoginSuccess?.(E.user,{ip:y,userAgent:T})),ie(g,E.refreshToken,r),ae(g,E.accessToken,r),s.info(m(n,"auth.login.success",{userId:E.user.id,duration_ms:Date.now()-I})),O(g,200,"Login successful",{accessToken:E.accessToken,user:E.user})):(Ke(()=>r.hooks?.onLoginFailed?.(R,E.error,{ip:y})),s.warn(m(n,"auth.login.failure",{errorCode:E.error.code,duration_ms:Date.now()-I})),g.status=E.error.statusCode,{error:true,statusCode:E.error.statusCode,code:E.error.code,message:E.error.message,data:null})}).post("/refresh",async({request:l,set:f})=>{let g=Date.now(),I=K(l.headers.get("cookie")??void 0,F(r));if(!I)throw new a("UNAUTHORIZED","Refresh token cookie is missing");let p=l.headers.get("x-forwarded-for")||"127.0.0.1",x=l.headers.get("user-agent")||"Unknown",w=await h(I,{ip:p,userAgent:x});return w.success?(ie(f,w.refreshToken,r),ae(f,w.accessToken,r),s.info(m(n,"auth.refresh.success",{userId:w.user.id,duration_ms:Date.now()-g})),O(f,200,"Token refreshed",{accessToken:w.accessToken})):(ve(f,r),s.warn(m(n,"auth.refresh.failure",{errorCode:w.error.code,duration_ms:Date.now()-g})),f.status=w.error.statusCode,{error:true,statusCode:w.error.statusCode,code:w.error.code,message:w.error.message,data:null})}).post("/logout",async({request:l,set:f})=>{let g=Date.now(),I=K(l.headers.get("cookie")??void 0,F(r));return await u(I),ve(f,r),Oe(f,r),s.info(m(n,"auth.logout",{duration_ms:Date.now()-g})),O(f,200,"Logged out",null)}).post("/logout-all",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r);return await d(I.id),Ke(()=>r.hooks?.onLogout?.(I.id)),ve(f,r),Oe(f,r),s.info(m(n,"auth.logout_all",{userId:I.id,duration_ms:Date.now()-g})),O(f,200,"All sessions revoked",null)}).get("/me",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r),p=await v(I.id);return p.success?(s.info(m(n,"auth.me.success",{userId:p.user.id,duration_ms:Date.now()-g})),O(f,200,"OK",p.user)):(s.warn(m(n,"auth.me.failure",{userId:I.id,errorCode:p.error.code,duration_ms:Date.now()-g})),f.status=p.error.statusCode,{error:true,statusCode:p.error.statusCode,code:p.error.code,message:p.error.message,data:null})}).get("/me/identifiers",async({request:l,set:f})=>{let g=Date.now(),I=await P(l,f,r),p=await v(I.id);return p.success?(s.info(m(n,"auth.me.identifiers.success",{userId:p.user.id,count:p.user.identifiers?.length??0,duration_ms:Date.now()-g})),O(f,200,"OK",{identifiers:p.user.identifiers??[]})):(s.warn(m(n,"auth.me.identifiers.failure",{userId:I.id,errorCode:p.error.code,duration_ms:Date.now()-g})),f.status=p.error.statusCode,{error:true,statusCode:p.error.statusCode,code:p.error.code,message:p.error.message,data:null})}).post("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{identifiers:w}=x;if(!Array.isArray(w)||w.length===0)throw _("identifiers is required and must be a non-empty array");if(w.length>G)throw _(`identifiers must not exceed ${G} entries`);let R=w.map((T,E)=>$e(T,E)),y=await b(p.id,R);return y.success?(s.info(m(n,"auth.identifiers.created",{userId:p.id,count:y.identifiers.length,duration_ms:Date.now()-I})),O(g,201,"Identifiers added successfully",{identifiers:y.identifiers})):(s.warn(m(n,"auth.identifiers.create_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).put("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{identifiers:w}=x;if(!Array.isArray(w)||w.length===0)throw _("identifiers is required and must be a non-empty array");if(w.length>G)throw _(`identifiers must not exceed ${G} entries`);let R=w.map((T,E)=>{if(typeof T!="object"||T===null||Array.isArray(T))throw _(`identifiers[${E}] must be an object`);let L=T;if(typeof L.id!="string"||L.id.trim().length===0)throw _(`identifiers[${E}].id is required and must be a non-empty string`);let{type:W,value:U}=$e(T,E);return {id:L.id,type:W,value:U}}),y=await k(p.id,R);return y.success?(s.info(m(n,"auth.identifiers.updated",{userId:p.id,count:y.identifiers.length,duration_ms:Date.now()-I})),O(g,200,"Identifiers updated successfully",{identifiers:y.identifiers})):(s.warn(m(n,"auth.identifiers.update_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).delete("/me/identifiers",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{ids:w}=x;if(!Array.isArray(w)||w.length===0)throw _("ids is required and must be a non-empty array of strings");if(!w.every(y=>typeof y=="string"))throw _("each id must be a string");let R=await H(p.id,w);return R.success?(s.info(m(n,"auth.identifiers.deleted",{userId:p.id,duration_ms:Date.now()-I})),O(g,200,"Identifiers deleted successfully",{identifiers:R.identifiers})):(s.warn(m(n,"auth.identifiers.delete_failure",{userId:p.id,errorCode:R.error.code,duration_ms:Date.now()-I})),g.status=R.error.statusCode,{error:true,statusCode:R.error.statusCode,code:R.error.code,message:R.error.message,data:null})}).patch("/me/password",async({body:l,request:f,set:g})=>{let I=Date.now(),p=await P(f,g,r),x=$(l),{currentPassword:w,newPassword:R}=x;if(typeof w!="string"||w.length===0)throw _("currentPassword is required");if(typeof R!="string"||R.length<Ae)throw _(`newPassword must be at least ${Ae} characters`);if(R.length>z)throw _(`newPassword must not exceed ${z} characters`);if(w===R)throw _("newPassword must be different from currentPassword");let y=await C(p.id,w,R);return y.success?(s.info(m(n,"auth.password.changed",{userId:p.id,duration_ms:Date.now()-I})),O(g,200,"Password updated successfully. All sessions have been revoked.",null)):(s.warn(m(n,"auth.password.change_failure",{userId:p.id,errorCode:y.error.code,duration_ms:Date.now()-I})),g.status=y.error.statusCode,{error:true,statusCode:y.error.statusCode,code:y.error.code,message:y.error.message,data:null})}).post("/users/:userId/roles",async({body:l,request:f,set:g,params:I})=>{let p=Date.now();if(!(await P(f,g,r)).roles.includes("admin"))throw new a("FORBIDDEN","Requires one of roles: admin");let w=$(l),{roles:R}=w,{userId:y}=I;if(!y)throw _("userId is required");if(!Array.isArray(R)||R.length===0)throw _("roles must be a non-empty array of strings");if(!R.every(E=>typeof E=="string"))throw _("each role must be a string");let T=await D(y,R);return T.success?(s.info(m(n,"auth.roles.assigned",{targetUserId:y,roles:R,duration_ms:Date.now()-p})),O(g,200,"Roles assigned successfully",{user:T.user})):(s.warn(m(n,"auth.roles.assign_failure",{targetUserId:y,errorCode:T.error.code,duration_ms:Date.now()-p})),g.status=T.error.statusCode,{error:true,statusCode:T.error.statusCode,code:T.error.code,message:T.error.message,data:null})}),M}o(Dr,"createAuthRouter");function Fs(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}};Xe(u);let d=u.logger??S,v=u.loggerService??"sentri",D=Ue(d,v),C=Le(d,v);return {protect:o(()=>be(u),"protect"),authorize:o((...b)=>D(...b),"authorize"),permit:o(b=>C(b),"permit"),errorHandler:o(b=>ue(b),"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.validIdentifiers!==void 0&&{validIdentifiers:e.validIdentifiers},...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.rateLimit!==void 0&&{rateLimit:e.rateLimit},...e.redisUrl!==void 0&&{redisUrl:e.redisUrl},...e.logger!==void 0&&{logger:e.logger},...e.loggerService!==void 0&&{loggerService:e.loggerService}},s=t.logger??S,n=t.loggerService??"sentri",i=A(t),c=Ue(s,n),h=Le(s,n);return {protect:o(()=>be(t),"protect"),authorize:o((...u)=>c(...u),"authorize"),permit:o(u=>h(u),"permit"),errorHandler:o(u=>ue(u),"errorHandler"),router:o(()=>Dr(t),"router"),migrate:o(()=>ze(N(t.dialect)),"migrate"),getCurrentAccessToken:o(u=>j(u,t),"getCurrentAccessToken"),hashPassword:o(u=>Y(u,i.saltRounds),"hashPassword"),verifyPassword:o((u,d)=>Q(u,d),"verifyPassword"),signAccessToken:o(u=>re(u,t),"signAccessToken"),signRefreshToken:o(u=>te(u,t),"signRefreshToken"),verifyAccessToken:o(u=>q(u,t),"verifyAccessToken"),verifyRefreshToken:o(u=>se(u,t),"verifyRefreshToken"),register:o(u=>le(u,t),"register"),login:o(u=>fe(u,t),"login"),refresh:o(u=>B(u,t),"refresh"),logout:o(u=>he(u,t),"logout"),logoutAll:o(u=>me(u,t),"logoutAll"),getUser:o(u=>pe(u,t),"getUser"),changePassword:o((u,d,v)=>we(u,d,v,t),"changePassword"),assignRoles:o((u,d)=>ge(u,d,t),"assignRoles"),bulkCreateIdentifiers:o((u,d)=>ye(u,d,t),"bulkCreateIdentifiers"),bulkUpdateIdentifiers:o((u,d)=>Ie(u,d,t),"bulkUpdateIdentifiers"),bulkDeleteIdentifiers:o((u,d)=>Re(u,d,t),"bulkDeleteIdentifiers")}}o(Fs,"createAuthElysia");export{He as SENTRI_ERROR_STATUS,a as SentriError,$r as authorize,Fs as createAuthElysia,Dr as createAuthRouter,Ue as createAuthorize,ue as createErrorHandler,Le as createPermit,j as getCurrentAccessToken,Mr as permit,be as protect};