@analog-tools/auth
Version:
Authentication module for AnalogJS applications
4 lines (3 loc) • 35.2 kB
JavaScript
;Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("h3"),d=require("@analog-tools/session"),K=require("@trpc/server");var f=(r=>(r.SkyBlue="\x1B[94m",r.OceanBlue="\x1B[34m",r.MidnightBlue="\x1B[38;5;17m",r.SkyBlueBg="\x1B[104m",r.OceanBlueBg="\x1B[44m",r.MidnightBlueBg="\x1B[48;5;17m",r.MintGreen="\x1B[92m",r.ForestGreen="\x1B[32m",r.EmeraldGreen="\x1B[38;5;28m",r.MintGreenBg="\x1B[102m",r.ForestGreenBg="\x1B[42m",r.EmeraldGreenBg="\x1B[48;5;28m",r.LemonYellow="\x1B[93m",r.SunflowerYellow="\x1B[33m",r.GoldYellow="\x1B[38;5;220m",r.LemonYellowBg="\x1B[103m",r.SunflowerYellowBg="\x1B[43m",r.GoldYellowBg="\x1B[48;5;220m",r.RoseRed="\x1B[91m",r.FireRed="\x1B[31m",r.BurgundyRed="\x1B[38;5;88m",r.RoseRedBg="\x1B[101m",r.FireRedBg="\x1B[41m",r.BurgundyRedBg="\x1B[48;5;88m",r.LavenderPurple="\x1B[95m",r.RoyalPurple="\x1B[38;5;93m",r.DeepPurple="\x1B[38;5;54m",r.LavenderPurpleBg="\x1B[105m",r.RoyalPurpleBg="\x1B[48;5;93m",r.DeepPurpleBg="\x1B[48;5;54m",r.PeachOrange="\x1B[38;5;215m",r.TangerineOrange="\x1B[38;5;208m",r.AmberOrange="\x1B[38;5;214m",r.PeachOrangeBg="\x1B[48;5;215m",r.TangerineOrangeBg="\x1B[48;5;208m",r.AmberOrangeBg="\x1B[48;5;214m",r.SilverGray="\x1B[37m",r.SlateGray="\x1B[90m",r.CharcoalGray="\x1B[38;5;238m",r.SilverGrayBg="\x1B[47m",r.SlateGrayBg="\x1B[100m",r.CharcoalGrayBg="\x1B[48;5;238m",r.PureBlack="\x1B[30m",r.PureWhite="\x1B[97m",r.PureBlackBg="\x1B[40m",r.PureWhiteBg="\x1B[107m",r.Cyan="\x1B[36m",r.Reset="\x1B[0m",r.Bold="\x1B[1m",r.Dim="\x1B[2m",r.Underline="\x1B[4m",r.Blink="\x1B[5m",r.Reverse="\x1B[7m",r.Hidden="\x1B[8m",r))(f||{}),h=(r=>(r[r.trace=0]="trace",r[r.debug=1]="debug",r[r.info=2]="info",r[r.warn=3]="warn",r[r.error=4]="error",r[r.fatal=5]="fatal",r[r.silent=6]="silent",r))(h||{});function R(r){return["trace","debug","info","warn","error","fatal","silent"].includes(r)}const C=class C{static getCacheKey(e,t,s,i){if(!e.stack)return null;const a=e.stack.split(`
`)[0]||"";return`${e.name}:${e.message}:${a}:${t}:${s}:${i}`}static addToCache(e,t){if(this.serializationCache.size>=this.MAX_CACHE_SIZE){const s=this.serializationCache.keys().next().value;s&&this.serializationCache.delete(s)}this.serializationCache.set(e,t)}static serialize(e,t={}){const{includeStack:s=!0,maxDepth:i=C.DEFAULT_MAX_DEPTH,includeNonEnumerable:a=!1}=t;if(e instanceof Error){const o=this.getCacheKey(e,s,i,a);if(o){const l=this.serializationCache.get(o);if(l)return l}const c=this.serializeError(e,s,i,a,new WeakSet);return o&&this.addToCache(o,c),c}if(typeof e=="string")return e;const n=this.safeStringify(e,i,new WeakSet);if(typeof n=="string")return n;try{return JSON.stringify(n,null,2)}catch{return String(n)}}static serializeError(e,t,s,i,a=new WeakSet){if(a.has(e))return{message:e.message,name:e.name,[Symbol.for("circular")]:this.CIRCULAR_REF_PLACEHOLDER};a.add(e);const n={message:e.message,name:e.name};return t&&e.stack&&(n.stack=e.stack),"cause"in e&&e.cause!==void 0&&(e.cause instanceof Error?n.cause=this.serializeError(e.cause,t,s-1,i,a):n.cause=this.safeStringify(e.cause,s-1,a)),Object.keys(e).forEach(o=>{if(!(o in n))try{const c=e;n[o]=this.safeStringify(c[o],s-1,a)}catch{n[o]=this.UNABLE_TO_SERIALIZE}}),i&&Object.getOwnPropertyNames(e).forEach(o=>{if(!(o in n)&&o!=="stack"&&o!=="message"&&o!=="name")try{const c=Object.getOwnPropertyDescriptor(e,o);if(c&&c.enumerable===!1){const l=e;n[o]=this.safeStringify(l[o],s-1,a)}}catch{n[o]=this.UNABLE_TO_SERIALIZE}}),n}static safeStringify(e,t,s=new WeakSet){if(t<=0)return this.MAX_DEPTH_PLACEHOLDER;if(e===null||typeof e!="object")return e;if(s.has(e))return this.CIRCULAR_REF_PLACEHOLDER;s.add(e);try{if(Array.isArray(e)){const a=e.map(n=>this.safeStringify(n,t-1,s));return s.delete(e),a}const i={};for(const[a,n]of Object.entries(e))try{i[a]=this.safeStringify(n,t-1,s)}catch{i[a]=this.UNABLE_TO_SERIALIZE}return s.delete(e),i}catch{return s.delete(e),this.UNABLE_TO_SERIALIZE}}};C.DEFAULT_MAX_DEPTH=10,C.CIRCULAR_REF_PLACEHOLDER="[Circular Reference]",C.MAX_DEPTH_PLACEHOLDER="[Max Depth Reached]",C.UNABLE_TO_SERIALIZE="[Unable to serialize]",C.serializationCache=new Map,C.MAX_CACHE_SIZE=100;let E=C;const G={highlight:{color:f.LemonYellow,bold:!0},accent:{color:f.SkyBlue},attention:{color:f.RoyalPurple,bold:!0},success:{color:f.ForestGreen},warning:{color:f.TangerineOrange},error:{color:f.FireRed},info:{color:f.OceanBlue},debug:{color:f.SlateGray}},O={success:"✅",warning:"⚠️",error:"❌",info:"ℹ️",debug:"🐞"},Y={trace:f.SlateGray,debug:f.Cyan,info:f.ForestGreen,warn:f.SunflowerYellow,error:f.FireRed,fatal:f.FireRed,silent:f.Reset},x=class x{constructor(e={}){this.styleCache=new Map;const t=process.env.NODE_ENV==="test"||process.env.VITEST==="true";this.useColors=e.useColors!==void 0?e.useColors:!t,this.globalStyles={...G,...e.styles},this.globalIcons={...O,...e.icons}}resolveStyle(e,t="test",s){return this.applyStyle(e,t,s)}setUseColors(e){this.useColors=e}getUseColors(){return this.useColors}updateStyleConfig(e,t){this.globalStyles={...this.globalStyles,...e},this.globalIcons={...this.globalIcons,...t}}formatMessage(e,t,s,i,a){const n=i?`[${s}:${i}]`:`[${s}]`;if(this.useColors){let o=this.getColorForLevel(e);return a&&(o=a),`${o}${n} ${t}${f.Reset}`}else return`${n} ${t}`}formatMessageWithMetadata(e,t,s,i,a){let n=t,o=this.getColorForLevel(e);if(i!=null&&i.style){const c=this.applyStyle(i.style,s,a);c&&(o=c)}return i!=null&&i.icon&&(n=`${this.resolveIcon(i.icon,s,a)} ${t}`),this.formatMessage(e,n,s,a,o)}parseMetadataParameter(e,t=[]){if(e&&typeof e=="object"&&!Array.isArray(e)&&("style"in e||"icon"in e))return{metadata:e,restData:t};const s=e!==void 0?[e,...t]:t;if(s.length>0){const i=s[s.length-1];if(i&&typeof i=="object"&&!Array.isArray(i)&&("style"in i||"icon"in i))return{metadata:i,restData:s.slice(0,-1)}}return{metadata:void 0,restData:s}}getColorForLevel(e){const t=h[e],s=Y[t];return e===h.fatal?`${f.Bold}${s}`:s||f.Reset}applyStyle(e,t,s){const i=this.getStyleCacheValue(e);if(i!==void 0)return i;if(typeof e=="string"){const a=this.getSemanticStyleColor(e,t,s);return this.setStyleCache(e,a),a}if(typeof e=="object"&&"color"in e){if(!this.isValidColor(e.color)){this.setStyleCache(e,void 0),this.logWarning("Invalid color provided. Only predefined ColorEnum values are allowed.",t,s);return}const a=this.constructStyleCode(e);return this.setStyleCache(e,a),a}this.setStyleCache(e,void 0),this.logWarning("Unknown style configuration provided. Only semantic style names or valid ColorEnum objects are allowed.",t,s)}isValidColor(e){return Object.values(f).includes(e)}isValidIcon(e){return this.isEmojiIcon(e)}getStyleCacheValue(e){return this.styleCache.has(e)?this.styleCache.get(e):void 0}setStyleCache(e,t){this.styleCache.set(e,t)}logWarning(e,t,s){const i=s?`${t}:${s}`:t;console.warn(`[${i}] ${e}`)}constructStyleCode(e){let t=e.color.toString();return e.bold&&(t+=f.Bold),e.underline&&(t+=f.Underline),t}getSemanticStyleColor(e,t,s){if(this.styleCache.has(e))return this.styleCache.get(e);const i=this.globalStyles[e];if(i){let n=i.color.toString();return i.bold&&(n+=f.Bold),i.underline&&(n+=f.Underline),this.styleCache.set(e,n),n}const a=s?`${t}:${s}`:t;console.warn(`[${a}] Unknown semantic style: ${e}. Falling back to default.`),this.styleCache.set(e,void 0)}getStyleCache(){return this.styleCache}resolveIcon(e,t,s){if(this.isValidIcon(e))return e;const i=["success","warning","error","info","debug"],a=i.find(n=>n===e);return a&&this.globalIcons[a]?this.globalIcons[a]:(i.includes(e)?this.logWarning(`Unknown icon: ${e}. Expected a valid emoji or semantic icon name.`,t,s):this.logWarning(`Invalid icon: ${e}. Expected a valid emoji or semantic icon name.`,t,s),e)}isEmojiIcon(e){return["✅","⚠️","❌","ℹ️","🐞","⭐️","🚀","🔥","✔️","✖️","❓","🔒","🔓","⏳","🕒","⬆️","⬇️","➡️","⬅️","📁","📄","👤","👥","✏️","➕","➖","🔔","⚡️","🎁","🐛","🌟","❤️","👀","⚙️","🔧","🔨","🔑","🎉","📝","🚨","📅","💡","🔍","🔗","🔖","📌","📎","✉️","📞","🌍","☁️","🌈","🌙","☀️","❄️","✨","🎵","📷","🎥","🎤","🔊","🔋","🗑️","💰","💳","🎂","🏅","🏆","👑","🛸","🛡️","🛑","▶️","⏸️","⏺️","⏪","⏩","🔁","🔀","🎲","🎈","🍪","☕️","🍵","🍺","🍷","🍕","🍔","🍟","🍎","🍌","🍒","🍋","🥕","🌽","🥦","🥚","🧀","🍞","🍰","🍦","🍫","🍿","🥓","🍤","🐟","🦀","🐙","🐋","🐬","🐧","🐸","🐢","🐍","🐉","🦄","🐱","🐶","🐭","🐰","🐻","🐼","🐨","🐯","🦁","🐒","🐘","🐎","🐄","🐖","🐑","🐔","🦆","🦢","🦉","🦅","🦜","🦚","🦩","🦋","🐝","🐜","🐞","🕷️","🦂","🐌","🪱","🐛","🦗","🦟","🪰","🪳","🪲"].includes(e)}};x.INJECTABLE=!0;let B=x;class T extends Error{constructor(e){super(e),this.name="LoggerError"}}const _={windowMs:5e3,flushOnCritical:!0},q=[h.error,h.fatal];class J{constructor(e,t){this.config=e,this.formatMessage=t,this.entries=new Map}addMessage(e,t,s=""){if(!this.config.enabled||this.config.flushOnCritical&&q.includes(e))return!0;const i=this.generateFingerprint(t,e,s),a=this.entries.get(i);return a?a.count++:this.entries.set(i,{message:t,level:e,context:s,firstSeen:Date.now(),count:1}),this.scheduleFlush(),!1}flush(){if(this.entries.size!==0){this.flushTimer&&(clearTimeout(this.flushTimer),this.flushTimer=void 0);for(const e of this.entries.values())this.outputMessage(e);this.entries.clear()}}destroy(){this.flushTimer&&(clearTimeout(this.flushTimer),this.flushTimer=void 0),this.entries.clear()}generateFingerprint(e,t,s){return`${t}:${s}:${e}`}scheduleFlush(){this.flushTimer||(this.flushTimer=setTimeout(()=>{this.flush()},this.config.windowMs))}outputMessage(e){let t=e.message;e.count>1&&(t=`${t} (×${e.count})`);const s=this.formatMessage(e.level,t,e.context);switch(e.level){case h.trace:console.trace(s);break;case h.debug:console.debug(s);break;case h.info:console.info(s);break;case h.warn:console.warn(s);break;case h.error:console.error(s);break;case h.fatal:console.error(s);break}}}const v=class v{constructor(e={},t,s,i){var a,n;if(this.config=e,this.childLoggers={},this.disabledContexts=[],this.activeGroups=[],i)this.parentLogger=i,this.name=i.name,this.context=s,this.logLevel=i.getLogLevel(),this.styleEngine=i.styleEngine;else{if(typeof e.level=="string"&&!Object.keys(h).includes(e.level))throw new T(`Invalid log level: ${e.level}`);if(this.logLevel=this.castLoglevel(e.level||process.env.LOG_LEVEL||"info"),this.name=e.name||"analog-tools",this.setDisabledContexts(e.disabledContexts??((a=process.env.LOG_DISABLED_CONTEXTS)==null?void 0:a.split(","))??[]),this.styleEngine=t||new B({useColors:e.useColors,styles:{...G,...e.styles},icons:{...O,...e.icons}}),(n=e.deduplication)!=null&&n.enabled){const o={enabled:!0,windowMs:e.deduplication.windowMs??_.windowMs,flushOnCritical:e.deduplication.flushOnCritical??_.flushOnCritical},c=(l,u,w)=>this.styleEngine.formatMessage(l,u,this.name,w);this.deduplicator=new J(o,c)}}}isContextEnabled(){return this.context?!(this.parentLogger||this).disabledContexts.includes(this.context):!0}setDisabledContexts(e){this.disabledContexts=e}getLogLevel(){return this.logLevel}getDisabledContexts(){return(this.parentLogger||this).disabledContexts||[]}setUseColors(e){(this.parentLogger||this).styleEngine.setUseColors(e)}getUseColors(){return(this.parentLogger||this).styleEngine.getUseColors()}forContext(e){return this.childLoggers[e]||(this.childLoggers[e]=new v({},void 0,e,this)),this.childLoggers[e]}group(e){var i;if(!this.isContextEnabled())return;(((i=this.parentLogger)==null?void 0:i.activeGroups)||this.activeGroups).push(e);const s=this.styleEngine.formatMessage(h.info,`Group: ${e}`,this.name,this.context);console.group(`${s} ▼`)}groupEnd(e){var s;if(!this.isContextEnabled())return;const t=((s=this.parentLogger)==null?void 0:s.activeGroups)||this.activeGroups;if(e){const i=t.lastIndexOf(e);if(i!==-1){const a=t.splice(i);for(let n=0;n<a.length;n++)console.groupEnd(),console.log("")}}else t.length>0&&(t.pop(),console.groupEnd(),console.log(""))}trace(e,t,...s){if(!this.isContextEnabled()||this.logLevel>h.trace)return;const{metadata:i,restData:a}=this.styleEngine.parseMetadataParameter(t,s);if(this.handleDeduplication(h.trace,e,i,a))if(i){const n=this.styleEngine.formatMessageWithMetadata(h.trace,e,this.name,i,this.context);console.trace(n,...a||[])}else{const n=this.styleEngine.formatMessage(h.trace,e,this.name,this.context);console.trace(n,...a||[])}}debug(e,t,...s){if(!this.isContextEnabled()||this.logLevel>h.debug)return;const{metadata:i,restData:a}=this.styleEngine.parseMetadataParameter(t,s);if(this.handleDeduplication(h.debug,e,i,a))if(i){const n=this.styleEngine.formatMessageWithMetadata(h.debug,e,this.name,i,this.context);console.debug(n,...a||[])}else{const n=this.styleEngine.formatMessage(h.debug,e,this.name,this.context);console.debug(n,...a||[])}}info(e,t,...s){if(!this.isContextEnabled()||this.logLevel>h.info)return;const{metadata:i,restData:a}=this.styleEngine.parseMetadataParameter(t,s);if(this.handleDeduplication(h.info,e,i,a))if(i){const n=this.styleEngine.formatMessageWithMetadata(h.info,e,this.name,i,this.context);console.info(n,...a||[])}else{const n=this.styleEngine.formatMessage(h.info,e,this.name,this.context);console.info(n,...a||[])}}warn(e,t,...s){if(!this.isContextEnabled()||this.logLevel>h.warn)return;const{metadata:i,restData:a}=this.styleEngine.parseMetadataParameter(t,s);if(this.handleDeduplication(h.warn,e,i,a))if(i){const n=this.styleEngine.formatMessageWithMetadata(h.warn,e,this.name,i,this.context);console.warn(n,...a||[])}else{const n=this.styleEngine.formatMessage(h.warn,e,this.name,this.context);console.warn(n,...a||[])}}error(e,t,s,...i){if(!this.isContextEnabled()||this.logLevel>h.error)return;const{message:a,serializedError:n,context:o,data:c}=this.parseErrorParameters(e,t,s,i),l=c||[];let u,w=l;if(l.length>0){const y=l[l.length-1];y&&typeof y=="object"&&!Array.isArray(y)&&("style"in y||"icon"in y)&&(u=y,w=l.slice(0,-1))}const S=[this.styleEngine.formatMessageWithMetadata(h.error,a,this.name,u,this.context)];n&&S.push(n),o&&S.push(o),console.error(...S,...w)}fatal(e,t,s,...i){if(!this.isContextEnabled()||this.logLevel>h.fatal)return;if(typeof e=="string"&&t&&typeof t=="object"&&!Array.isArray(t)&&("style"in t||"icon"in t)){const y=t,W=this.styleEngine.formatMessageWithMetadata(h.fatal,`FATAL: ${e}`,this.name,y,this.context);console.error(W);return}const{message:a,serializedError:n,context:o,data:c}=this.parseErrorParameters(e,t,s,i),l=c||[];let u,w=l;if(l.length>0){const y=l[l.length-1];y&&typeof y=="object"&&!Array.isArray(y)&&("style"in y||"icon"in y)&&(u=y,w=l.slice(0,-1))}const S=[this.styleEngine.formatMessageWithMetadata(h.fatal,`FATAL: ${a}`,this.name,u,this.context)];n&&S.push(n),o&&S.push(o),console.error(...S,...w)}parseErrorParameters(e,t,s,i=[]){if(e instanceof Error&&t===void 0)return{message:e.message,serializedError:E.serialize(e),data:[]};if(typeof e=="string"&&t===void 0)return{message:e,data:[]};if(typeof e=="string"&&t instanceof Error&&s===void 0)return{message:e,serializedError:E.serialize(t),data:[]};if(typeof e=="string"&&this.isLogContext(t)&&s===void 0)return{message:e,context:t,data:[]};if(typeof e=="string"&&t instanceof Error&&this.isLogContext(s))return{message:e,serializedError:E.serialize(t),context:s,data:i};const a=typeof e=="string"?e:"Unknown error",n=t?E.serialize(t):void 0,o=s!==void 0?[s,...i]:i;return{message:a,serializedError:n,data:o}}isLogContext(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)&&!(e instanceof Error)&&!(e instanceof Date)&&!(e instanceof RegExp)&&typeof e!="function"}castLoglevel(e){if(R(e))switch(e){case"trace":return h.trace;case"debug":return h.debug;case"info":return h.info;case"warn":return h.warn;case"error":return h.error;case"fatal":return h.fatal;case"silent":return h.silent}const t=e.toLowerCase();throw R(t)?new T(`Invalid log level: ${e}. Log levels are case-sensitive. Valid levels: trace, debug, info, warn, error, fatal, silent.`):new T(`Invalid log level: ${e}. Valid levels: trace, debug, info, warn, error, fatal, silent.`)}safeFormatMessage(e,t,s,i="",a=""){try{return this.styleEngine.formatMessage(e,t,s,i,a)}catch(n){throw this.error("Style engine failure",n),new T("Style engine failure")}}shouldLogImmediately(e,t){const i=(this.parentLogger||this).deduplicator;if(!i)return!0;try{return i.addMessage(e,t,this.context)}catch(a){return console.error("Logger deduplication error:",a),!0}}handleDeduplication(e,t,s,i){return!s&&i.length===0?this.shouldLogImmediately(e,t):!0}};v.INJECTABLE=!0;let A=v,I=null;function z(){return I||(I=new Z),I}class Z{constructor(){this.serviceMap=new Map}register(e,...t){if(this.isServiceInjectable(e)&&(!this.hasService(e)||this.getService(e)===void 0)){if(t===void 0||t.length===0){this.serviceMap.set(this.getInjcectableName(e),new e);return}this.serviceMap.set(this.getInjcectableName(e),new e(...t))}}registerAsUndefined(e){if(this.isServiceInjectable(e))this.serviceMap.set(this.getInjcectableName(e),void 0);else throw new Error(`Service with token ${this.getInjcectableName(e)} is not injectable. Ensure it has the INJECTABLE static property set to true.`)}registerCustomServiceInstance(e,t){if(this.isServiceInjectable(e))this.serviceMap.set(this.getInjcectableName(e),t);else throw new Error(`Service with token ${this.getInjcectableName(e)} is not injectable. Ensure it has the INJECTABLE static property set to true.`)}getService(e){if(this.isServiceInjectable(e))return this.hasService(e)||this.register(e),this.serviceMap.get(this.getInjcectableName(e))}hasService(e){return this.serviceMap.has(this.getInjcectableName(e))}isServiceInjectable(e){const t=e.INJECTABLE;return t!==void 0&&t!==!1}getInjcectableName(e){if(this.isServiceInjectable(e)){const t=e.INJECTABLE;return typeof t!="string"?e.name:t}throw new Error(`Service with token ${e.name} is not injectable. Ensure it has the INJECTABLE static property set to true.`)}destroy(){this.serviceMap.clear()}}function X(r,e,t={}){const{required:s=!0}=t,i=r.getService(e);if(!i&&s)throw new Error(`Service with token ${e||"unknown"} not found in registry`);return i}function p(r,e={}){return X(z(),r,e)}function N(r,...e){z().register(r,...e)}const L=class L{constructor(e){this.storageConfig=e,this.logger=p(A).forContext("SessionService")}async initSession(e){if(d.getSession(e))this.logger.debug("Session already exists, skipping initialization");else{if(this.logger.info("Creating new session"),!this.store)if(this.storageConfig.type==="redis"){const s={...this.storageConfig.config};if("url"in s)this.store=d.createRedisStore(s);else if("host"in s){const i={...s,port:typeof s.port=="string"?parseInt(s.port,10):s.port};this.store=d.createRedisStore(i)}else throw new Error("Invalid Redis configuration: must provide either url or host/port")}else this.store=d.createMemoryStore();await d.useSession(e,{store:this.store,secret:this.storageConfig.config.sessionSecret||"change-me-in-production",name:"auth.session",maxAge:3600*24,cookie:{httpOnly:!0,secure:process.env.NODE_ENV==="production",sameSite:"lax"},generate:()=>({auth:{isAuthenticated:!1}})})}}async getSession(e){try{const t=await this.store.getItem(e);return t?{id:e,data:t,save:async()=>{await this.store.setItem(e,t)}}:null}catch(t){return this.logger.error("Error retrieving session",t,{sessionId:e}),null}}async getActiveSessions(){try{const e=await this.store.getKeys();return(await Promise.all(e.map(async s=>{const i=await this.store.getItem(s);return i?{id:this.storageConfig.config.prefix&&s.startsWith(`${this.storageConfig.config.prefix}:`)?s.substring(`${this.storageConfig.config.prefix}:`.length):s,data:i,update:n=>{const o=n(i);Object.assign(i,o)},save:async()=>{await this.store.setItem(s,i)}}:null}))).filter(s=>s!==null)}catch(e){return this.logger.error("Error retrieving active sessions",e),[]}}async destroyAuthSession(e){try{await this.initSession(e);const t=d.getSession(e);t!=null&&t.auth&&await d.updateSession(e,s=>{const i={...s};return delete i.auth,i}),await d.destroySession(e)}catch(t){throw this.logger.error("Session destruction failed",t),g.createError({statusCode:500,message:"Session handling failed"})}}async getSessionData(e,t){await this.initSession(e);const s=d.getSession(e);return this.logger.debug("Retrieved session data",s),(s==null?void 0:s[t])||null}async setSessionData(e,t,s){await this.initSession(e),await d.updateSession(e,i=>({...i,[t]:s}))}async isValidSession(e){return await this.initSession(e),!!d.getSession(e)}};L.INJECTABLE=!0;let k=L;const M=class M{constructor(e){this.openIDConfigCache=null,this.configLastFetched=null,this.CONFIG_CACHE_TTL=36e5,this.TOKEN_REFRESH_SAFETY_MARGIN=300,this.logger=p(A).forContext("OAuthAuthenticationService"),N(k,e.sessionStorage),this.config=e}validateConfiguration(){if(!this.config.issuer||!this.config.clientId||!this.config.clientSecret||!this.config.callbackUri)throw new Error("OAuth Authentication Service not properly initialized. Make sure to call AnalogAuth() with valid configuration before using authentication features.")}async initSession(e){return await p(k).initSession(e)}getConfig(){return this.validateConfiguration(),this.config}getConfigValue(e,t){const s=this.config[e];if(s===void 0||typeof s=="string"&&s===""){if(t!==void 0)return t;if(e==="userHandler"||e==="logoutUrl"||e==="audience")return;if(e==="unprotectedRoutes")return[];throw new Error(`Configuration value for '${e}' doesn't exist`)}return s}isUnprotectedRoute(e){const t=this.getConfigValue("unprotectedRoutes",[]);return Array.isArray(t)&&t.some(s=>e.startsWith(s))}async getAuthorizationUrl(e,t){this.validateConfiguration();const s=await this.getOpenIDConfiguration(),i=this.getConfigValue("audience",void 0),a={response_type:"code",client_id:this.getConfigValue("clientId"),redirect_uri:t||this.getConfigValue("callbackUri"),scope:this.getConfigValue("scope"),state:e,...i?{audience:i}:{}},n=new URLSearchParams(a);return`${s.authorization_endpoint}?${n.toString()}`}async exchangeCodeForTokens(e,t){const s=await this.getOpenIDConfiguration(),i=await fetch(s.token_endpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:this.getConfigValue("clientId"),client_secret:this.getConfigValue("clientSecret"),code:e,redirect_uri:t||this.getConfigValue("callbackUri")}).toString()});if(!i.ok){const a=await i.json();throw this.logger.error("Error exchanging code for tokens",a),g.createError({statusCode:401,message:"Failed to exchange authorization code"})}return await i.json()}async refreshTokens(e){const t=await this.getOpenIDConfiguration();try{const s=await fetch(t.token_endpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",client_id:this.getConfigValue("clientId"),client_secret:this.getConfigValue("clientSecret"),refresh_token:e}).toString()});if(!s.ok){const i=await s.json().catch(()=>({error:"Unknown error"}));throw this.logger.error("Error refreshing token",i),g.createError({statusCode:401,message:"Failed to refresh token"})}return await s.json()}catch(s){throw this.logger.error("Error during token refresh",s),g.createError({statusCode:401,message:"Failed to refresh authentication token"})}}async getUserInfo(e,t=3){const s=await this.getOpenIDConfiguration();let i=null;for(let a=1;a<=t;a++)try{const n=await fetch(s.userinfo_endpoint,{headers:{Authorization:`Bearer ${e}`},signal:AbortSignal.timeout(1e4)});if(!n.ok){const c=await n.json().catch(()=>({error:"Unknown error"}));if(this.logger.error("Error getting user info",c,{attempt:a,maxRetries:t}),n.status===401)throw g.createError({statusCode:401,message:"Authentication token is invalid or expired"});if(n.status===429){const l=parseInt(n.headers.get("Retry-After")||"5",10);await new Promise(u=>setTimeout(u,l*1e3));continue}else if(n.status>=500){await new Promise(l=>setTimeout(l,a*1e3));continue}else throw g.createError({statusCode:n.status,message:`Failed to get user info: ${c.error||"Unknown error"}`})}const o=await n.json();if(!o||!o.sub&&!o.id)throw g.createError({statusCode:500,message:"Invalid user data received from provider"});return o}catch(n){if(i=n,n instanceof Error&&"statusCode"in n&&n.statusCode===401)throw n;if((n instanceof TypeError||n instanceof Error&&n.name==="AbortError")&&(this.logger.error("Network error fetching user info",n,{attempt:a,maxRetries:t}),a<t)){await new Promise(o=>setTimeout(o,Math.pow(2,a)*500));continue}if(a===t)throw this.logger.error("Failed to get user info after multiple attempts",{maxRetries:t}),g.createError({statusCode:500,message:"Failed to get user info after multiple attempts",cause:i})}throw g.createError({statusCode:500,message:"Unexpected error getting user info"})}async handleCallback(e,t,s){var S;if(!s)throw g.createError({statusCode:400,message:"Invalid state parameter"});const i=await this.exchangeCodeForTokens(t),{access_token:a,id_token:n,refresh_token:o,expires_in:c}=i,l=await this.getUserInfo(a),u=this.getConfigValue("userHandler",void 0);let w=l;u&&"createOrUpdateUser"in u&&(w=await((S=u.createOrUpdateUser)==null?void 0:S.call(u,l)));const b={isAuthenticated:!0,accessToken:a,idToken:n,refreshToken:o,expiresAt:Date.now()+c*1e3,userInfo:l};return await d.updateSession(e,()=>({user:w,auth:b})),this.logger.debug("Authentication session data saved successfully"),{user:w,tokens:i}}shouldRefreshToken(e){const t=Date.now(),s=this.TOKEN_REFRESH_SAFETY_MARGIN*1e3;return t+s>e}async refreshExpiringTokens(){var e;this.logger.debug("Starting bulk token refresh process");try{const s=await p(k).getActiveSessions();let i=0,a=0;const n=s.length;this.logger.debug(`Found ${n} active sessions to check`);for(const c of s)try{if(!((e=c.data.auth)!=null&&e.isAuthenticated)||!c.data.auth.refreshToken||!c.data.auth.expiresAt){this.logger.debug(`Skipping session ${c.id} - no valid auth data`);continue}if(!this.shouldRefreshToken(c.data.auth.expiresAt)){this.logger.debug(`Skipping session ${c.id} - token not expiring soon`);continue}this.logger.debug(`Refreshing token for session ${c.id}`);const l=await this.refreshTokens(c.data.auth.refreshToken);c.update(u=>{const w=u.auth;return w?{...u,auth:{...w,accessToken:l.access_token,idToken:l.id_token||w.idToken,refreshToken:l.refresh_token||w.refreshToken,expiresAt:Date.now()+l.expires_in*1e3}}:u}),await c.save(),i++,this.logger.debug(`Successfully refreshed token for session ${c.id}`)}catch(l){a++,this.logger.error("Failed to refresh token for session",l,{sessionId:c.id});try{c.update(u=>{const w=u.auth;return w?{...u,auth:{...w,isAuthenticated:!1}}:u}),await c.save()}catch(u){this.logger.error("Failed to update session after refresh failure",u,{sessionId:c.id})}}const o={refreshed:i,failed:a,total:n};return this.logger.info("Bulk token refresh completed",o),o}catch(t){throw this.logger.error("Error during bulk token refresh",t),g.createError({statusCode:500,message:"Failed to refresh expiring tokens"})}}async isAuthenticated(e){await p(k).initSession(e);const t=await d.getSession(e);if(!(t!=null&&t.auth))return await d.updateSession(e,()=>({auth:{isAuthenticated:!1}})),!1;if(!t.auth.isAuthenticated)return!1;if(t.auth.expiresAt&&t.auth.expiresAt<Date.now())if(t.auth.refreshToken)try{const s=await this.refreshTokens(t.auth.refreshToken);return await d.updateSession(e,i=>{var a,n;return{auth:{...i.auth,isAuthenticated:!0,accessToken:s.access_token,idToken:s.id_token||((a=t==null?void 0:t.auth)==null?void 0:a.idToken),refreshToken:s.refresh_token||((n=t==null?void 0:t.auth)==null?void 0:n.refreshToken),expiresAt:Date.now()+s.expires_in*1e3}}}),!0}catch(s){return this.logger.error("Error refreshing token",s),await d.updateSession(e,i=>({auth:{...i.auth,isAuthenticated:!1}})),this.logger.info("error occurred while refreshing token"),this.logger.groupEnd("OAuthAuthenticationService.isAuthenticated "+e.path),!1}else return this.logger.info(" No refresh token available"),this.logger.groupEnd("OAuthAuthenticationService.isAuthenticated "+e.path),!1;return t.auth.expiresAt&&this.shouldRefreshToken(t.auth.expiresAt)&&setTimeout(async()=>{var s;try{const i=await d.getSession(e);if(!((s=i==null?void 0:i.auth)!=null&&s.isAuthenticated)||!i.auth.refreshToken)return;const a=await this.refreshTokens(i.auth.refreshToken);await d.updateSession(e,n=>{var o,c;return{auth:{...n.auth,isAuthenticated:!0,accessToken:a.access_token,idToken:a.id_token||((o=i==null?void 0:i.auth)==null?void 0:o.idToken),refreshToken:a.refresh_token||((c=i==null?void 0:i.auth)==null?void 0:c.refreshToken),expiresAt:Date.now()+a.expires_in*1e3}}}),this.logger.debug("Background token refresh completed")}catch(i){this.logger.error("Background token refresh failed",i)}},0),!0}async getAuthenticatedUser(e){var i,a,n;if(!await this.isAuthenticated(e))return null;const t=d.getSession(e),s=this.getConfigValue("userHandler",void 0);return s&&"mapUserToLocal"in s?(a=s.mapUserToLocal)==null?void 0:a.call(s,(i=t.auth)==null?void 0:i.userInfo):(n=t.auth)==null?void 0:n.userInfo}async revokeToken(e){const t=await this.getOpenIDConfiguration();if(!(await fetch(t.revocation_endpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:this.getConfigValue("clientId"),client_secret:this.getConfigValue("clientSecret"),token:e})})).ok)throw new Error("Failed to revoke token")}async logout(e){var n,o;await p(k).initSession(e);const t=d.getSession(e),s=await this.getOpenIDConfiguration();if((n=t==null?void 0:t.auth)!=null&&n.accessToken)try{await this.revokeToken(t.auth.accessToken)}catch(c){this.logger.error("Failed to revoke access token",c)}if((o=t==null?void 0:t.auth)!=null&&o.refreshToken)try{await this.revokeToken(t.auth.refreshToken)}catch(c){this.logger.error("Failed to revoke refresh token",c)}const i=new URL(s.end_session_endpoint);i.searchParams.append("client_id",this.getConfigValue("clientId"));const a=this.getConfigValue("logoutUrl");return a&&i.searchParams.append("returnTo",a),await d.updateSession(e,()=>({auth:{isAuthenticated:!1},user:null})),i.toString()}async getOpenIDConfiguration(){const e=Date.now();if(this.openIDConfigCache&&this.configLastFetched&&e-this.configLastFetched<this.CONFIG_CACHE_TTL)return this.openIDConfigCache;try{const t=await fetch(`${this.getConfigValue("issuer")}/.well-known/openid-configuration`);if(!t.ok)throw new Error(`Failed to fetch OpenID configuration: ${t.statusText}`);const s=await t.json();return this.openIDConfigCache=s,this.configLastFetched=e,s}catch(t){throw this.logger.error("Error fetching OpenID configuration",t),g.createError({statusCode:500,message:"Failed to fetch OpenID configuration"})}}};M.INJECTABLE=!0;let m=M;const U={path:"authenticated",handler:async r=>{const t={authenticated:await p(m).isAuthenticated(r)};return p(A).forContext("AuthMiddleware").info("User authentication status checked",t),t}},F={path:"callback",handler:async r=>{const e=p(m);if(await e.initSession(r),await e.isAuthenticated(r))return g.sendRedirect(r,"/");const t=g.getQuery(r),s=t.code,i=t.state,a=d.getSession(r),n=a==null?void 0:a.state;if(!i||!n||i!==n)throw g.createError({statusCode:400,message:"Invalid or missing state parameter. Authentication flow may have been tampered with.",statusMessage:"Authorization Failed"});await d.updateSession(r,l=>{const u={...l};return delete u.state,u}),await e.handleCallback(r,s,i);const o=d.getSession(r),c=(o==null?void 0:o.redirectUrl)||"/";return await d.updateSession(r,l=>{const u={...l};return delete u.redirectUrl,u}),g.sendRedirect(r,c)}},Q=globalThis.crypto,ee=()=>Q.randomUUID(),D={path:"login",handler:async r=>{const e=p(m);await e.initSession(r);const t=ee();await d.updateSession(r,n=>({...n,state:t}));const i=g.getQuery(r).redirect_uri,a=await e.getAuthorizationUrl(t,i);return g.sendRedirect(r,a)}},P={path:"logout",handler:async r=>{const e=p(A).forContext("LogoutRoute");try{const t=p(m);await t.initSession(r);const s=await t.logout(r);return g.sendRedirect(r,s)}catch(t){throw e.error("Logout failed",t),g.createError({statusCode:500,message:"Logout failed"})}}},$={path:"protected-data",handler:async r=>({message:"This is protected data that requires authentication",user:await p(m).getAuthenticatedUser(r)})},j={path:"refresh-tokens",handler:async r=>{const e=p(A).forContext("TokenRefresh"),t=p(m),s=t.getConfig().tokenRefreshApiKey;if(!s)throw e.error("Token refresh API key not configured in either AnalogAuthConfig.tokenRefreshApiKey or TOKEN_REFRESH_API_KEY env variable"),g.createError({statusCode:500,message:"Server configuration error"});const i=g.getRequestHeaders(r).authorization;if(!i||`Bearer ${s}`!==i)throw e.warn("Unauthorized token refresh attempt"),g.createError({statusCode:401,message:"Unauthorized"});try{const a=await t.refreshExpiringTokens();return e.info("Token refresh job completed",{refreshed:a.refreshed,failed:a.failed,total:a.total}),{success:!0,...a}}catch(a){throw e.error("Error in token refresh job",a),g.createError({statusCode:500,message:"Failed to refresh tokens"})}}},V={path:"user",handler:async r=>{const e=p(m);if(await e.initSession(r),!await e.isAuthenticated(r))throw g.createError({statusCode:401,message:"Unauthorized"});return e.getAuthenticatedUser(r)}};function te(){return{[U.path]:U.handler,[F.path]:F.handler,[D.path]:D.handler,[P.path]:P.handler,[$.path]:$.handler,[j.path]:j.handler,[V.path]:V.handler}}function se(r){return new URL(r,"http://dummy-base").pathname.split("/").filter(Boolean).pop()??""}async function ie(r){if(r.path.includes("/api/auth/")){const e=se(r.path);if(!e)throw g.createError({statusCode:400,statusMessage:"Missing path parameter"});await p(m).initSession(r);const s=te();if(s[e])return s[e](r);throw g.createError({statusCode:404,statusMessage:`Authentication route '${e}' not found`})}}async function H(r){const e=p(m);return await e.initSession(r),e.isAuthenticated(r)}async function re(r){const e=g.getRequestURL(r).pathname,t=p(m),s=p(A).forContext("AuthMiddleware");if(s.info("Processing authentication middleware",e),e.startsWith("/api/auth/login")||e.startsWith("/api/auth/callback")||e.startsWith("/api/auth/authenticated")||t.isUnprotectedRoute(e)||e.startsWith("/api/trpc"))return;const i=g.getHeader(r,"fetch");if(g.getHeader(r,"ssr")!=="true"&&(await t.initSession(r),!await H(r))){if(i==="true")throw new K.TRPCError({code:"UNAUTHORIZED",message:"User is not authenticated"});s.debug("Redirecting to login page",{path:e}),await g.sendRedirect(r,"/api/auth/login")}if(i==="true")return{name:"TrpcError",code:"NOT_IMPLEMENTED",message:"SSR is not supported for this route"}}async function ae(r,e){const s=["issuer","clientId","clientSecret","callbackUri"].filter(i=>!r[i]);if(s.length>0)throw new Error(`AnalogAuth initialization failed: Missing mandatory configuration values: ${s.join(", ")}`);return N(m,r),await re(e),ie(e)}exports.checkAuthentication=H;exports.useAnalogAuth=ae;
//# sourceMappingURL=index.cjs.map