UNPKG

edge-utils

Version:

Platform-agnostic utilities for edge computing and serverless environments

3 lines (2 loc) 7.63 kB
const e=require("jsonwebtoken"),t=require("crypto");module.exports={JWTManager:class{constructor(e={}){this.secret=e.secret||t.randomBytes(32).toString("hex"),this.algorithm=e.algorithm||"HS256",this.issuer=e.issuer,this.audience=e.audience,this.expiration=e.expiration||"1h",this.clockTolerance=e.clockTolerance||30,this.refreshExpiration=e.refreshExpiration||"7d",this.revocationStore=e.revocationStore||new Set}generate(t,s={}){const r={...t,iat:Math.floor(Date.now()/1e3),exp:Math.floor(Date.now()/1e3)+this._parseExpiration(s.expiration||this.expiration)};return this.issuer&&(r.iss=this.issuer),this.audience&&(r.aud=this.audience),e.sign(r,this.secret,{algorithm:s.algorithm||this.algorithm})}generateRefreshToken(t){const s={...t,type:"refresh",iat:Math.floor(Date.now()/1e3),exp:Math.floor(Date.now()/1e3)+this._parseExpiration(this.refreshExpiration)};return e.sign(s,this.secret,{algorithm:this.algorithm})}verify(t,s={}){try{if(this.revocationStore.has(t))return{valid:!1,error:"Token revoked"};const r=e.verify(t,this.secret,{algorithms:[s.algorithm||this.algorithm],issuer:s.issuer||this.issuer,audience:s.audience||this.audience,clockTolerance:s.clockTolerance||this.clockTolerance,ignoreExpiration:s.ignoreExpiration||!1});return s.customValidator&&!s.customValidator(r)?{valid:!1,error:"Custom validation failed"}:{valid:!0,payload:r}}catch(e){return{valid:!1,error:e.message}}}refresh(e){const t=this.verify(e);if(!t.valid)return{valid:!1,error:t.error};if("refresh"!==t.payload.type)return{valid:!1,error:"Invalid refresh token"};const{type:s,iat:r,exp:i,...a}=t.payload;return{valid:!0,accessToken:this.generate(a)}}revoke(e){this.revocationStore.add(e)}isRevoked(e){return this.revocationStore.has(e)}decode(t){try{return e.decode(t)}catch(e){return null}}validateForTenant(e,t){const s=this.verify(e);return s.valid&&s.payload.tenant!==t?{valid:!1,error:"Invalid tenant"}:s}_parseExpiration(e){if("number"==typeof e)return e;const t=e.match(/^(\d+)([smhd])$/);if(!t)return 3600;const s=parseInt(t[1]);switch(t[2]){case"s":return s;case"m":return 60*s;case"h":return 3600*s;case"d":return 86400*s;default:return 3600}}},APIKeyManager:class{constructor(e={}){this.keys=new Map,this.usage=new Map,this.storage=e.storage,this.hmacSecret=e.hmacSecret||t.randomBytes(32).toString("hex"),this.rotationWindow=e.rotationWindow||2592e6,this.auditLog=e.auditLog||[]}generate(e={}){const s=t.randomBytes(8).toString("hex"),r=t.randomBytes(32).toString("hex"),i=`${s}:${r}`,a=`ak_${s}_${t.createHmac("sha256",this.hmacSecret).update(i).digest("hex")}`;return this.keys.set(a,{...e,secret:r,created:Date.now(),lastUsed:null,usage:0,permissions:e.permissions||[],expires:e.expires||null,quota:e.quota||null}),a}validate(e){const s=this.keys.get(e);if(!s)return this._logAudit("invalid_key",{apiKey:e.substring(0,10)+"..."}),{valid:!1,error:"Invalid API key"};if(s.expires&&Date.now()>s.expires)return this._logAudit("expired_key",{keyId:e.split("_")[1]}),{valid:!1,error:"API key expired"};if(s.quota&&s.usage>=s.quota.limit)return this._logAudit("quota_exceeded",{keyId:e.split("_")[1]}),{valid:!1,error:"API key quota exceeded"};const r=e.split("_");if(3!==r.length||"ak"!==r[0])return{valid:!1,error:"Invalid API key format"};const i=r[1],a=r[2],o=this._getSecretForKeyId(i);if(!o)return{valid:!1,error:"Key not found"};const n=`${i}:${o}`,c=t.createHmac("sha256",this.hmacSecret).update(n).digest("hex");return t.timingSafeEqual(Buffer.from(a,"hex"),Buffer.from(c,"hex"))?(s.usage++,s.lastUsed=Date.now(),this.keys.set(e,s),this._logAudit("valid_key",{keyId:i,usage:s.usage}),{valid:!0,metadata:s}):(this._logAudit("invalid_signature",{keyId:i}),{valid:!1,error:"Invalid API key signature"})}rotate(e){const t=this.keys.get(e);if(!t)return null;const s=this.generate(t);return t.rotateTo=s,t.rotateExpires=Date.now()+this.rotationWindow,this.keys.set(e,t),s}revoke(e){this.keys.delete(e),this._logAudit("key_revoked",{keyId:e.split("_")[1]})}updatePermissions(e,t){const s=this.keys.get(e);s&&(s.permissions=t,this.keys.set(e,s))}getUsage(e){const t=this.keys.get(e);return t?{usage:t.usage,lastUsed:t.lastUsed,quota:t.quota}:null}listKeys(){const e=[];for(const[t,s]of this.keys)e.push({key:t.substring(0,15)+"...",created:s.created,lastUsed:s.lastUsed,usage:s.usage,permissions:s.permissions});return e}getAuditLog(){return[...this.auditLog]}_getSecretForKeyId(e){for(const[t,s]of this.keys)if(t.includes(e))return s.secret;return null}_logAudit(e,t){this.auditLog.push({timestamp:Date.now(),event:e,...t}),this.auditLog.length>1e3&&this.auditLog.shift()}},EdgeSessionManager:class{constructor(e={}){this.secret=e.secret||t.randomBytes(32).toString("hex"),this.storage=e.storage||new Map,this.ttl=e.ttl||864e5,this.slidingExpiration=!1!==e.slidingExpiration,this.compression=!1!==e.compression,this.maxConcurrentSessions=e.maxConcurrentSessions||5,this.cookieOptions={name:e.cookieName||"session",httpOnly:!1!==e.httpOnly,secure:!1!==e.secure,sameSite:e.sameSite||"strict",path:e.path||"/",...e.cookieOptions}}async create(e,s){if(s){const e=await this._getUserSessions(s);if(e.length>=this.maxConcurrentSessions){const t=e.sort((e,t)=>e.created-t.created)[0];await this.destroy(t.id)}}const r=t.randomBytes(16).toString("hex"),i={id:r,userId:s,data:this.compression?this._compress(e):e,created:Date.now(),lastAccessed:Date.now(),expires:Date.now()+this.ttl};return await this._store(i),r}async get(e){const t=await this._retrieve(e);return t?Date.now()>t.expires?(await this.destroy(e),null):(this.slidingExpiration&&(t.lastAccessed=Date.now(),t.expires=Date.now()+this.ttl,await this._store(t)),this.compression?this._decompress(t.data):t.data):null}async update(e,t){const s=await this._retrieve(e);if(!s)return;const r={...this.compression?this._decompress(s.data):s.data,...t};s.data=this.compression?this._compress(r):r,s.lastAccessed=Date.now(),await this._store(s)}async destroy(e){await this._remove(e)}async destroyUserSessions(e){const t=await this._getUserSessions(e);for(const e of t)await this.destroy(e.id)}generateToken(e){const s=`${e}:${Date.now()}`,r=t.createCipher("aes-256-cbc",this.secret);let i=r.update(s,"utf8","hex");return i+=r.final("hex"),i}verifyToken(e){try{const s=t.createDecipher("aes-256-cbc",this.secret);let r=s.update(e,"hex","utf8");r+=s.final("utf8");const[i,a]=r.split(":");return Date.now()-parseInt(a)>3e5?null:i}catch(e){return null}}getCookieOptions(e){return{...this.cookieOptions,value:e,maxAge:Math.floor(this.ttl/1e3)}}middleware(){return async(e,t)=>{const s=this._extractSessionToken(e);if(!s)return null;const r=this.verifyToken(s);if(!r)return null;const i=await this.get(r);return i?(t.session={id:r,data:i},null):null}}_extractSessionToken(e){const t=this._parseCookies(e.headers?.cookie||"");if(t[this.cookieOptions.name])return t[this.cookieOptions.name];const s=e.headers?.authorization;return s&&s.startsWith("Session ")?s.substring(8):null}_parseCookies(e){const t={};return e?(e.split(";").forEach(e=>{const[s,r]=e.trim().split("=");s&&r&&(t[s]=decodeURIComponent(r))}),t):t}async _store(e){const t=`session:${e.id}`;try{const s=JSON.stringify(e);await(this.storage.put?.(t,s))||this.storage.set(t,s)}catch(e){}}async _retrieve(e){const t=`session:${e}`;try{const e=await(this.storage.get?.(t))||this.storage.get(t);return e?JSON.parse(e):null}catch(e){return null}}async _remove(e){const t=`session:${e}`;try{await(this.storage.delete?.(t))||this.storage.delete(t)}catch(e){}}async _getUserSessions(e){const t=[];try{return t}catch(e){return[]}}_compress(e){return JSON.stringify(e)}_decompress(e){return JSON.parse(e)}}}; //# sourceMappingURL=auth.esm.js.map