@warlock.js/auth 4.0.31 โ 4.0.39
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/cjs/index.js +8 -1
- package/cjs/index.js.map +1 -1
- package/esm/index.js +8 -1
- package/esm/index.js.map +1 -1
- package/package.json +48 -34
- package/cjs/commands/auth-cleanup-command.d.ts +0 -10
- package/cjs/commands/auth-cleanup-command.d.ts.map +0 -1
- package/cjs/commands/auth-cleanup-command.js +0 -29
- package/cjs/commands/auth-cleanup-command.js.map +0 -1
- package/cjs/commands/jwt-secret-generator-command.d.ts +0 -2
- package/cjs/commands/jwt-secret-generator-command.d.ts.map +0 -1
- package/cjs/commands/jwt-secret-generator-command.js +0 -7
- package/cjs/commands/jwt-secret-generator-command.js.map +0 -1
- package/cjs/contracts/auth-contract.d.ts +0 -23
- package/cjs/contracts/auth-contract.d.ts.map +0 -1
- package/cjs/contracts/index.d.ts +0 -3
- package/cjs/contracts/index.d.ts.map +0 -1
- package/cjs/contracts/types.d.ts +0 -134
- package/cjs/contracts/types.d.ts.map +0 -1
- package/cjs/contracts/types.js +0 -20
- package/cjs/contracts/types.js.map +0 -1
- package/cjs/index.d.ts +0 -8
- package/cjs/index.d.ts.map +0 -1
- package/cjs/middleware/auth.middleware.d.ts +0 -2
- package/cjs/middleware/auth.middleware.d.ts.map +0 -1
- package/cjs/middleware/auth.middleware.js +0 -73
- package/cjs/middleware/auth.middleware.js.map +0 -1
- package/cjs/middleware/index.d.ts +0 -2
- package/cjs/middleware/index.d.ts.map +0 -1
- package/cjs/models/access-token/access-token.d.ts +0 -12
- package/cjs/models/access-token/access-token.d.ts.map +0 -1
- package/cjs/models/access-token/access-token.js +0 -16
- package/cjs/models/access-token/access-token.js.map +0 -1
- package/cjs/models/access-token/index.d.ts +0 -3
- package/cjs/models/access-token/index.d.ts.map +0 -1
- package/cjs/models/access-token/migration.d.ts +0 -3
- package/cjs/models/access-token/migration.d.ts.map +0 -1
- package/cjs/models/access-token/migration.js +0 -10
- package/cjs/models/access-token/migration.js.map +0 -1
- package/cjs/models/auth.d.ts +0 -47
- package/cjs/models/auth.d.ts.map +0 -1
- package/cjs/models/auth.js +0 -58
- package/cjs/models/auth.js.map +0 -1
- package/cjs/models/casts/cast-password.d.ts +0 -7
- package/cjs/models/casts/cast-password.d.ts.map +0 -1
- package/cjs/models/casts/cast-password.js +0 -9
- package/cjs/models/casts/cast-password.js.map +0 -1
- package/cjs/models/casts/index.d.ts +0 -2
- package/cjs/models/casts/index.d.ts.map +0 -1
- package/cjs/models/index.d.ts +0 -5
- package/cjs/models/index.d.ts.map +0 -1
- package/cjs/models/refresh-token/index.d.ts +0 -2
- package/cjs/models/refresh-token/index.d.ts.map +0 -1
- package/cjs/models/refresh-token/migration.d.ts +0 -3
- package/cjs/models/refresh-token/migration.d.ts.map +0 -1
- package/cjs/models/refresh-token/refresh-token.d.ts +0 -32
- package/cjs/models/refresh-token/refresh-token.d.ts.map +0 -1
- package/cjs/models/refresh-token/refresh-token.js +0 -52
- package/cjs/models/refresh-token/refresh-token.js.map +0 -1
- package/cjs/services/auth-events.d.ts +0 -84
- package/cjs/services/auth-events.d.ts.map +0 -1
- package/cjs/services/auth-events.js +0 -65
- package/cjs/services/auth-events.js.map +0 -1
- package/cjs/services/auth.service.d.ts +0 -78
- package/cjs/services/auth.service.d.ts.map +0 -1
- package/cjs/services/auth.service.js +0 -265
- package/cjs/services/auth.service.js.map +0 -1
- package/cjs/services/generate-jwt-secret.d.ts +0 -2
- package/cjs/services/generate-jwt-secret.d.ts.map +0 -1
- package/cjs/services/generate-jwt-secret.js +0 -26
- package/cjs/services/generate-jwt-secret.js.map +0 -1
- package/cjs/services/index.d.ts +0 -5
- package/cjs/services/index.d.ts.map +0 -1
- package/cjs/services/jwt.d.ts +0 -23
- package/cjs/services/jwt.d.ts.map +0 -1
- package/cjs/services/jwt.js +0 -39
- package/cjs/services/jwt.js.map +0 -1
- package/cjs/utils/auth-error-codes.d.ts +0 -18
- package/cjs/utils/auth-error-codes.d.ts.map +0 -1
- package/cjs/utils/auth-error-codes.js +0 -18
- package/cjs/utils/auth-error-codes.js.map +0 -1
- package/cjs/utils/duration.d.ts +0 -45
- package/cjs/utils/duration.d.ts.map +0 -1
- package/cjs/utils/duration.js +0 -93
- package/cjs/utils/duration.js.map +0 -1
- package/cjs/utils/index.d.ts +0 -3
- package/cjs/utils/index.d.ts.map +0 -1
- package/esm/commands/auth-cleanup-command.d.ts +0 -10
- package/esm/commands/auth-cleanup-command.d.ts.map +0 -1
- package/esm/commands/auth-cleanup-command.js +0 -29
- package/esm/commands/auth-cleanup-command.js.map +0 -1
- package/esm/commands/jwt-secret-generator-command.d.ts +0 -2
- package/esm/commands/jwt-secret-generator-command.d.ts.map +0 -1
- package/esm/commands/jwt-secret-generator-command.js +0 -7
- package/esm/commands/jwt-secret-generator-command.js.map +0 -1
- package/esm/contracts/auth-contract.d.ts +0 -23
- package/esm/contracts/auth-contract.d.ts.map +0 -1
- package/esm/contracts/index.d.ts +0 -3
- package/esm/contracts/index.d.ts.map +0 -1
- package/esm/contracts/types.d.ts +0 -134
- package/esm/contracts/types.d.ts.map +0 -1
- package/esm/contracts/types.js +0 -20
- package/esm/contracts/types.js.map +0 -1
- package/esm/index.d.ts +0 -8
- package/esm/index.d.ts.map +0 -1
- package/esm/middleware/auth.middleware.d.ts +0 -2
- package/esm/middleware/auth.middleware.d.ts.map +0 -1
- package/esm/middleware/auth.middleware.js +0 -73
- package/esm/middleware/auth.middleware.js.map +0 -1
- package/esm/middleware/index.d.ts +0 -2
- package/esm/middleware/index.d.ts.map +0 -1
- package/esm/models/access-token/access-token.d.ts +0 -12
- package/esm/models/access-token/access-token.d.ts.map +0 -1
- package/esm/models/access-token/access-token.js +0 -16
- package/esm/models/access-token/access-token.js.map +0 -1
- package/esm/models/access-token/index.d.ts +0 -3
- package/esm/models/access-token/index.d.ts.map +0 -1
- package/esm/models/access-token/migration.d.ts +0 -3
- package/esm/models/access-token/migration.d.ts.map +0 -1
- package/esm/models/access-token/migration.js +0 -10
- package/esm/models/access-token/migration.js.map +0 -1
- package/esm/models/auth.d.ts +0 -47
- package/esm/models/auth.d.ts.map +0 -1
- package/esm/models/auth.js +0 -58
- package/esm/models/auth.js.map +0 -1
- package/esm/models/casts/cast-password.d.ts +0 -7
- package/esm/models/casts/cast-password.d.ts.map +0 -1
- package/esm/models/casts/cast-password.js +0 -9
- package/esm/models/casts/cast-password.js.map +0 -1
- package/esm/models/casts/index.d.ts +0 -2
- package/esm/models/casts/index.d.ts.map +0 -1
- package/esm/models/index.d.ts +0 -5
- package/esm/models/index.d.ts.map +0 -1
- package/esm/models/refresh-token/index.d.ts +0 -2
- package/esm/models/refresh-token/index.d.ts.map +0 -1
- package/esm/models/refresh-token/migration.d.ts +0 -3
- package/esm/models/refresh-token/migration.d.ts.map +0 -1
- package/esm/models/refresh-token/refresh-token.d.ts +0 -32
- package/esm/models/refresh-token/refresh-token.d.ts.map +0 -1
- package/esm/models/refresh-token/refresh-token.js +0 -52
- package/esm/models/refresh-token/refresh-token.js.map +0 -1
- package/esm/services/auth-events.d.ts +0 -84
- package/esm/services/auth-events.d.ts.map +0 -1
- package/esm/services/auth-events.js +0 -65
- package/esm/services/auth-events.js.map +0 -1
- package/esm/services/auth.service.d.ts +0 -78
- package/esm/services/auth.service.d.ts.map +0 -1
- package/esm/services/auth.service.js +0 -265
- package/esm/services/auth.service.js.map +0 -1
- package/esm/services/generate-jwt-secret.d.ts +0 -2
- package/esm/services/generate-jwt-secret.d.ts.map +0 -1
- package/esm/services/generate-jwt-secret.js +0 -26
- package/esm/services/generate-jwt-secret.js.map +0 -1
- package/esm/services/index.d.ts +0 -5
- package/esm/services/index.d.ts.map +0 -1
- package/esm/services/jwt.d.ts +0 -23
- package/esm/services/jwt.d.ts.map +0 -1
- package/esm/services/jwt.js +0 -39
- package/esm/services/jwt.js.map +0 -1
- package/esm/utils/auth-error-codes.d.ts +0 -18
- package/esm/utils/auth-error-codes.d.ts.map +0 -1
- package/esm/utils/auth-error-codes.js +0 -18
- package/esm/utils/auth-error-codes.js.map +0 -1
- package/esm/utils/duration.d.ts +0 -45
- package/esm/utils/duration.d.ts.map +0 -1
- package/esm/utils/duration.js +0 -93
- package/esm/utils/duration.js.map +0 -1
- package/esm/utils/index.d.ts +0 -3
- package/esm/utils/index.d.ts.map +0 -1
package/cjs/index.js
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var copper=require('@mongez/copper'),core=require('@warlock.js/core'),password=require('@mongez/password'),reinforcements=require('@mongez/reinforcements'),cascade=require('@warlock.js/cascade'),seal=require('@warlock.js/seal'),w=require('@mongez/events'),fastJwt=require('fast-jwt'),fs=require('@mongez/fs'),logger=require('@warlock.js/logger');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var w__default=/*#__PURE__*/_interopDefault(w);var U=seal.v.object({token:seal.v.string().required(),lastAccess:seal.v.date().default(()=>new Date),user:seal.v.object({id:seal.v.number().required(),userType:seal.v.string()}).allowUnknown().required()}),l=class extends cascade.Model{static table="accessTokens";static schema=U};cascade.migrate(l,{name:"accessToken",up(){this.string("accessToken").index(),this.date("lastAccess");},down(){this.dropIndex("token");}});var $=seal.v.object({token:seal.v.string().required(),userId:seal.v.number().required(),userType:seal.v.string().required(),familyId:seal.v.string().required(),expiresAt:seal.v.date().required(),lastUsedAt:seal.v.date().default(()=>new Date),revokedAt:seal.v.date(),deviceInfo:seal.v.record(seal.v.any())}),u=class extends cascade.Model{static table="refreshTokens";static schema=$;get isExpired(){let e=this.get("expiresAt");return e?new Date>new Date(e):false}get isRevoked(){return !!this.get("revokedAt")}get isValid(){return !this.isExpired&&!this.isRevoked}async revoke(){return this.merge({revokedAt:new Date}).save()}async markAsUsed(){await this.merge({lastUsedAt:new Date}).save();}};var I=Symbol("NO_EXPIRATION");function b(t,e=36e5){if(t===void 0)return e;if(t!==I)return typeof t=="number"?t:typeof t=="string"?H(t):L(t)}function L(t){let e=0;return t.milliseconds&&(e+=t.milliseconds),t.seconds&&(e+=t.seconds*1e3),t.minutes&&(e+=t.minutes*60*1e3),t.hours&&(e+=t.hours*60*60*1e3),t.days&&(e+=t.days*24*60*60*1e3),t.weeks&&(e+=t.weeks*7*24*60*60*1e3),e}function H(t){let e=0,r=t.trim().split(/\s+/);for(let o of r){let n=o.match(/^(\d+(?:\.\d+)?)([smhdw])$/i);if(!n)continue;let s=parseFloat(n[1]);switch(n[2].toLowerCase()){case "s":e+=s*1e3;break;case "m":e+=s*60*1e3;break;case "h":e+=s*60*60*1e3;break;case "d":e+=s*24*60*60*1e3;break;case "w":e+=s*7*24*60*60*1e3;break}}return e||36e5}function S(t,e=36e5){let r=b(t,e);if(r!==void 0)return Math.floor(r/1e3)+"s"}var v="auth.",a={on(t,e){return w__default.default.subscribe(v+t,e)},subscribe(t,e){return this.on(t,e)},emit(t,...e){w__default.default.trigger(v+t,...e);},trigger(t,...e){this.emit(t,...e);},unsubscribeAll(){w__default.default.unsubscribeNamespace(v.slice(0,-1));},off(t){t?w__default.default.unsubscribe(v+t):this.unsubscribeAll();}};var j=()=>core.config.key("auth.jwt.secret"),g=()=>core.config.key("auth.jwt.algorithm"),M=()=>core.config.key("auth.jwt.refresh.secret"),X=()=>core.config.key("auth.jwt.refresh.expiresIn"),h={async generate(t,{key:e=j(),algorithm:r=g(),...o}={}){return fastJwt.createSigner({key:e,...o,algorithm:r})({...t})},async verify(t,{key:e=j(),algorithms:r=g()?[g()]:void 0,...o}={}){return await fastJwt.createVerifier({key:e,...o,algorithms:r})(t)},async generateRefreshToken(t,{key:e=M(),expiresIn:r=X(),algorithm:o=g(),...n}={}){return fastJwt.createSigner({key:e,expiresIn:r,algorithm:o,...n})({...t})},async verifyRefreshToken(t,{key:e=M(),algorithms:r=[g()],...o}={}){return await fastJwt.createVerifier({key:e,algorithms:r,...o})(t)}};var x=class{buildAccessTokenPayload(e){return {id:e.id,_id:e.get("_id"),userType:e.userType,createdAt:Date.now()}}async generateAccessToken(e,r){let o=r||this.buildAccessTokenPayload(e),n=core.config.key("auth.jwt.expiresIn"),s=S(n,36e5),i=s?await h.generate(o,{expiresIn:s}):await h.generate(o);return await l.create({token:i,user:o}),i}async createRefreshToken(e,r){let o=r?.familyId||reinforcements.Random.string(32),n=core.config.key("auth.jwt.refresh.expiresIn"),s=b(n,10080*60*1e3),i={userId:e.id,userType:e.userType,familyId:o},p=await h.generateRefreshToken(i);await this.enforceMaxRefreshTokens(e);let f=s?new Date(Date.now()+s):new Date(Date.now()+100*365*24*60*60*1e3);return u.create({token:p,userId:e.id,userType:e.userType,familyId:o,expiresAt:f,deviceInfo:r?{userAgent:r.userAgent,ip:r.ip,deviceId:r.deviceId}:void 0})}async createTokenPair(e,r){let o=await this.generateAccessToken(e,r?.payload),n=await this.createRefreshToken(e,r),s={accessToken:o,refreshToken:n.get("token"),expiresIn:core.config.key("auth.jwt.expiresIn","1h")};return a.emit("token.created",e,s),a.emit("session.created",e,n,r),s}async refreshTokens(e,r){try{let o=await h.verifyRefreshToken(e);if(!o)return null;let n=await u.first({token:e});if(!n?.isValid)return n&&await this.revokeTokenFamily(n.get("familyId")),null;let s=core.config.key(`auth.userType.${o.userType}`);if(!s)return null;let i=await s.find(o.userId);if(!i)return null;core.config.key("auth.jwt.refresh.rotation",!0)?await n.revoke():await n.markAsUsed();let f=await this.createTokenPair(i,{...r,familyId:n.get("familyId")});return a.emit("token.refreshed",i,f,n),f}catch{return null}}verifyPassword(e,r){return password.verify(String(e),String(r))}hashPassword(e){return password.hash(String(e),core.config.key("auth.password.salt",12))}async attemptLogin(e,r){let{password:o,...n}=r;a.emit("login.attempt",n);let s=await e.first(n);return s?this.verifyPassword(s.string("password"),o)?s:(a.emit("login.failed",n,"Invalid password"),null):(a.emit("login.failed",n,"User not found"),null)}async login(e,r,o){let n=await this.attemptLogin(e,r);if(!n)return null;if(!core.config.key("auth.jwt.refresh.enabled",true)){let i=await this.generateAccessToken(n,o?.payload);return {user:n,accessToken:i}}let s=await this.createTokenPair(n,o);return a.emit("login.success",n,s,o),{user:n,tokens:s}}async logout(e,r,o){if(r&&await this.removeAccessToken(e,r),o){let n=await u.first({token:o,userId:e.id});n&&(await n.revoke(),a.emit("session.destroyed",e,n));}else {if(core.config.key("auth.jwt.refresh.logoutWithoutToken","revoke-all")==="error")throw new Error("Refresh token required for logout");await this.revokeAllTokens(e),a.emit("logout.failsafe",e);}a.emit("logout",e);}async removeAccessToken(e,r){l.delete({token:r,"user.id":e.id});}async revokeAllTokens(e){let r=await u.query().where("userId",e.id).where("userType",e.userType).where("revokedAt",null).get();for(let o of r)await o.revoke(),a.emit("token.revoked",e,o);await l.delete({"user.id":e.id,"user.userType":e.userType}),a.emit("logout.all",e);}async revokeTokenFamily(e){let r=await u.query().where("familyId",e).where("revokedAt",null).get();for(let o of r)await o.revoke();a.emit("token.familyRevoked",e,r);}async cleanupExpiredTokens(){let e=await u.query().where("expiresAt","<",new Date).get();for(let r of e)a.emit("token.expired",r),await r.destroy();return a.emit("cleanup.completed",e.length),e.length}async enforceMaxRefreshTokens(e){let r=core.config.key("auth.jwt.refresh.maxPerUser",5),o=await u.query().where("userId",e.id).where("userType",e.userType).where("revokedAt",null).orderBy("createdAt","asc").get();if(o.length>=r){let n=o.slice(0,o.length-r+1);for(let s of n)await s.revoke();}}async getActiveSessions(e){return u.query().where("userId",e.id).where("userType",e.userType).where("revokedAt",null).where("expiresAt",">",new Date).orderBy("createdAt","desc").get()}},c=new x;function ze(){return core.command({name:"auth.cleanup",description:"Remove expired refresh tokens from the database",preload:{env:true,config:["auth","database"],connectors:["database"]},action:async()=>{console.log(copper.colors.cyan("\u{1F9F9} Cleaning up expired tokens..."));let t=await c.cleanupExpiredTokens();console.log(t===0?copper.colors.green("\u2705 No expired tokens found."):copper.colors.green(`\u2705 Removed ${t} expired token(s).`));}})}async function N(){let t=core.rootPath(".env");logger.log.info("jwt","generating","Generating JWT secrets");let e=core.environment();if(await fs.fileExistsAsync(t)||(t=core.rootPath(e==="production"?".env.production":".env.development")),!await fs.fileExistsAsync(t)){logger.log.error("jwt","error",".env file not found");return}let r=await fs.getFileAsync(t),o=r.includes("JWT_SECRET"),n=r.includes("JWT_REFRESH_SECRET");if(o&&n){logger.log.warn("jwt","exists","JWT secrets already exist in the .env file.");return}let s="";if(o)logger.log.info("jwt","exists","JWT_SECRET already exists in the .env file.");else {let i=reinforcements.Random.string(32);s+=`
|
|
2
|
+
# JWT Secret
|
|
3
|
+
JWT_SECRET=${i}
|
|
4
|
+
`,logger.log.success("jwt","generated","JWT_SECRET generated and added to the .env file.");}if(n)logger.log.info("jwt","exists","JWT_REFRESH_SECRET already exists in the .env file.");else {let i=reinforcements.Random.string(32);s+=`
|
|
5
|
+
# JWT Refresh Secret
|
|
6
|
+
JWT_REFRESH_SECRET=${i}
|
|
7
|
+
`,logger.log.success("jwt","generated","JWT_REFRESH_SECRET generated and added to the .env file.");}s&&(r+=s,await fs.putFileAsync(t,r));}function Qe(){return core.command({name:"jwt.generate",description:"Generate JWT Secret key in .env file",action:N})}var O=(o=>(o.MissingAccessToken="EC001",o.InvalidAccessToken="EC002",o.Unauthorized="EC003",o))(O||{});function ct(t){let e=t?Array.isArray(t)?t:[t]:[];return async(o,n)=>{try{let s=o.authorizationValue;if(!e.length&&!s)return;if(!s)return n.unauthorized({error:core.t("auth.errors.missingAccessToken"),errorCode:"EC001"});let i=await h.verify(s);o.decodedAccessToken=i;let p=await l.first({token:s});if(!p)return n.unauthorized({error:core.t("auth.errors.invalidAccessToken"),errorCode:"EC002"});let f=i.userType||p.get("userType");if(e.length&&!e.includes(f))return n.unauthorized({error:core.t("auth.errors.unauthorized"),errorCode:"EC003"});let E=core.config.key(`auth.userType.${f}`);if(!E)throw new Error(`User type ${f} is unknown type.`);let R=await E.find(i.id);if(!R)return p.destroy(),n.unauthorized({error:core.t("auth.errors.invalidAccessToken"),errorCode:"EC002"});p.set("lastAccess",new Date),await p.save({skipEvents:!0}),o.user=R;}catch(s){return logger.log.error("http","auth",s),o.clearCurrentUser(),n.unauthorized({error:core.t("auth.errors.invalidAccessToken"),errorCode:"EC002"})}}}var q=class extends cascade.Model{accessTokenPayload(){return c.buildAccessTokenPayload(this)}async createTokenPair(e){return c.createTokenPair(this,e)}async generateAccessToken(e){return c.generateAccessToken(this,e)}async generateRefreshToken(e){return c.createRefreshToken(this,e)}async removeAccessToken(e){return c.removeAccessToken(this,e)}async revokeAllTokens(){return c.revokeAllTokens(this)}async activeSessions(){return c.getActiveSessions(this)}static async attempt(e){return c.attemptLogin(this,e)}confirmPassword(e){return c.verifyPassword(this.string("password"),e)}};function At(t,e,r){return t?password.hash(String(t),core.config.key("auth.password.salt",12)):r.getInitial(e)}exports.AccessToken=l;exports.Auth=q;exports.AuthErrorCodes=O;exports.NO_EXPIRATION=I;exports.RefreshToken=u;exports.authEvents=a;exports.authMiddleware=ct;exports.authService=c;exports.castPassword=At;exports.generateJWTSecret=N;exports.jwt=h;exports.parseExpirationToMs=b;exports.registerAuthCleanupCommand=ze;exports.registerJWTSecretGeneratorCommand=Qe;exports.toJwtExpiresIn=S;//# sourceMappingURL=index.js.map
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
package/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../warlock.js/auth/src/models/access-token/access-token.ts","../../../../../../../warlock.js/auth/src/models/access-token/migration.ts","../../../../../../../warlock.js/auth/src/models/refresh-token/refresh-token.ts","../../../../../../../warlock.js/auth/src/contracts/types.ts","../../../../../../../warlock.js/auth/src/utils/duration.ts","../../../../../../../warlock.js/auth/src/services/auth-events.ts","../../../../../../../warlock.js/auth/src/services/jwt.ts","../../../../../../../warlock.js/auth/src/services/auth.service.ts","../../../../../../../warlock.js/auth/src/commands/auth-cleanup-command.ts","../../../../../../../warlock.js/auth/src/services/generate-jwt-secret.ts","../../../../../../../warlock.js/auth/src/commands/jwt-secret-generator-command.ts","../../../../../../../warlock.js/auth/src/utils/auth-error-codes.ts","../../../../../../../warlock.js/auth/src/middleware/auth.middleware.ts","../../../../../../../warlock.js/auth/src/models/auth.ts","../../../../../../../warlock.js/auth/src/models/casts/cast-password.ts"],"names":["accessTokenSchema","v","AccessToken","Model","migrate","refreshTokenSchema","RefreshToken","expiresAt","NO_EXPIRATION","parseExpirationToMs","expiration","defaultMs","parseStringDuration","parseDurationObject","duration","ms","str","totalMs","parts","part","match","value","toJwtExpiresIn","AUTH_EVENT_PREFIX","authEvents","event","callback","events","args","getSecretKey","config","getAlgorithm","getRefreshSecretKey","getRefreshTokenValidity","jwt","payload","key","algorithm","options","createSigner","token","algorithms","createVerifier","expiresIn","AuthService","user","data","expiresInConfig","deviceInfo","familyId","Random","expiresInMs","accessToken","refreshToken","tokenPair","refreshTokenString","decoded","UserModel","newTokenPair","hashedPassword","plainPassword","verify","password","hash","otherData","credentials","tokens","refreshTokens","expiredTokens","maxPerUser","activeTokens","tokensToRevoke","authService","registerAuthCleanupCommand","command","colors","count","generateJWTSecret","envFile","rootPath","log","environmentMode","environment","fileExistsAsync","contents","getFileAsync","hasJwtSecret","hasJwtRefreshSecret","secretsToAdd","jwtSecret","jwtRefreshSecret","putFileAsync","registerJWTSecretGeneratorCommand","AuthErrorCodes","authMiddleware","allowedUserType","allowedTypes","request","response","authorizationValue","t","userType","currentUser","err","Auth","castPassword","column","model"],"mappings":"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","file":"index.js","sourcesContent":["import { Model } from \"@warlock.js/cascade\";\r\nimport { v } from \"@warlock.js/seal\";\r\n\r\nconst accessTokenSchema = v.object({\r\n token: v.string().required(),\r\n lastAccess: v.date().default(() => new Date()),\r\n user: v\r\n .object({\r\n id: v.number().required(),\r\n userType: v.string(),\r\n })\r\n .allowUnknown()\r\n .required(),\r\n});\r\n\r\nexport class AccessToken extends Model {\r\n /**\r\n * {@inheritDoc}\r\n */\r\n public static table = \"accessTokens\";\r\n\r\n public static schema = accessTokenSchema;\r\n}\r\n","import { migrate } from \"@warlock.js/cascade\";\r\nimport { AccessToken } from \"./access-token\";\r\n\r\nexport default migrate(AccessToken, {\r\n name: \"accessToken\",\r\n up() {\r\n this.string(\"accessToken\").index();\r\n this.date(\"lastAccess\");\r\n },\r\n down() {\r\n this.dropIndex(\"token\");\r\n },\r\n});\r\n","import { Model } from \"@warlock.js/cascade\";\nimport { v } from \"@warlock.js/seal\";\n\nconst refreshTokenSchema = v.object({\n token: v.string().required(),\n userId: v.number().required(),\n userType: v.string().required(),\n familyId: v.string().required(),\n expiresAt: v.date().required(),\n lastUsedAt: v.date().default(() => new Date()),\n revokedAt: v.date(),\n deviceInfo: v.record(v.any()),\n});\n\nexport class RefreshToken extends Model {\n /**\n * {@inheritDoc}\n */\n public static table = \"refreshTokens\";\n\n /**\n * {@inheritDoc}\n */\n public static schema = refreshTokenSchema;\n\n /**\n * Check if token is expired\n */\n public get isExpired(): boolean {\n const expiresAt = this.get(\"expiresAt\");\n if (!expiresAt) return false;\n return new Date() > new Date(expiresAt);\n }\n\n /**\n * Check if token is revoked\n */\n public get isRevoked(): boolean {\n return !!this.get(\"revokedAt\");\n }\n\n /**\n * Check if token is valid (not expired and not revoked)\n */\n public get isValid(): boolean {\n return !this.isExpired && !this.isRevoked;\n }\n\n /**\n * Revoke this token\n */\n public async revoke(): Promise<this> {\n return this.merge({ revokedAt: new Date() }).save();\n }\n\n /**\n * Mark token as used (update lastUsedAt)\n */\n public async markAsUsed(): Promise<void> {\n await this.merge({ lastUsedAt: new Date() }).save();\n }\n}\n","import { ChildModel } from \"@warlock.js/cascade\";\r\nimport { type Algorithm } from \"fast-jwt\";\r\nimport type { Auth } from \"../models/auth\";\r\nimport type { Duration, ExpiresIn } from \"../utils/duration\";\r\n\r\n/**\r\n * Symbol to indicate no expiration for tokens\r\n * Use this when you explicitly want tokens to never expire\r\n *\r\n * @example\r\n * ```typescript\r\n * // src/config/auth.ts\r\n * import { NO_EXPIRATION, type AuthConfigurations } from \"@warlock.js/auth\";\r\n *\r\n * const authConfigurations: AuthConfigurations = {\r\n * jwt: {\r\n * secret: env(\"JWT_SECRET\"),\r\n * expiresIn: NO_EXPIRATION, // Token never expires\r\n * },\r\n * };\r\n *\r\n * export default authConfigurations;\r\n * ```\r\n */\r\nexport const NO_EXPIRATION = Symbol(\"NO_EXPIRATION\");\r\n\r\n/**\r\n * Behavior when logout is called without a refresh token\r\n * - \"revoke-all\": Revoke all refresh tokens for the user (secure default)\r\n * - \"error\": Return an error requiring the refresh token\r\n */\r\nexport type LogoutWithoutTokenBehavior = \"revoke-all\" | \"error\";\r\n\r\nexport type AuthConfigurations = {\r\n /**\r\n * Define all user types\r\n * This is important to differentiate between user types when validating and generating tokens\r\n */\r\n userType: {\r\n [userType: string]: ChildModel<Auth>;\r\n };\r\n /**\r\n * JWT configurations\r\n */\r\n jwt: {\r\n /**\r\n * JWT secret key for signing access tokens\r\n */\r\n secret: string;\r\n /**\r\n * JWT algorithm\r\n * @default \"HS256\"\r\n */\r\n algorithm?: Algorithm;\r\n /**\r\n * Access token expiration time\r\n * Supports Duration object, string format, or NO_EXPIRATION\r\n * @example { hours: 1 }, { days: 7, hours: 12 }, \"1h\", \"1d 2h\", NO_EXPIRATION\r\n * @default { hours: 1 }\r\n */\r\n expiresIn?: ExpiresIn;\r\n /**\r\n * Refresh token configurations\r\n */\r\n refresh?: {\r\n /**\r\n * Separate secret for refresh tokens (recommended for security)\r\n * If not provided, falls back to main JWT secret\r\n */\r\n secret?: string;\r\n /**\r\n * Enable refresh token\r\n * @default true\r\n */\r\n enabled?: boolean;\r\n /**\r\n * Refresh token expiration time\r\n * Supports Duration object or string format\r\n * @example { days: 7 }, { weeks: 1 }, \"7d\", \"1w\"\r\n * @default { days: 7 }\r\n */\r\n expiresIn?: Duration | string | number;\r\n /**\r\n * Enable token rotation (issue new refresh token on each use)\r\n * Old refresh token is invalidated after use\r\n * @default true\r\n */\r\n rotation?: boolean;\r\n /**\r\n * Maximum number of active refresh tokens per user\r\n * When exceeded, oldest tokens are revoked\r\n * @default 5\r\n */\r\n maxPerUser?: number;\r\n /**\r\n * Behavior when logout is called without a refresh token\r\n * - \"revoke-all\": Revoke all tokens for security (default)\r\n * - \"error\": Require refresh token, return error if missing\r\n * @default \"revoke-all\"\r\n */\r\n logoutWithoutToken?: LogoutWithoutTokenBehavior;\r\n };\r\n };\r\n /**\r\n * Password configurations\r\n */\r\n password?: {\r\n /**\r\n * Password salt\r\n * The higher the salt, the more secure the password is\r\n * But, it will take more time to generate the password\r\n * @default 12\r\n */\r\n salt?: number;\r\n };\r\n};\r\n\r\n/**\r\n * Token pair returned after login or token refresh\r\n */\r\nexport type TokenPair = {\r\n /**\r\n * JWT access token (short-lived)\r\n */\r\n accessToken: string;\r\n /**\r\n * JWT refresh token (long-lived)\r\n */\r\n refreshToken: string;\r\n /**\r\n * Access token expiration time in seconds or time string\r\n */\r\n expiresIn: number | string;\r\n};\r\n\r\n/**\r\n * Device information for session tracking\r\n */\r\nexport type DeviceInfo = {\r\n /**\r\n * User agent string from request\r\n */\r\n userAgent?: string;\r\n /**\r\n * Client IP address\r\n */\r\n ip?: string;\r\n /**\r\n * Optional device identifier\r\n */\r\n deviceId?: string;\r\n /**\r\n * Token family ID (for rotation tracking)\r\n * @internal\r\n */\r\n familyId?: string;\r\n /**\r\n * Access token payload\r\n */\r\n payload?: Record<string, any>;\r\n};\r\n","import { NO_EXPIRATION } from \"../contracts/types\";\n\n/**\n * Duration object for specifying time periods\n * All units are additive (e.g., { days: 1, hours: 6 } = 30 hours)\n *\n * @example\n * ```typescript\n * { hours: 1 } // 1 hour\n * { days: 7, hours: 12 } // 7.5 days\n * { minutes: 30 } // 30 minutes\n * ```\n */\nexport type Duration = {\n milliseconds?: number;\n seconds?: number;\n minutes?: number;\n hours?: number;\n days?: number;\n weeks?: number;\n};\n\n/**\n * Expiration value type - can be a Duration object, string format, or NO_EXPIRATION\n */\nexport type ExpiresIn = Duration | typeof NO_EXPIRATION | string | number;\n\n/**\n * Parse duration to milliseconds\n * Supports Duration object, string format (\"1d 2h 30m\"), or number (raw ms)\n *\n * @example\n * ```typescript\n * parseExpirationToMs({ hours: 1 }) // 3600000\n * parseExpirationToMs({ days: 1 }) // 86400000\n * parseExpirationToMs(\"1h\") // 3600000\n * parseExpirationToMs(\"1d 2h 30m\") // 95400000\n * parseExpirationToMs(3600000) // 3600000\n * parseExpirationToMs(NO_EXPIRATION) // undefined\n * ```\n */\nexport function parseExpirationToMs(\n expiration: ExpiresIn | undefined,\n defaultMs: number = 3600000, // 1 hour\n): number | undefined {\n if (expiration === undefined) {\n return defaultMs;\n }\n\n if (expiration === NO_EXPIRATION) {\n return undefined;\n }\n\n if (typeof expiration === \"number\") {\n return expiration;\n }\n\n if (typeof expiration === \"string\") {\n return parseStringDuration(expiration);\n }\n\n // It's a Duration object\n return parseDurationObject(expiration);\n}\n\n/**\n * Parse a Duration object to milliseconds\n */\nfunction parseDurationObject(duration: Duration): number {\n let ms = 0;\n\n if (duration.milliseconds) ms += duration.milliseconds;\n if (duration.seconds) ms += duration.seconds * 1000;\n if (duration.minutes) ms += duration.minutes * 60 * 1000;\n if (duration.hours) ms += duration.hours * 60 * 60 * 1000;\n if (duration.days) ms += duration.days * 24 * 60 * 60 * 1000;\n if (duration.weeks) ms += duration.weeks * 7 * 24 * 60 * 60 * 1000;\n\n return ms;\n}\n\n/**\n * Parse a string duration to milliseconds\n * Supports formats: \"1h\", \"7d\", \"30m\", \"90s\", \"1d 2h 30m\"\n */\nfunction parseStringDuration(str: string): number {\n let totalMs = 0;\n const parts = str.trim().split(/\\s+/);\n\n for (const part of parts) {\n const match = part.match(/^(\\d+(?:\\.\\d+)?)([smhdw])$/i);\n if (!match) continue;\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n\n switch (unit) {\n case \"s\":\n totalMs += value * 1000;\n break;\n case \"m\":\n totalMs += value * 60 * 1000;\n break;\n case \"h\":\n totalMs += value * 60 * 60 * 1000;\n break;\n case \"d\":\n totalMs += value * 24 * 60 * 60 * 1000;\n break;\n case \"w\":\n totalMs += value * 7 * 24 * 60 * 60 * 1000;\n break;\n }\n }\n\n return totalMs || 3600000; // Default to 1 hour if nothing parsed\n}\n\n/**\n * Convert ExpiresIn to a value suitable for jwt.generate (string or number)\n * Returns undefined if NO_EXPIRATION\n */\nexport function toJwtExpiresIn(\n expiration: ExpiresIn | undefined,\n defaultMs: number = 3600000,\n): string | undefined {\n const ms = parseExpirationToMs(expiration, defaultMs);\n if (ms === undefined) return undefined;\n\n // Convert ms to seconds for JWT (more common format)\n return Math.floor(ms / 1000) + \"s\";\n}\n","import events, { type EventSubscription } from \"@mongez/events\";\nimport type { DeviceInfo, TokenPair } from \"../contracts/types\";\nimport type { Auth } from \"../models/auth\";\nimport type { RefreshToken } from \"../models/refresh-token\";\n\n/**\n * Auth event payload types\n */\nexport type AuthEventPayloads = {\n // Login events\n \"login.success\": [user: Auth, tokenPair: TokenPair, deviceInfo?: DeviceInfo];\n \"login.failed\": [credentials: { email?: string; username?: string }, reason: string];\n \"login.attempt\": [credentials: { email?: string; username?: string }];\n\n // Logout events\n logout: [user: Auth];\n \"logout.all\": [user: Auth];\n \"logout.failsafe\": [user: Auth];\n\n // Token events\n \"token.created\": [user: Auth, tokenPair: TokenPair];\n \"token.refreshed\": [user: Auth, newTokenPair: TokenPair, oldRefreshToken: RefreshToken];\n \"token.revoked\": [user: Auth, token: RefreshToken];\n \"token.expired\": [token: RefreshToken];\n \"token.familyRevoked\": [familyId: string, tokens: RefreshToken[]];\n\n // Password events\n \"password.changed\": [user: Auth];\n \"password.resetRequested\": [user: Auth, resetToken: string];\n \"password.reset\": [user: Auth];\n\n // Session events\n \"session.created\": [user: Auth, refreshToken: RefreshToken, deviceInfo?: DeviceInfo];\n \"session.destroyed\": [user: Auth, refreshToken: RefreshToken];\n\n // Cleanup events\n \"cleanup.completed\": [expiredCount: number];\n};\n\n/**\n * Auth event names\n */\nexport type AuthEventName = keyof AuthEventPayloads;\n\n/**\n * Callback type for a specific event\n */\nexport type AuthEventCallback<T extends AuthEventName> = (\n ...args: AuthEventPayloads[T]\n) => void | Promise<void>;\n\n/**\n * Event namespace prefix for auth events\n */\nconst AUTH_EVENT_PREFIX = \"auth.\";\n\n/**\n * Type-safe auth events manager\n *\n * @example\n * ```typescript\n * // Subscribe to events with full autocomplete\n * authEvents.on(\"login.success\", (user, tokenPair, deviceInfo) => {\n * console.log(`User ${user.id} logged in`);\n * });\n *\n * authEvents.on(\"token.refreshed\", (user, newPair, oldToken) => {\n * console.log(`Token refreshed for user ${user.id}`);\n * });\n *\n * // Trigger events\n * authEvents.emit(\"login.success\", user, tokenPair, deviceInfo);\n * ```\n */\nexport const authEvents = {\n /**\n * Subscribe to an auth event\n */\n on<T extends AuthEventName>(event: T, callback: AuthEventCallback<T>): EventSubscription {\n return events.subscribe(AUTH_EVENT_PREFIX + event, callback as Function);\n },\n\n /**\n * Subscribe to an auth event (alias for `on`)\n */\n subscribe<T extends AuthEventName>(event: T, callback: AuthEventCallback<T>): EventSubscription {\n return this.on(event, callback);\n },\n\n /**\n * Emit an auth event\n */\n emit<T extends AuthEventName>(event: T, ...args: AuthEventPayloads[T]): void {\n events.trigger(AUTH_EVENT_PREFIX + event, ...args);\n },\n\n /**\n * Emit an auth event (alias for `emit`)\n */\n trigger<T extends AuthEventName>(event: T, ...args: AuthEventPayloads[T]): void {\n this.emit(event, ...args);\n },\n\n /**\n * Unsubscribe from all auth events\n */\n unsubscribeAll(): void {\n events.unsubscribeNamespace(AUTH_EVENT_PREFIX.slice(0, -1));\n },\n\n /**\n * Unsubscribe from a specific auth event\n */\n off(event?: AuthEventName): void {\n if (event) {\n events.unsubscribe(AUTH_EVENT_PREFIX + event);\n } else {\n this.unsubscribeAll();\n }\n },\n};\n","import { config } from \"@warlock.js/core\";\r\nimport {\r\n createSigner,\r\n createVerifier,\r\n type Algorithm,\r\n type SignerOptions,\r\n type VerifierOptions,\r\n} from \"fast-jwt\";\r\n\r\nconst getSecretKey = () => config.key(\"auth.jwt.secret\") as string;\r\nconst getAlgorithm = () => config.key(\"auth.jwt.algorithm\") as Algorithm;\r\n\r\nconst getRefreshSecretKey = () => config.key(\"auth.jwt.refresh.secret\") as string;\r\n// Assuming there's a separate config for refresh token validity, for example, '7d' for 7 days\r\nconst getRefreshTokenValidity = () => config.key(\"auth.jwt.refresh.expiresIn\") as number | string;\r\n\r\nexport const jwt = {\r\n /**\r\n * Generate a new JWT token for the user.\r\n * @param payload The payload to encode in the JWT token.\r\n */\r\n async generate(\r\n payload: any,\r\n {\r\n key = getSecretKey(),\r\n algorithm = getAlgorithm(),\r\n ...options\r\n }: SignerOptions & { key?: string } = {},\r\n ): Promise<string> {\r\n // Create a signer function with predefined options\r\n const sign = createSigner({ key, ...options, algorithm });\r\n\r\n return sign({ ...payload });\r\n },\r\n\r\n /**\r\n * Verify the given token.\r\n * @param token The JWT token to verify.\r\n * @returns The decoded token payload if verification is successful.\r\n */\r\n async verify<T = any>(\r\n token: string,\r\n {\r\n key = getSecretKey(),\r\n algorithms = getAlgorithm() ? [getAlgorithm()] : undefined,\r\n ...options\r\n }: VerifierOptions & { key?: string } = {},\r\n ): Promise<T> {\r\n const verify = createVerifier({ key, ...options, algorithms });\r\n\r\n return await verify(token as string);\r\n },\r\n\r\n /**\r\n * Generate a new refresh token for the user.\r\n */\r\n async generateRefreshToken(\r\n payload: any,\r\n {\r\n key = getRefreshSecretKey(),\r\n expiresIn = getRefreshTokenValidity(),\r\n algorithm = getAlgorithm(),\r\n ...options\r\n }: SignerOptions & { key?: string } = {},\r\n ): Promise<string> {\r\n const sign = createSigner({ key, expiresIn, algorithm, ...options });\r\n return sign({ ...payload });\r\n },\r\n\r\n /**\r\n * Verify the given refresh token.\r\n */\r\n async verifyRefreshToken<T = any>(\r\n token: string,\r\n {\r\n key = getRefreshSecretKey(),\r\n algorithms = [getAlgorithm()],\r\n ...options\r\n }: VerifierOptions & { key?: string } = {},\r\n ): Promise<T> {\r\n const verify = createVerifier({ key, algorithms, ...options });\r\n return await verify(token);\r\n },\r\n};\r\n","import { hash, verify } from \"@mongez/password\";\nimport { Random } from \"@mongez/reinforcements\";\nimport type { ChildModel } from \"@warlock.js/cascade\";\nimport { config } from \"@warlock.js/core\";\nimport type { DeviceInfo, TokenPair } from \"../contracts/types\";\nimport { AccessToken } from \"../models/access-token\";\nimport type { Auth } from \"../models/auth\";\nimport { RefreshToken } from \"../models/refresh-token\";\nimport { parseExpirationToMs, toJwtExpiresIn } from \"../utils/duration\";\nimport { authEvents } from \"./auth-events\";\nimport { jwt } from \"./jwt\";\n\nclass AuthService {\n /**\n * Build access token payload from user\n */\n public buildAccessTokenPayload(user: Auth) {\n return {\n id: user.id,\n _id: user.get(\"_id\"),\n userType: user.userType,\n createdAt: Date.now(),\n };\n }\n\n /**\n * Generate access token for user\n */\n public async generateAccessToken(user: Auth, payload?: any): Promise<string> {\n const data = payload || this.buildAccessTokenPayload(user);\n const expiresInConfig = config.key(\"auth.jwt.expiresIn\");\n const expiresIn = toJwtExpiresIn(expiresInConfig, 3600000); // default 1 hour\n\n // If expiresIn is undefined, token never expires\n const token = expiresIn ? await jwt.generate(data, { expiresIn }) : await jwt.generate(data);\n\n // Store in database\n await AccessToken.create({\n token,\n user: data,\n });\n\n return token;\n }\n\n /**\n * Create refresh token for user\n */\n public async createRefreshToken(user: Auth, deviceInfo?: DeviceInfo): Promise<RefreshToken> {\n const familyId = deviceInfo?.familyId || Random.string(32);\n const expiresInConfig = config.key(\"auth.jwt.refresh.expiresIn\");\n const expiresInMs = parseExpirationToMs(expiresInConfig, 7 * 24 * 60 * 60 * 1000); // default 7 days\n\n const payload = {\n userId: user.id,\n userType: user.userType,\n familyId,\n };\n\n const token = await jwt.generateRefreshToken(payload);\n\n // Enforce max tokens per user\n await this.enforceMaxRefreshTokens(user);\n\n // Calculate expiration date (undefined means never expires, but we still set a far future date)\n const expiresAt = expiresInMs\n ? new Date(Date.now() + expiresInMs)\n : new Date(Date.now() + 100 * 365 * 24 * 60 * 60 * 1000);\n\n // Store in database\n return RefreshToken.create({\n token,\n userId: user.id,\n userType: user.userType,\n familyId,\n expiresAt,\n deviceInfo: deviceInfo\n ? {\n userAgent: deviceInfo.userAgent,\n ip: deviceInfo.ip,\n deviceId: deviceInfo.deviceId,\n }\n : undefined,\n });\n }\n\n /**\n * Create both access and refresh tokens\n */\n public async createTokenPair(user: Auth, deviceInfo?: DeviceInfo): Promise<TokenPair> {\n const accessToken = await this.generateAccessToken(user, deviceInfo?.payload);\n const refreshToken = await this.createRefreshToken(user, deviceInfo);\n\n const tokenPair: TokenPair = {\n accessToken,\n refreshToken: refreshToken.get(\"token\"),\n expiresIn: config.key(\"auth.jwt.expiresIn\", \"1h\"),\n };\n\n // Emit events\n authEvents.emit(\"token.created\", user, tokenPair);\n authEvents.emit(\"session.created\", user, refreshToken, deviceInfo);\n\n return tokenPair;\n }\n\n /**\n * Refresh tokens using a refresh token\n */\n public async refreshTokens(\n refreshTokenString: string,\n deviceInfo?: DeviceInfo,\n ): Promise<TokenPair | null> {\n try {\n // 1. Verify JWT signature\n const decoded = await jwt.verifyRefreshToken<{\n userId: number;\n userType: string;\n familyId: string;\n }>(refreshTokenString);\n\n if (!decoded) return null;\n\n // 2. Find token in database\n const refreshToken = await RefreshToken.first({ token: refreshTokenString });\n\n if (!refreshToken?.isValid) {\n // If token was already used (rotation detection), revoke entire family\n if (refreshToken) {\n await this.revokeTokenFamily(refreshToken.get(\"familyId\"));\n }\n return null;\n }\n\n // 3. Get user model and find user\n const UserModel = config.key(`auth.userType.${decoded.userType}`);\n if (!UserModel) return null;\n\n const user = (await UserModel.find(decoded.userId)) as Auth | null;\n if (!user) return null;\n\n // 4. Rotate token if enabled (revoke old token)\n const rotationEnabled = config.key(\"auth.jwt.refresh.rotation\", true);\n if (rotationEnabled) {\n await refreshToken.revoke();\n } else {\n await refreshToken.markAsUsed();\n }\n\n // 5. Generate new token pair (keep same family)\n const newTokenPair = await this.createTokenPair(user, {\n ...deviceInfo,\n familyId: refreshToken.get(\"familyId\"),\n });\n\n // Emit token refreshed event\n authEvents.emit(\"token.refreshed\", user, newTokenPair, refreshToken);\n\n return newTokenPair;\n } catch {\n return null;\n }\n }\n\n /**\n * Verify password\n */\n public verifyPassword(hashedPassword: string, plainPassword: string): boolean {\n return verify(String(hashedPassword), String(plainPassword));\n }\n\n /**\n * Hash password\n */\n public hashPassword(password: string): string {\n return hash(String(password), config.key(\"auth.password.salt\", 12));\n }\n\n /**\n * Attempt to login user with given credentials\n */\n public async attemptLogin(Model: ChildModel<Auth>, data: any): Promise<Auth | null> {\n const { password, ...otherData } = data;\n\n // Emit login attempt event\n authEvents.emit(\"login.attempt\", otherData);\n\n const user = (await Model.first(otherData)) as Auth | null;\n\n if (!user) {\n authEvents.emit(\"login.failed\", otherData, \"User not found\");\n return null;\n }\n\n if (!this.verifyPassword(user.string(\"password\")!, password)) {\n authEvents.emit(\"login.failed\", otherData, \"Invalid password\");\n return null;\n }\n\n return user;\n }\n\n /**\n * Full login flow: validate credentials, create tokens, emit events\n * Returns token pair on success, null on failure\n */\n public async login(\n Model: ChildModel<Auth>,\n credentials: any,\n deviceInfo?: DeviceInfo,\n ): Promise<{ user: Auth; tokens: TokenPair } | null | { user: Auth; accessToken: string }> {\n const user = await this.attemptLogin(Model, credentials);\n\n if (!user) {\n return null;\n }\n\n // if no refresh token in config, then return user and access token only\n if (!config.key(\"auth.jwt.refresh.enabled\", true)) {\n const accessToken = await this.generateAccessToken(user, deviceInfo?.payload);\n return { user, accessToken };\n }\n\n const tokens = await this.createTokenPair(user, deviceInfo);\n\n // Emit login success event\n authEvents.emit(\"login.success\", user, tokens, deviceInfo);\n\n return { user, tokens };\n }\n\n /**\n * Logout user\n * @param user - The authenticated user\n * @param accessToken - Optional access token string to revoke\n * @param refreshToken - Optional refresh token string to revoke\n * If refresh token is not provided, behavior is determined by config:\n * - \"revoke-all\" (default): Revoke ALL refresh tokens for security\n * - \"error\": Throw error requiring refresh token\n */\n public async logout(user: Auth, accessToken?: string, refreshToken?: string): Promise<void> {\n // Remove access token if provided\n if (accessToken) {\n await this.removeAccessToken(user, accessToken);\n }\n\n if (refreshToken) {\n // Revoke specific refresh token\n const token = await RefreshToken.first({\n token: refreshToken,\n userId: user.id, // Security: ensure token belongs to this user\n });\n\n if (token) {\n await token.revoke();\n authEvents.emit(\"session.destroyed\", user, token);\n }\n } else {\n // No refresh token provided - check configured behavior\n const behavior = config.key(\"auth.jwt.refresh.logoutWithoutToken\", \"revoke-all\") as\n | \"revoke-all\"\n | \"error\";\n\n if (behavior === \"error\") {\n throw new Error(\"Refresh token required for logout\");\n }\n\n // Default: revoke-all (fail-safe)\n await this.revokeAllTokens(user);\n authEvents.emit(\"logout.failsafe\", user);\n }\n\n // Emit logout event\n authEvents.emit(\"logout\", user);\n }\n\n /**\n * Remove specific access token\n */\n public async removeAccessToken(user: Auth, token: string): Promise<void> {\n AccessToken.delete({\n token,\n \"user.id\": user.id,\n });\n }\n\n /**\n * Revoke all tokens for a user\n */\n public async revokeAllTokens(user: Auth): Promise<void> {\n // Revoke all refresh tokens\n const refreshTokens = await RefreshToken.query()\n .where(\"userId\", user.id)\n .where(\"userType\", user.userType)\n .where(\"revokedAt\", null)\n .get();\n\n for (const token of refreshTokens) {\n await token.revoke();\n authEvents.emit(\"token.revoked\", user, token);\n }\n\n // Delete all access tokens\n await AccessToken.delete({\n \"user.id\": user.id,\n \"user.userType\": user.userType,\n });\n\n // Emit logout all event\n authEvents.emit(\"logout.all\", user);\n }\n\n /**\n * Revoke entire token family (for rotation breach detection)\n */\n public async revokeTokenFamily(familyId: string): Promise<void> {\n const tokens = await RefreshToken.query()\n .where(\"familyId\", familyId)\n .where(\"revokedAt\", null)\n .get();\n\n for (const token of tokens) {\n await token.revoke();\n }\n\n // Emit family revoked event\n authEvents.emit(\"token.familyRevoked\", familyId, tokens);\n }\n\n /**\n * Cleanup expired tokens\n */\n public async cleanupExpiredTokens(): Promise<number> {\n const expiredTokens = await RefreshToken.query().where(\"expiresAt\", \"<\", new Date()).get();\n\n for (const token of expiredTokens) {\n authEvents.emit(\"token.expired\", token);\n await token.destroy();\n }\n\n // Emit cleanup completed event\n authEvents.emit(\"cleanup.completed\", expiredTokens.length);\n\n return expiredTokens.length;\n }\n\n /**\n * Enforce max refresh tokens per user\n */\n private async enforceMaxRefreshTokens(user: Auth): Promise<void> {\n const maxPerUser = config.key(\"auth.jwt.refresh.maxPerUser\", 5);\n\n const activeTokens = await RefreshToken.query()\n .where(\"userId\", user.id)\n .where(\"userType\", user.userType)\n .where(\"revokedAt\", null)\n .orderBy(\"createdAt\", \"asc\")\n .get();\n\n // Revoke oldest tokens if exceeding limit\n if (activeTokens.length >= maxPerUser) {\n const tokensToRevoke = activeTokens.slice(0, activeTokens.length - maxPerUser + 1);\n for (const token of tokensToRevoke) {\n await token.revoke();\n }\n }\n }\n\n /**\n * Get active sessions for user\n */\n public async getActiveSessions(user: Auth): Promise<RefreshToken[]> {\n return RefreshToken.query()\n .where(\"userId\", user.id)\n .where(\"userType\", user.userType)\n .where(\"revokedAt\", null)\n .where(\"expiresAt\", \">\", new Date())\n .orderBy(\"createdAt\", \"desc\")\n .get();\n }\n}\n\nexport const authService = new AuthService();\n","import { colors } from \"@mongez/copper\";\nimport { command } from \"@warlock.js/core\";\nimport { authService } from \"../services/auth.service\";\n\n/**\n * Register the auth:cleanup CLI command\n *\n * @example\n * ```bash\n * warlock auth:cleanup\n * ```\n */\nexport function registerAuthCleanupCommand() {\n return command({\n name: \"auth.cleanup\",\n description: \"Remove expired refresh tokens from the database\",\n preload: {\n env: true,\n config: [\"auth\", \"database\"],\n connectors: [\"database\"],\n },\n action: async () => {\n console.log(colors.cyan(\"๐งน Cleaning up expired tokens...\"));\n\n const count = await authService.cleanupExpiredTokens();\n\n if (count === 0) {\n console.log(colors.green(\"โ
No expired tokens found.\"));\n } else {\n console.log(colors.green(`โ
Removed ${count} expired token(s).`));\n }\n },\n });\n}\n","import { fileExistsAsync, getFileAsync, putFileAsync } from \"@mongez/fs\";\r\nimport { Random } from \"@mongez/reinforcements\";\r\nimport { environment, rootPath } from \"@warlock.js/core\";\r\nimport { log } from \"@warlock.js/logger\";\r\n\r\nexport async function generateJWTSecret() {\r\n let envFile = rootPath(\".env\");\r\n\r\n log.info(\"jwt\", \"generating\", \"Generating JWT secrets\");\r\n\r\n const environmentMode = environment();\r\n\r\n if (!(await fileExistsAsync(envFile))) {\r\n const envFileType = environmentMode === \"production\" ? \".env.production\" : \".env.development\";\r\n envFile = rootPath(envFileType);\r\n }\r\n\r\n if (!(await fileExistsAsync(envFile))) {\r\n log.error(\"jwt\", \"error\", \".env file not found\");\r\n return;\r\n }\r\n\r\n let contents = await getFileAsync(envFile);\r\n\r\n const hasJwtSecret = contents.includes(\"JWT_SECRET\");\r\n const hasJwtRefreshSecret = contents.includes(\"JWT_REFRESH_SECRET\");\r\n\r\n if (hasJwtSecret && hasJwtRefreshSecret) {\r\n log.warn(\"jwt\", \"exists\", \"JWT secrets already exist in the .env file.\");\r\n return;\r\n }\r\n\r\n let secretsToAdd = \"\";\r\n\r\n if (!hasJwtSecret) {\r\n const jwtSecret = Random.string(32);\r\n secretsToAdd += `\r\n# JWT Secret\r\nJWT_SECRET=${jwtSecret}\r\n`;\r\n log.success(\"jwt\", \"generated\", \"JWT_SECRET generated and added to the .env file.\");\r\n } else {\r\n log.info(\"jwt\", \"exists\", \"JWT_SECRET already exists in the .env file.\");\r\n }\r\n\r\n if (!hasJwtRefreshSecret) {\r\n const jwtRefreshSecret = Random.string(32);\r\n secretsToAdd += `\r\n# JWT Refresh Secret\r\nJWT_REFRESH_SECRET=${jwtRefreshSecret}\r\n`;\r\n log.success(\"jwt\", \"generated\", \"JWT_REFRESH_SECRET generated and added to the .env file.\");\r\n } else {\r\n log.info(\"jwt\", \"exists\", \"JWT_REFRESH_SECRET already exists in the .env file.\");\r\n }\r\n\r\n if (secretsToAdd) {\r\n contents += secretsToAdd;\r\n await putFileAsync(envFile, contents);\r\n }\r\n}\r\n","import { command } from \"@warlock.js/core\";\r\nimport { generateJWTSecret } from \"../services/generate-jwt-secret\";\r\n\r\nexport function registerJWTSecretGeneratorCommand() {\r\n return command({\r\n name: \"jwt.generate\",\r\n description: \"Generate JWT Secret key in .env file\",\r\n action: generateJWTSecret,\r\n });\r\n}\r\n","export enum AuthErrorCodes {\n /**\n * Missing Access Token Error Code EC001\n * EC001 = Missing Access Token\n */\n MissingAccessToken = \"EC001\", // Error Code 001\n /**\n * Invalid Access Token Error Code EC002\n * EC002 = Invalid Access Token\n */\n InvalidAccessToken = \"EC002\", // Error Code 002\n /**\n * Unauthorized Error Code EC003\n * EC003 = Unauthorized\n */\n Unauthorized = \"EC003\", // Error Code 003\n}\n","import { config, t, type Middleware, type Request, type Response } from \"@warlock.js/core\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { AccessToken } from \"../models/access-token\";\r\nimport { jwt } from \"../services/jwt\";\r\nimport { AuthErrorCodes } from \"../utils/auth-error-codes\";\r\n\r\nexport function authMiddleware(allowedUserType?: string | string[]) {\r\n const allowedTypes = !allowedUserType\r\n ? []\r\n : Array.isArray(allowedUserType)\r\n ? allowedUserType\r\n : [allowedUserType];\r\n\r\n const auth: Middleware = async (request: Request, response: Response) => {\r\n try {\r\n const authorizationValue = request.authorizationValue;\r\n\r\n if (!allowedTypes.length && !authorizationValue) return;\r\n\r\n if (!authorizationValue) {\r\n return response.unauthorized({\r\n error: t(\"auth.errors.missingAccessToken\"),\r\n errorCode: AuthErrorCodes.MissingAccessToken,\r\n });\r\n }\r\n\r\n // get current user jwt\r\n const user = await jwt.verify(authorizationValue);\r\n\r\n // store decoded access token object in request object\r\n request.decodedAccessToken = user;\r\n // use our own jwt verify to verify the token\r\n const accessToken = await AccessToken.first({\r\n token: authorizationValue,\r\n });\r\n\r\n if (!accessToken) {\r\n return response.unauthorized({\r\n error: t(\"auth.errors.invalidAccessToken\"),\r\n errorCode: AuthErrorCodes.InvalidAccessToken,\r\n });\r\n }\r\n\r\n // now, we need to get an instance of user using its corresponding model\r\n const userType = user.userType || accessToken.get(\"userType\");\r\n\r\n // check if the user type is allowed\r\n if (allowedTypes.length && !allowedTypes.includes(userType)) {\r\n return response.unauthorized({\r\n error: t(\"auth.errors.unauthorized\"),\r\n errorCode: AuthErrorCodes.Unauthorized,\r\n });\r\n }\r\n\r\n // get user model class\r\n const UserModel = config.key(`auth.userType.${userType}`);\r\n\r\n if (!UserModel) {\r\n throw new Error(`User type ${userType} is unknown type.`);\r\n }\r\n\r\n // get user model instance\r\n const currentUser = await UserModel.find(user.id);\r\n\r\n if (!currentUser) {\r\n accessToken.destroy();\r\n return response.unauthorized({\r\n error: t(\"auth.errors.invalidAccessToken\"),\r\n errorCode: AuthErrorCodes.InvalidAccessToken,\r\n });\r\n }\r\n\r\n // update last access\r\n accessToken.set(\"lastAccess\", new Date());\r\n await accessToken.save({ skipEvents: true });\r\n\r\n // set current user\r\n request.user = currentUser;\r\n } catch (err: any) {\r\n log.error(\"http\", \"auth\", err);\r\n\r\n // unset current user\r\n request.clearCurrentUser();\r\n\r\n return response.unauthorized({\r\n error: t(\"auth.errors.invalidAccessToken\"),\r\n errorCode: AuthErrorCodes.InvalidAccessToken,\r\n });\r\n }\r\n };\r\n\r\n return auth;\r\n}\r\n","import { type ChildModel, Model, type ModelSchema } from \"@warlock.js/cascade\";\r\nimport type { DeviceInfo, TokenPair } from \"../contracts/types\";\r\nimport { authService } from \"../services\";\r\nimport type { RefreshToken } from \"./refresh-token/refresh-token\";\r\n\r\nexport abstract class Auth<Schema extends ModelSchema = ModelSchema> extends Model<Schema> {\r\n /**\r\n * Get user type\r\n */\r\n public abstract get userType(): string;\r\n\r\n /**\r\n * Get access token payload\r\n */\r\n public accessTokenPayload() {\r\n return authService.buildAccessTokenPayload(this);\r\n }\r\n\r\n /**\r\n * Create both access and refresh tokens\r\n */\r\n public async createTokenPair(deviceInfo?: DeviceInfo): Promise<TokenPair> {\r\n return authService.createTokenPair(this, deviceInfo);\r\n }\r\n\r\n /**\r\n * Generate access token\r\n */\r\n public async generateAccessToken(data?: any): Promise<string> {\r\n return authService.generateAccessToken(this, data);\r\n }\r\n\r\n /**\r\n * Generate refresh token\r\n */\r\n public async generateRefreshToken(deviceInfo?: DeviceInfo): Promise<RefreshToken> {\r\n return authService.createRefreshToken(this, deviceInfo);\r\n }\r\n\r\n /**\r\n * Remove current access token\r\n */\r\n public async removeAccessToken(token: string): Promise<void> {\r\n return authService.removeAccessToken(this, token);\r\n }\r\n\r\n /**\r\n * Revoke all tokens (logout from all devices)\r\n */\r\n public async revokeAllTokens(): Promise<void> {\r\n return authService.revokeAllTokens(this);\r\n }\r\n\r\n /**\r\n * Get active sessions\r\n */\r\n public async activeSessions(): Promise<RefreshToken[]> {\r\n return authService.getActiveSessions(this);\r\n }\r\n\r\n /**\r\n * Attempt to login the user\r\n */\r\n public static async attempt(this: ChildModel<Auth>, data: any): Promise<Auth | null> {\r\n return authService.attemptLogin(this, data);\r\n }\r\n\r\n /**\r\n * Confirm password\r\n */\r\n public confirmPassword(password: string): boolean {\r\n return authService.verifyPassword(this.string(\"password\")!, password);\r\n }\r\n}\r\n","import { hash } from \"@mongez/password\";\r\nimport type { Model } from \"@warlock.js/cascade\";\r\nimport { config } from \"@warlock.js/core\";\r\n\r\n/**\r\n * Cast password on model save\r\n * If the password is not changed, keep it as is\r\n */\r\nexport function castPassword(value: any, column: string, model: Model) {\r\n return value\r\n ? hash(String(value), config.key(\"auth.password.salt\", 12))\r\n : model.getInitial(column);\r\n}\r\n"]}
|
package/esm/index.js
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import {colors}from'@mongez/copper';import {config,command,rootPath,environment,t}from'@warlock.js/core';import {verify,hash}from'@mongez/password';import {Random}from'@mongez/reinforcements';import {migrate,Model}from'@warlock.js/cascade';import {v as v$1}from'@warlock.js/seal';import w from'@mongez/events';import {createVerifier,createSigner}from'fast-jwt';import {fileExistsAsync,getFileAsync,putFileAsync}from'@mongez/fs';import {log}from'@warlock.js/logger';var U=v$1.object({token:v$1.string().required(),lastAccess:v$1.date().default(()=>new Date),user:v$1.object({id:v$1.number().required(),userType:v$1.string()}).allowUnknown().required()}),l=class extends Model{static table="accessTokens";static schema=U};migrate(l,{name:"accessToken",up(){this.string("accessToken").index(),this.date("lastAccess");},down(){this.dropIndex("token");}});var $=v$1.object({token:v$1.string().required(),userId:v$1.number().required(),userType:v$1.string().required(),familyId:v$1.string().required(),expiresAt:v$1.date().required(),lastUsedAt:v$1.date().default(()=>new Date),revokedAt:v$1.date(),deviceInfo:v$1.record(v$1.any())}),u=class extends Model{static table="refreshTokens";static schema=$;get isExpired(){let e=this.get("expiresAt");return e?new Date>new Date(e):false}get isRevoked(){return !!this.get("revokedAt")}get isValid(){return !this.isExpired&&!this.isRevoked}async revoke(){return this.merge({revokedAt:new Date}).save()}async markAsUsed(){await this.merge({lastUsedAt:new Date}).save();}};var I=Symbol("NO_EXPIRATION");function b(t,e=36e5){if(t===void 0)return e;if(t!==I)return typeof t=="number"?t:typeof t=="string"?H(t):L(t)}function L(t){let e=0;return t.milliseconds&&(e+=t.milliseconds),t.seconds&&(e+=t.seconds*1e3),t.minutes&&(e+=t.minutes*60*1e3),t.hours&&(e+=t.hours*60*60*1e3),t.days&&(e+=t.days*24*60*60*1e3),t.weeks&&(e+=t.weeks*7*24*60*60*1e3),e}function H(t){let e=0,r=t.trim().split(/\s+/);for(let o of r){let n=o.match(/^(\d+(?:\.\d+)?)([smhdw])$/i);if(!n)continue;let s=parseFloat(n[1]);switch(n[2].toLowerCase()){case "s":e+=s*1e3;break;case "m":e+=s*60*1e3;break;case "h":e+=s*60*60*1e3;break;case "d":e+=s*24*60*60*1e3;break;case "w":e+=s*7*24*60*60*1e3;break}}return e||36e5}function S(t,e=36e5){let r=b(t,e);if(r!==void 0)return Math.floor(r/1e3)+"s"}var v="auth.",a={on(t,e){return w.subscribe(v+t,e)},subscribe(t,e){return this.on(t,e)},emit(t,...e){w.trigger(v+t,...e);},trigger(t,...e){this.emit(t,...e);},unsubscribeAll(){w.unsubscribeNamespace(v.slice(0,-1));},off(t){t?w.unsubscribe(v+t):this.unsubscribeAll();}};var j=()=>config.key("auth.jwt.secret"),g=()=>config.key("auth.jwt.algorithm"),M=()=>config.key("auth.jwt.refresh.secret"),X=()=>config.key("auth.jwt.refresh.expiresIn"),h={async generate(t,{key:e=j(),algorithm:r=g(),...o}={}){return createSigner({key:e,...o,algorithm:r})({...t})},async verify(t,{key:e=j(),algorithms:r=g()?[g()]:void 0,...o}={}){return await createVerifier({key:e,...o,algorithms:r})(t)},async generateRefreshToken(t,{key:e=M(),expiresIn:r=X(),algorithm:o=g(),...n}={}){return createSigner({key:e,expiresIn:r,algorithm:o,...n})({...t})},async verifyRefreshToken(t,{key:e=M(),algorithms:r=[g()],...o}={}){return await createVerifier({key:e,algorithms:r,...o})(t)}};var x=class{buildAccessTokenPayload(e){return {id:e.id,_id:e.get("_id"),userType:e.userType,createdAt:Date.now()}}async generateAccessToken(e,r){let o=r||this.buildAccessTokenPayload(e),n=config.key("auth.jwt.expiresIn"),s=S(n,36e5),i=s?await h.generate(o,{expiresIn:s}):await h.generate(o);return await l.create({token:i,user:o}),i}async createRefreshToken(e,r){let o=r?.familyId||Random.string(32),n=config.key("auth.jwt.refresh.expiresIn"),s=b(n,10080*60*1e3),i={userId:e.id,userType:e.userType,familyId:o},p=await h.generateRefreshToken(i);await this.enforceMaxRefreshTokens(e);let f=s?new Date(Date.now()+s):new Date(Date.now()+100*365*24*60*60*1e3);return u.create({token:p,userId:e.id,userType:e.userType,familyId:o,expiresAt:f,deviceInfo:r?{userAgent:r.userAgent,ip:r.ip,deviceId:r.deviceId}:void 0})}async createTokenPair(e,r){let o=await this.generateAccessToken(e,r?.payload),n=await this.createRefreshToken(e,r),s={accessToken:o,refreshToken:n.get("token"),expiresIn:config.key("auth.jwt.expiresIn","1h")};return a.emit("token.created",e,s),a.emit("session.created",e,n,r),s}async refreshTokens(e,r){try{let o=await h.verifyRefreshToken(e);if(!o)return null;let n=await u.first({token:e});if(!n?.isValid)return n&&await this.revokeTokenFamily(n.get("familyId")),null;let s=config.key(`auth.userType.${o.userType}`);if(!s)return null;let i=await s.find(o.userId);if(!i)return null;config.key("auth.jwt.refresh.rotation",!0)?await n.revoke():await n.markAsUsed();let f=await this.createTokenPair(i,{...r,familyId:n.get("familyId")});return a.emit("token.refreshed",i,f,n),f}catch{return null}}verifyPassword(e,r){return verify(String(e),String(r))}hashPassword(e){return hash(String(e),config.key("auth.password.salt",12))}async attemptLogin(e,r){let{password:o,...n}=r;a.emit("login.attempt",n);let s=await e.first(n);return s?this.verifyPassword(s.string("password"),o)?s:(a.emit("login.failed",n,"Invalid password"),null):(a.emit("login.failed",n,"User not found"),null)}async login(e,r,o){let n=await this.attemptLogin(e,r);if(!n)return null;if(!config.key("auth.jwt.refresh.enabled",true)){let i=await this.generateAccessToken(n,o?.payload);return {user:n,accessToken:i}}let s=await this.createTokenPair(n,o);return a.emit("login.success",n,s,o),{user:n,tokens:s}}async logout(e,r,o){if(r&&await this.removeAccessToken(e,r),o){let n=await u.first({token:o,userId:e.id});n&&(await n.revoke(),a.emit("session.destroyed",e,n));}else {if(config.key("auth.jwt.refresh.logoutWithoutToken","revoke-all")==="error")throw new Error("Refresh token required for logout");await this.revokeAllTokens(e),a.emit("logout.failsafe",e);}a.emit("logout",e);}async removeAccessToken(e,r){l.delete({token:r,"user.id":e.id});}async revokeAllTokens(e){let r=await u.query().where("userId",e.id).where("userType",e.userType).where("revokedAt",null).get();for(let o of r)await o.revoke(),a.emit("token.revoked",e,o);await l.delete({"user.id":e.id,"user.userType":e.userType}),a.emit("logout.all",e);}async revokeTokenFamily(e){let r=await u.query().where("familyId",e).where("revokedAt",null).get();for(let o of r)await o.revoke();a.emit("token.familyRevoked",e,r);}async cleanupExpiredTokens(){let e=await u.query().where("expiresAt","<",new Date).get();for(let r of e)a.emit("token.expired",r),await r.destroy();return a.emit("cleanup.completed",e.length),e.length}async enforceMaxRefreshTokens(e){let r=config.key("auth.jwt.refresh.maxPerUser",5),o=await u.query().where("userId",e.id).where("userType",e.userType).where("revokedAt",null).orderBy("createdAt","asc").get();if(o.length>=r){let n=o.slice(0,o.length-r+1);for(let s of n)await s.revoke();}}async getActiveSessions(e){return u.query().where("userId",e.id).where("userType",e.userType).where("revokedAt",null).where("expiresAt",">",new Date).orderBy("createdAt","desc").get()}},c=new x;function ze(){return command({name:"auth.cleanup",description:"Remove expired refresh tokens from the database",preload:{env:true,config:["auth","database"],connectors:["database"]},action:async()=>{console.log(colors.cyan("\u{1F9F9} Cleaning up expired tokens..."));let t=await c.cleanupExpiredTokens();console.log(t===0?colors.green("\u2705 No expired tokens found."):colors.green(`\u2705 Removed ${t} expired token(s).`));}})}async function N(){let t=rootPath(".env");log.info("jwt","generating","Generating JWT secrets");let e=environment();if(await fileExistsAsync(t)||(t=rootPath(e==="production"?".env.production":".env.development")),!await fileExistsAsync(t)){log.error("jwt","error",".env file not found");return}let r=await getFileAsync(t),o=r.includes("JWT_SECRET"),n=r.includes("JWT_REFRESH_SECRET");if(o&&n){log.warn("jwt","exists","JWT secrets already exist in the .env file.");return}let s="";if(o)log.info("jwt","exists","JWT_SECRET already exists in the .env file.");else {let i=Random.string(32);s+=`
|
|
2
|
+
# JWT Secret
|
|
3
|
+
JWT_SECRET=${i}
|
|
4
|
+
`,log.success("jwt","generated","JWT_SECRET generated and added to the .env file.");}if(n)log.info("jwt","exists","JWT_REFRESH_SECRET already exists in the .env file.");else {let i=Random.string(32);s+=`
|
|
5
|
+
# JWT Refresh Secret
|
|
6
|
+
JWT_REFRESH_SECRET=${i}
|
|
7
|
+
`,log.success("jwt","generated","JWT_REFRESH_SECRET generated and added to the .env file.");}s&&(r+=s,await putFileAsync(t,r));}function Qe(){return command({name:"jwt.generate",description:"Generate JWT Secret key in .env file",action:N})}var O=(o=>(o.MissingAccessToken="EC001",o.InvalidAccessToken="EC002",o.Unauthorized="EC003",o))(O||{});function ct(t$1){let e=t$1?Array.isArray(t$1)?t$1:[t$1]:[];return async(o,n)=>{try{let s=o.authorizationValue;if(!e.length&&!s)return;if(!s)return n.unauthorized({error:t("auth.errors.missingAccessToken"),errorCode:"EC001"});let i=await h.verify(s);o.decodedAccessToken=i;let p=await l.first({token:s});if(!p)return n.unauthorized({error:t("auth.errors.invalidAccessToken"),errorCode:"EC002"});let f=i.userType||p.get("userType");if(e.length&&!e.includes(f))return n.unauthorized({error:t("auth.errors.unauthorized"),errorCode:"EC003"});let E=config.key(`auth.userType.${f}`);if(!E)throw new Error(`User type ${f} is unknown type.`);let R=await E.find(i.id);if(!R)return p.destroy(),n.unauthorized({error:t("auth.errors.invalidAccessToken"),errorCode:"EC002"});p.set("lastAccess",new Date),await p.save({skipEvents:!0}),o.user=R;}catch(s){return log.error("http","auth",s),o.clearCurrentUser(),n.unauthorized({error:t("auth.errors.invalidAccessToken"),errorCode:"EC002"})}}}var q=class extends Model{accessTokenPayload(){return c.buildAccessTokenPayload(this)}async createTokenPair(e){return c.createTokenPair(this,e)}async generateAccessToken(e){return c.generateAccessToken(this,e)}async generateRefreshToken(e){return c.createRefreshToken(this,e)}async removeAccessToken(e){return c.removeAccessToken(this,e)}async revokeAllTokens(){return c.revokeAllTokens(this)}async activeSessions(){return c.getActiveSessions(this)}static async attempt(e){return c.attemptLogin(this,e)}confirmPassword(e){return c.verifyPassword(this.string("password"),e)}};function At(t,e,r){return t?hash(String(t),config.key("auth.password.salt",12)):r.getInitial(e)}export{l as AccessToken,q as Auth,O as AuthErrorCodes,I as NO_EXPIRATION,u as RefreshToken,a as authEvents,ct as authMiddleware,c as authService,At as castPassword,N as generateJWTSecret,h as jwt,b as parseExpirationToMs,ze as registerAuthCleanupCommand,Qe as registerJWTSecretGeneratorCommand,S as toJwtExpiresIn};//# sourceMappingURL=index.js.map
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
package/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../warlock.js/auth/src/models/access-token/access-token.ts","../../../../../../../warlock.js/auth/src/models/access-token/migration.ts","../../../../../../../warlock.js/auth/src/models/refresh-token/refresh-token.ts","../../../../../../../warlock.js/auth/src/contracts/types.ts","../../../../../../../warlock.js/auth/src/utils/duration.ts","../../../../../../../warlock.js/auth/src/services/auth-events.ts","../../../../../../../warlock.js/auth/src/services/jwt.ts","../../../../../../../warlock.js/auth/src/services/auth.service.ts","../../../../../../../warlock.js/auth/src/commands/auth-cleanup-command.ts","../../../../../../../warlock.js/auth/src/services/generate-jwt-secret.ts","../../../../../../../warlock.js/auth/src/commands/jwt-secret-generator-command.ts","../../../../../../../warlock.js/auth/src/utils/auth-error-codes.ts","../../../../../../../warlock.js/auth/src/middleware/auth.middleware.ts","../../../../../../../warlock.js/auth/src/models/auth.ts","../../../../../../../warlock.js/auth/src/models/casts/cast-password.ts"],"names":["accessTokenSchema","v","AccessToken","Model","migrate","refreshTokenSchema","RefreshToken","expiresAt","NO_EXPIRATION","parseExpirationToMs","expiration","defaultMs","parseStringDuration","parseDurationObject","duration","ms","str","totalMs","parts","part","match","value","toJwtExpiresIn","AUTH_EVENT_PREFIX","authEvents","event","callback","events","args","getSecretKey","config","getAlgorithm","getRefreshSecretKey","getRefreshTokenValidity","jwt","payload","key","algorithm","options","createSigner","token","algorithms","createVerifier","expiresIn","AuthService","user","data","expiresInConfig","deviceInfo","familyId","Random","expiresInMs","accessToken","refreshToken","tokenPair","refreshTokenString","decoded","UserModel","newTokenPair","hashedPassword","plainPassword","verify","password","hash","otherData","credentials","tokens","refreshTokens","expiredTokens","maxPerUser","activeTokens","tokensToRevoke","authService","registerAuthCleanupCommand","command","colors","count","generateJWTSecret","envFile","rootPath","log","environmentMode","environment","fileExistsAsync","contents","getFileAsync","hasJwtSecret","hasJwtRefreshSecret","secretsToAdd","jwtSecret","jwtRefreshSecret","putFileAsync","registerJWTSecretGeneratorCommand","AuthErrorCodes","authMiddleware","allowedUserType","allowedTypes","request","response","authorizationValue","t","userType","currentUser","err","Auth","castPassword","column","model"],"mappings":"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","file":"index.js","sourcesContent":["import { Model } from \"@warlock.js/cascade\";\r\nimport { v } from \"@warlock.js/seal\";\r\n\r\nconst accessTokenSchema = v.object({\r\n token: v.string().required(),\r\n lastAccess: v.date().default(() => new Date()),\r\n user: v\r\n .object({\r\n id: v.number().required(),\r\n userType: v.string(),\r\n })\r\n .allowUnknown()\r\n .required(),\r\n});\r\n\r\nexport class AccessToken extends Model {\r\n /**\r\n * {@inheritDoc}\r\n */\r\n public static table = \"accessTokens\";\r\n\r\n public static schema = accessTokenSchema;\r\n}\r\n","import { migrate } from \"@warlock.js/cascade\";\r\nimport { AccessToken } from \"./access-token\";\r\n\r\nexport default migrate(AccessToken, {\r\n name: \"accessToken\",\r\n up() {\r\n this.string(\"accessToken\").index();\r\n this.date(\"lastAccess\");\r\n },\r\n down() {\r\n this.dropIndex(\"token\");\r\n },\r\n});\r\n","import { Model } from \"@warlock.js/cascade\";\nimport { v } from \"@warlock.js/seal\";\n\nconst refreshTokenSchema = v.object({\n token: v.string().required(),\n userId: v.number().required(),\n userType: v.string().required(),\n familyId: v.string().required(),\n expiresAt: v.date().required(),\n lastUsedAt: v.date().default(() => new Date()),\n revokedAt: v.date(),\n deviceInfo: v.record(v.any()),\n});\n\nexport class RefreshToken extends Model {\n /**\n * {@inheritDoc}\n */\n public static table = \"refreshTokens\";\n\n /**\n * {@inheritDoc}\n */\n public static schema = refreshTokenSchema;\n\n /**\n * Check if token is expired\n */\n public get isExpired(): boolean {\n const expiresAt = this.get(\"expiresAt\");\n if (!expiresAt) return false;\n return new Date() > new Date(expiresAt);\n }\n\n /**\n * Check if token is revoked\n */\n public get isRevoked(): boolean {\n return !!this.get(\"revokedAt\");\n }\n\n /**\n * Check if token is valid (not expired and not revoked)\n */\n public get isValid(): boolean {\n return !this.isExpired && !this.isRevoked;\n }\n\n /**\n * Revoke this token\n */\n public async revoke(): Promise<this> {\n return this.merge({ revokedAt: new Date() }).save();\n }\n\n /**\n * Mark token as used (update lastUsedAt)\n */\n public async markAsUsed(): Promise<void> {\n await this.merge({ lastUsedAt: new Date() }).save();\n }\n}\n","import { ChildModel } from \"@warlock.js/cascade\";\r\nimport { type Algorithm } from \"fast-jwt\";\r\nimport type { Auth } from \"../models/auth\";\r\nimport type { Duration, ExpiresIn } from \"../utils/duration\";\r\n\r\n/**\r\n * Symbol to indicate no expiration for tokens\r\n * Use this when you explicitly want tokens to never expire\r\n *\r\n * @example\r\n * ```typescript\r\n * // src/config/auth.ts\r\n * import { NO_EXPIRATION, type AuthConfigurations } from \"@warlock.js/auth\";\r\n *\r\n * const authConfigurations: AuthConfigurations = {\r\n * jwt: {\r\n * secret: env(\"JWT_SECRET\"),\r\n * expiresIn: NO_EXPIRATION, // Token never expires\r\n * },\r\n * };\r\n *\r\n * export default authConfigurations;\r\n * ```\r\n */\r\nexport const NO_EXPIRATION = Symbol(\"NO_EXPIRATION\");\r\n\r\n/**\r\n * Behavior when logout is called without a refresh token\r\n * - \"revoke-all\": Revoke all refresh tokens for the user (secure default)\r\n * - \"error\": Return an error requiring the refresh token\r\n */\r\nexport type LogoutWithoutTokenBehavior = \"revoke-all\" | \"error\";\r\n\r\nexport type AuthConfigurations = {\r\n /**\r\n * Define all user types\r\n * This is important to differentiate between user types when validating and generating tokens\r\n */\r\n userType: {\r\n [userType: string]: ChildModel<Auth>;\r\n };\r\n /**\r\n * JWT configurations\r\n */\r\n jwt: {\r\n /**\r\n * JWT secret key for signing access tokens\r\n */\r\n secret: string;\r\n /**\r\n * JWT algorithm\r\n * @default \"HS256\"\r\n */\r\n algorithm?: Algorithm;\r\n /**\r\n * Access token expiration time\r\n * Supports Duration object, string format, or NO_EXPIRATION\r\n * @example { hours: 1 }, { days: 7, hours: 12 }, \"1h\", \"1d 2h\", NO_EXPIRATION\r\n * @default { hours: 1 }\r\n */\r\n expiresIn?: ExpiresIn;\r\n /**\r\n * Refresh token configurations\r\n */\r\n refresh?: {\r\n /**\r\n * Separate secret for refresh tokens (recommended for security)\r\n * If not provided, falls back to main JWT secret\r\n */\r\n secret?: string;\r\n /**\r\n * Enable refresh token\r\n * @default true\r\n */\r\n enabled?: boolean;\r\n /**\r\n * Refresh token expiration time\r\n * Supports Duration object or string format\r\n * @example { days: 7 }, { weeks: 1 }, \"7d\", \"1w\"\r\n * @default { days: 7 }\r\n */\r\n expiresIn?: Duration | string | number;\r\n /**\r\n * Enable token rotation (issue new refresh token on each use)\r\n * Old refresh token is invalidated after use\r\n * @default true\r\n */\r\n rotation?: boolean;\r\n /**\r\n * Maximum number of active refresh tokens per user\r\n * When exceeded, oldest tokens are revoked\r\n * @default 5\r\n */\r\n maxPerUser?: number;\r\n /**\r\n * Behavior when logout is called without a refresh token\r\n * - \"revoke-all\": Revoke all tokens for security (default)\r\n * - \"error\": Require refresh token, return error if missing\r\n * @default \"revoke-all\"\r\n */\r\n logoutWithoutToken?: LogoutWithoutTokenBehavior;\r\n };\r\n };\r\n /**\r\n * Password configurations\r\n */\r\n password?: {\r\n /**\r\n * Password salt\r\n * The higher the salt, the more secure the password is\r\n * But, it will take more time to generate the password\r\n * @default 12\r\n */\r\n salt?: number;\r\n };\r\n};\r\n\r\n/**\r\n * Token pair returned after login or token refresh\r\n */\r\nexport type TokenPair = {\r\n /**\r\n * JWT access token (short-lived)\r\n */\r\n accessToken: string;\r\n /**\r\n * JWT refresh token (long-lived)\r\n */\r\n refreshToken: string;\r\n /**\r\n * Access token expiration time in seconds or time string\r\n */\r\n expiresIn: number | string;\r\n};\r\n\r\n/**\r\n * Device information for session tracking\r\n */\r\nexport type DeviceInfo = {\r\n /**\r\n * User agent string from request\r\n */\r\n userAgent?: string;\r\n /**\r\n * Client IP address\r\n */\r\n ip?: string;\r\n /**\r\n * Optional device identifier\r\n */\r\n deviceId?: string;\r\n /**\r\n * Token family ID (for rotation tracking)\r\n * @internal\r\n */\r\n familyId?: string;\r\n /**\r\n * Access token payload\r\n */\r\n payload?: Record<string, any>;\r\n};\r\n","import { NO_EXPIRATION } from \"../contracts/types\";\n\n/**\n * Duration object for specifying time periods\n * All units are additive (e.g., { days: 1, hours: 6 } = 30 hours)\n *\n * @example\n * ```typescript\n * { hours: 1 } // 1 hour\n * { days: 7, hours: 12 } // 7.5 days\n * { minutes: 30 } // 30 minutes\n * ```\n */\nexport type Duration = {\n milliseconds?: number;\n seconds?: number;\n minutes?: number;\n hours?: number;\n days?: number;\n weeks?: number;\n};\n\n/**\n * Expiration value type - can be a Duration object, string format, or NO_EXPIRATION\n */\nexport type ExpiresIn = Duration | typeof NO_EXPIRATION | string | number;\n\n/**\n * Parse duration to milliseconds\n * Supports Duration object, string format (\"1d 2h 30m\"), or number (raw ms)\n *\n * @example\n * ```typescript\n * parseExpirationToMs({ hours: 1 }) // 3600000\n * parseExpirationToMs({ days: 1 }) // 86400000\n * parseExpirationToMs(\"1h\") // 3600000\n * parseExpirationToMs(\"1d 2h 30m\") // 95400000\n * parseExpirationToMs(3600000) // 3600000\n * parseExpirationToMs(NO_EXPIRATION) // undefined\n * ```\n */\nexport function parseExpirationToMs(\n expiration: ExpiresIn | undefined,\n defaultMs: number = 3600000, // 1 hour\n): number | undefined {\n if (expiration === undefined) {\n return defaultMs;\n }\n\n if (expiration === NO_EXPIRATION) {\n return undefined;\n }\n\n if (typeof expiration === \"number\") {\n return expiration;\n }\n\n if (typeof expiration === \"string\") {\n return parseStringDuration(expiration);\n }\n\n // It's a Duration object\n return parseDurationObject(expiration);\n}\n\n/**\n * Parse a Duration object to milliseconds\n */\nfunction parseDurationObject(duration: Duration): number {\n let ms = 0;\n\n if (duration.milliseconds) ms += duration.milliseconds;\n if (duration.seconds) ms += duration.seconds * 1000;\n if (duration.minutes) ms += duration.minutes * 60 * 1000;\n if (duration.hours) ms += duration.hours * 60 * 60 * 1000;\n if (duration.days) ms += duration.days * 24 * 60 * 60 * 1000;\n if (duration.weeks) ms += duration.weeks * 7 * 24 * 60 * 60 * 1000;\n\n return ms;\n}\n\n/**\n * Parse a string duration to milliseconds\n * Supports formats: \"1h\", \"7d\", \"30m\", \"90s\", \"1d 2h 30m\"\n */\nfunction parseStringDuration(str: string): number {\n let totalMs = 0;\n const parts = str.trim().split(/\\s+/);\n\n for (const part of parts) {\n const match = part.match(/^(\\d+(?:\\.\\d+)?)([smhdw])$/i);\n if (!match) continue;\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n\n switch (unit) {\n case \"s\":\n totalMs += value * 1000;\n break;\n case \"m\":\n totalMs += value * 60 * 1000;\n break;\n case \"h\":\n totalMs += value * 60 * 60 * 1000;\n break;\n case \"d\":\n totalMs += value * 24 * 60 * 60 * 1000;\n break;\n case \"w\":\n totalMs += value * 7 * 24 * 60 * 60 * 1000;\n break;\n }\n }\n\n return totalMs || 3600000; // Default to 1 hour if nothing parsed\n}\n\n/**\n * Convert ExpiresIn to a value suitable for jwt.generate (string or number)\n * Returns undefined if NO_EXPIRATION\n */\nexport function toJwtExpiresIn(\n expiration: ExpiresIn | undefined,\n defaultMs: number = 3600000,\n): string | undefined {\n const ms = parseExpirationToMs(expiration, defaultMs);\n if (ms === undefined) return undefined;\n\n // Convert ms to seconds for JWT (more common format)\n return Math.floor(ms / 1000) + \"s\";\n}\n","import events, { type EventSubscription } from \"@mongez/events\";\nimport type { DeviceInfo, TokenPair } from \"../contracts/types\";\nimport type { Auth } from \"../models/auth\";\nimport type { RefreshToken } from \"../models/refresh-token\";\n\n/**\n * Auth event payload types\n */\nexport type AuthEventPayloads = {\n // Login events\n \"login.success\": [user: Auth, tokenPair: TokenPair, deviceInfo?: DeviceInfo];\n \"login.failed\": [credentials: { email?: string; username?: string }, reason: string];\n \"login.attempt\": [credentials: { email?: string; username?: string }];\n\n // Logout events\n logout: [user: Auth];\n \"logout.all\": [user: Auth];\n \"logout.failsafe\": [user: Auth];\n\n // Token events\n \"token.created\": [user: Auth, tokenPair: TokenPair];\n \"token.refreshed\": [user: Auth, newTokenPair: TokenPair, oldRefreshToken: RefreshToken];\n \"token.revoked\": [user: Auth, token: RefreshToken];\n \"token.expired\": [token: RefreshToken];\n \"token.familyRevoked\": [familyId: string, tokens: RefreshToken[]];\n\n // Password events\n \"password.changed\": [user: Auth];\n \"password.resetRequested\": [user: Auth, resetToken: string];\n \"password.reset\": [user: Auth];\n\n // Session events\n \"session.created\": [user: Auth, refreshToken: RefreshToken, deviceInfo?: DeviceInfo];\n \"session.destroyed\": [user: Auth, refreshToken: RefreshToken];\n\n // Cleanup events\n \"cleanup.completed\": [expiredCount: number];\n};\n\n/**\n * Auth event names\n */\nexport type AuthEventName = keyof AuthEventPayloads;\n\n/**\n * Callback type for a specific event\n */\nexport type AuthEventCallback<T extends AuthEventName> = (\n ...args: AuthEventPayloads[T]\n) => void | Promise<void>;\n\n/**\n * Event namespace prefix for auth events\n */\nconst AUTH_EVENT_PREFIX = \"auth.\";\n\n/**\n * Type-safe auth events manager\n *\n * @example\n * ```typescript\n * // Subscribe to events with full autocomplete\n * authEvents.on(\"login.success\", (user, tokenPair, deviceInfo) => {\n * console.log(`User ${user.id} logged in`);\n * });\n *\n * authEvents.on(\"token.refreshed\", (user, newPair, oldToken) => {\n * console.log(`Token refreshed for user ${user.id}`);\n * });\n *\n * // Trigger events\n * authEvents.emit(\"login.success\", user, tokenPair, deviceInfo);\n * ```\n */\nexport const authEvents = {\n /**\n * Subscribe to an auth event\n */\n on<T extends AuthEventName>(event: T, callback: AuthEventCallback<T>): EventSubscription {\n return events.subscribe(AUTH_EVENT_PREFIX + event, callback as Function);\n },\n\n /**\n * Subscribe to an auth event (alias for `on`)\n */\n subscribe<T extends AuthEventName>(event: T, callback: AuthEventCallback<T>): EventSubscription {\n return this.on(event, callback);\n },\n\n /**\n * Emit an auth event\n */\n emit<T extends AuthEventName>(event: T, ...args: AuthEventPayloads[T]): void {\n events.trigger(AUTH_EVENT_PREFIX + event, ...args);\n },\n\n /**\n * Emit an auth event (alias for `emit`)\n */\n trigger<T extends AuthEventName>(event: T, ...args: AuthEventPayloads[T]): void {\n this.emit(event, ...args);\n },\n\n /**\n * Unsubscribe from all auth events\n */\n unsubscribeAll(): void {\n events.unsubscribeNamespace(AUTH_EVENT_PREFIX.slice(0, -1));\n },\n\n /**\n * Unsubscribe from a specific auth event\n */\n off(event?: AuthEventName): void {\n if (event) {\n events.unsubscribe(AUTH_EVENT_PREFIX + event);\n } else {\n this.unsubscribeAll();\n }\n },\n};\n","import { config } from \"@warlock.js/core\";\r\nimport {\r\n createSigner,\r\n createVerifier,\r\n type Algorithm,\r\n type SignerOptions,\r\n type VerifierOptions,\r\n} from \"fast-jwt\";\r\n\r\nconst getSecretKey = () => config.key(\"auth.jwt.secret\") as string;\r\nconst getAlgorithm = () => config.key(\"auth.jwt.algorithm\") as Algorithm;\r\n\r\nconst getRefreshSecretKey = () => config.key(\"auth.jwt.refresh.secret\") as string;\r\n// Assuming there's a separate config for refresh token validity, for example, '7d' for 7 days\r\nconst getRefreshTokenValidity = () => config.key(\"auth.jwt.refresh.expiresIn\") as number | string;\r\n\r\nexport const jwt = {\r\n /**\r\n * Generate a new JWT token for the user.\r\n * @param payload The payload to encode in the JWT token.\r\n */\r\n async generate(\r\n payload: any,\r\n {\r\n key = getSecretKey(),\r\n algorithm = getAlgorithm(),\r\n ...options\r\n }: SignerOptions & { key?: string } = {},\r\n ): Promise<string> {\r\n // Create a signer function with predefined options\r\n const sign = createSigner({ key, ...options, algorithm });\r\n\r\n return sign({ ...payload });\r\n },\r\n\r\n /**\r\n * Verify the given token.\r\n * @param token The JWT token to verify.\r\n * @returns The decoded token payload if verification is successful.\r\n */\r\n async verify<T = any>(\r\n token: string,\r\n {\r\n key = getSecretKey(),\r\n algorithms = getAlgorithm() ? [getAlgorithm()] : undefined,\r\n ...options\r\n }: VerifierOptions & { key?: string } = {},\r\n ): Promise<T> {\r\n const verify = createVerifier({ key, ...options, algorithms });\r\n\r\n return await verify(token as string);\r\n },\r\n\r\n /**\r\n * Generate a new refresh token for the user.\r\n */\r\n async generateRefreshToken(\r\n payload: any,\r\n {\r\n key = getRefreshSecretKey(),\r\n expiresIn = getRefreshTokenValidity(),\r\n algorithm = getAlgorithm(),\r\n ...options\r\n }: SignerOptions & { key?: string } = {},\r\n ): Promise<string> {\r\n const sign = createSigner({ key, expiresIn, algorithm, ...options });\r\n return sign({ ...payload });\r\n },\r\n\r\n /**\r\n * Verify the given refresh token.\r\n */\r\n async verifyRefreshToken<T = any>(\r\n token: string,\r\n {\r\n key = getRefreshSecretKey(),\r\n algorithms = [getAlgorithm()],\r\n ...options\r\n }: VerifierOptions & { key?: string } = {},\r\n ): Promise<T> {\r\n const verify = createVerifier({ key, algorithms, ...options });\r\n return await verify(token);\r\n },\r\n};\r\n","import { hash, verify } from \"@mongez/password\";\nimport { Random } from \"@mongez/reinforcements\";\nimport type { ChildModel } from \"@warlock.js/cascade\";\nimport { config } from \"@warlock.js/core\";\nimport type { DeviceInfo, TokenPair } from \"../contracts/types\";\nimport { AccessToken } from \"../models/access-token\";\nimport type { Auth } from \"../models/auth\";\nimport { RefreshToken } from \"../models/refresh-token\";\nimport { parseExpirationToMs, toJwtExpiresIn } from \"../utils/duration\";\nimport { authEvents } from \"./auth-events\";\nimport { jwt } from \"./jwt\";\n\nclass AuthService {\n /**\n * Build access token payload from user\n */\n public buildAccessTokenPayload(user: Auth) {\n return {\n id: user.id,\n _id: user.get(\"_id\"),\n userType: user.userType,\n createdAt: Date.now(),\n };\n }\n\n /**\n * Generate access token for user\n */\n public async generateAccessToken(user: Auth, payload?: any): Promise<string> {\n const data = payload || this.buildAccessTokenPayload(user);\n const expiresInConfig = config.key(\"auth.jwt.expiresIn\");\n const expiresIn = toJwtExpiresIn(expiresInConfig, 3600000); // default 1 hour\n\n // If expiresIn is undefined, token never expires\n const token = expiresIn ? await jwt.generate(data, { expiresIn }) : await jwt.generate(data);\n\n // Store in database\n await AccessToken.create({\n token,\n user: data,\n });\n\n return token;\n }\n\n /**\n * Create refresh token for user\n */\n public async createRefreshToken(user: Auth, deviceInfo?: DeviceInfo): Promise<RefreshToken> {\n const familyId = deviceInfo?.familyId || Random.string(32);\n const expiresInConfig = config.key(\"auth.jwt.refresh.expiresIn\");\n const expiresInMs = parseExpirationToMs(expiresInConfig, 7 * 24 * 60 * 60 * 1000); // default 7 days\n\n const payload = {\n userId: user.id,\n userType: user.userType,\n familyId,\n };\n\n const token = await jwt.generateRefreshToken(payload);\n\n // Enforce max tokens per user\n await this.enforceMaxRefreshTokens(user);\n\n // Calculate expiration date (undefined means never expires, but we still set a far future date)\n const expiresAt = expiresInMs\n ? new Date(Date.now() + expiresInMs)\n : new Date(Date.now() + 100 * 365 * 24 * 60 * 60 * 1000);\n\n // Store in database\n return RefreshToken.create({\n token,\n userId: user.id,\n userType: user.userType,\n familyId,\n expiresAt,\n deviceInfo: deviceInfo\n ? {\n userAgent: deviceInfo.userAgent,\n ip: deviceInfo.ip,\n deviceId: deviceInfo.deviceId,\n }\n : undefined,\n });\n }\n\n /**\n * Create both access and refresh tokens\n */\n public async createTokenPair(user: Auth, deviceInfo?: DeviceInfo): Promise<TokenPair> {\n const accessToken = await this.generateAccessToken(user, deviceInfo?.payload);\n const refreshToken = await this.createRefreshToken(user, deviceInfo);\n\n const tokenPair: TokenPair = {\n accessToken,\n refreshToken: refreshToken.get(\"token\"),\n expiresIn: config.key(\"auth.jwt.expiresIn\", \"1h\"),\n };\n\n // Emit events\n authEvents.emit(\"token.created\", user, tokenPair);\n authEvents.emit(\"session.created\", user, refreshToken, deviceInfo);\n\n return tokenPair;\n }\n\n /**\n * Refresh tokens using a refresh token\n */\n public async refreshTokens(\n refreshTokenString: string,\n deviceInfo?: DeviceInfo,\n ): Promise<TokenPair | null> {\n try {\n // 1. Verify JWT signature\n const decoded = await jwt.verifyRefreshToken<{\n userId: number;\n userType: string;\n familyId: string;\n }>(refreshTokenString);\n\n if (!decoded) return null;\n\n // 2. Find token in database\n const refreshToken = await RefreshToken.first({ token: refreshTokenString });\n\n if (!refreshToken?.isValid) {\n // If token was already used (rotation detection), revoke entire family\n if (refreshToken) {\n await this.revokeTokenFamily(refreshToken.get(\"familyId\"));\n }\n return null;\n }\n\n // 3. Get user model and find user\n const UserModel = config.key(`auth.userType.${decoded.userType}`);\n if (!UserModel) return null;\n\n const user = (await UserModel.find(decoded.userId)) as Auth | null;\n if (!user) return null;\n\n // 4. Rotate token if enabled (revoke old token)\n const rotationEnabled = config.key(\"auth.jwt.refresh.rotation\", true);\n if (rotationEnabled) {\n await refreshToken.revoke();\n } else {\n await refreshToken.markAsUsed();\n }\n\n // 5. Generate new token pair (keep same family)\n const newTokenPair = await this.createTokenPair(user, {\n ...deviceInfo,\n familyId: refreshToken.get(\"familyId\"),\n });\n\n // Emit token refreshed event\n authEvents.emit(\"token.refreshed\", user, newTokenPair, refreshToken);\n\n return newTokenPair;\n } catch {\n return null;\n }\n }\n\n /**\n * Verify password\n */\n public verifyPassword(hashedPassword: string, plainPassword: string): boolean {\n return verify(String(hashedPassword), String(plainPassword));\n }\n\n /**\n * Hash password\n */\n public hashPassword(password: string): string {\n return hash(String(password), config.key(\"auth.password.salt\", 12));\n }\n\n /**\n * Attempt to login user with given credentials\n */\n public async attemptLogin(Model: ChildModel<Auth>, data: any): Promise<Auth | null> {\n const { password, ...otherData } = data;\n\n // Emit login attempt event\n authEvents.emit(\"login.attempt\", otherData);\n\n const user = (await Model.first(otherData)) as Auth | null;\n\n if (!user) {\n authEvents.emit(\"login.failed\", otherData, \"User not found\");\n return null;\n }\n\n if (!this.verifyPassword(user.string(\"password\")!, password)) {\n authEvents.emit(\"login.failed\", otherData, \"Invalid password\");\n return null;\n }\n\n return user;\n }\n\n /**\n * Full login flow: validate credentials, create tokens, emit events\n * Returns token pair on success, null on failure\n */\n public async login(\n Model: ChildModel<Auth>,\n credentials: any,\n deviceInfo?: DeviceInfo,\n ): Promise<{ user: Auth; tokens: TokenPair } | null | { user: Auth; accessToken: string }> {\n const user = await this.attemptLogin(Model, credentials);\n\n if (!user) {\n return null;\n }\n\n // if no refresh token in config, then return user and access token only\n if (!config.key(\"auth.jwt.refresh.enabled\", true)) {\n const accessToken = await this.generateAccessToken(user, deviceInfo?.payload);\n return { user, accessToken };\n }\n\n const tokens = await this.createTokenPair(user, deviceInfo);\n\n // Emit login success event\n authEvents.emit(\"login.success\", user, tokens, deviceInfo);\n\n return { user, tokens };\n }\n\n /**\n * Logout user\n * @param user - The authenticated user\n * @param accessToken - Optional access token string to revoke\n * @param refreshToken - Optional refresh token string to revoke\n * If refresh token is not provided, behavior is determined by config:\n * - \"revoke-all\" (default): Revoke ALL refresh tokens for security\n * - \"error\": Throw error requiring refresh token\n */\n public async logout(user: Auth, accessToken?: string, refreshToken?: string): Promise<void> {\n // Remove access token if provided\n if (accessToken) {\n await this.removeAccessToken(user, accessToken);\n }\n\n if (refreshToken) {\n // Revoke specific refresh token\n const token = await RefreshToken.first({\n token: refreshToken,\n userId: user.id, // Security: ensure token belongs to this user\n });\n\n if (token) {\n await token.revoke();\n authEvents.emit(\"session.destroyed\", user, token);\n }\n } else {\n // No refresh token provided - check configured behavior\n const behavior = config.key(\"auth.jwt.refresh.logoutWithoutToken\", \"revoke-all\") as\n | \"revoke-all\"\n | \"error\";\n\n if (behavior === \"error\") {\n throw new Error(\"Refresh token required for logout\");\n }\n\n // Default: revoke-all (fail-safe)\n await this.revokeAllTokens(user);\n authEvents.emit(\"logout.failsafe\", user);\n }\n\n // Emit logout event\n authEvents.emit(\"logout\", user);\n }\n\n /**\n * Remove specific access token\n */\n public async removeAccessToken(user: Auth, token: string): Promise<void> {\n AccessToken.delete({\n token,\n \"user.id\": user.id,\n });\n }\n\n /**\n * Revoke all tokens for a user\n */\n public async revokeAllTokens(user: Auth): Promise<void> {\n // Revoke all refresh tokens\n const refreshTokens = await RefreshToken.query()\n .where(\"userId\", user.id)\n .where(\"userType\", user.userType)\n .where(\"revokedAt\", null)\n .get();\n\n for (const token of refreshTokens) {\n await token.revoke();\n authEvents.emit(\"token.revoked\", user, token);\n }\n\n // Delete all access tokens\n await AccessToken.delete({\n \"user.id\": user.id,\n \"user.userType\": user.userType,\n });\n\n // Emit logout all event\n authEvents.emit(\"logout.all\", user);\n }\n\n /**\n * Revoke entire token family (for rotation breach detection)\n */\n public async revokeTokenFamily(familyId: string): Promise<void> {\n const tokens = await RefreshToken.query()\n .where(\"familyId\", familyId)\n .where(\"revokedAt\", null)\n .get();\n\n for (const token of tokens) {\n await token.revoke();\n }\n\n // Emit family revoked event\n authEvents.emit(\"token.familyRevoked\", familyId, tokens);\n }\n\n /**\n * Cleanup expired tokens\n */\n public async cleanupExpiredTokens(): Promise<number> {\n const expiredTokens = await RefreshToken.query().where(\"expiresAt\", \"<\", new Date()).get();\n\n for (const token of expiredTokens) {\n authEvents.emit(\"token.expired\", token);\n await token.destroy();\n }\n\n // Emit cleanup completed event\n authEvents.emit(\"cleanup.completed\", expiredTokens.length);\n\n return expiredTokens.length;\n }\n\n /**\n * Enforce max refresh tokens per user\n */\n private async enforceMaxRefreshTokens(user: Auth): Promise<void> {\n const maxPerUser = config.key(\"auth.jwt.refresh.maxPerUser\", 5);\n\n const activeTokens = await RefreshToken.query()\n .where(\"userId\", user.id)\n .where(\"userType\", user.userType)\n .where(\"revokedAt\", null)\n .orderBy(\"createdAt\", \"asc\")\n .get();\n\n // Revoke oldest tokens if exceeding limit\n if (activeTokens.length >= maxPerUser) {\n const tokensToRevoke = activeTokens.slice(0, activeTokens.length - maxPerUser + 1);\n for (const token of tokensToRevoke) {\n await token.revoke();\n }\n }\n }\n\n /**\n * Get active sessions for user\n */\n public async getActiveSessions(user: Auth): Promise<RefreshToken[]> {\n return RefreshToken.query()\n .where(\"userId\", user.id)\n .where(\"userType\", user.userType)\n .where(\"revokedAt\", null)\n .where(\"expiresAt\", \">\", new Date())\n .orderBy(\"createdAt\", \"desc\")\n .get();\n }\n}\n\nexport const authService = new AuthService();\n","import { colors } from \"@mongez/copper\";\nimport { command } from \"@warlock.js/core\";\nimport { authService } from \"../services/auth.service\";\n\n/**\n * Register the auth:cleanup CLI command\n *\n * @example\n * ```bash\n * warlock auth:cleanup\n * ```\n */\nexport function registerAuthCleanupCommand() {\n return command({\n name: \"auth.cleanup\",\n description: \"Remove expired refresh tokens from the database\",\n preload: {\n env: true,\n config: [\"auth\", \"database\"],\n connectors: [\"database\"],\n },\n action: async () => {\n console.log(colors.cyan(\"๐งน Cleaning up expired tokens...\"));\n\n const count = await authService.cleanupExpiredTokens();\n\n if (count === 0) {\n console.log(colors.green(\"โ
No expired tokens found.\"));\n } else {\n console.log(colors.green(`โ
Removed ${count} expired token(s).`));\n }\n },\n });\n}\n","import { fileExistsAsync, getFileAsync, putFileAsync } from \"@mongez/fs\";\r\nimport { Random } from \"@mongez/reinforcements\";\r\nimport { environment, rootPath } from \"@warlock.js/core\";\r\nimport { log } from \"@warlock.js/logger\";\r\n\r\nexport async function generateJWTSecret() {\r\n let envFile = rootPath(\".env\");\r\n\r\n log.info(\"jwt\", \"generating\", \"Generating JWT secrets\");\r\n\r\n const environmentMode = environment();\r\n\r\n if (!(await fileExistsAsync(envFile))) {\r\n const envFileType = environmentMode === \"production\" ? \".env.production\" : \".env.development\";\r\n envFile = rootPath(envFileType);\r\n }\r\n\r\n if (!(await fileExistsAsync(envFile))) {\r\n log.error(\"jwt\", \"error\", \".env file not found\");\r\n return;\r\n }\r\n\r\n let contents = await getFileAsync(envFile);\r\n\r\n const hasJwtSecret = contents.includes(\"JWT_SECRET\");\r\n const hasJwtRefreshSecret = contents.includes(\"JWT_REFRESH_SECRET\");\r\n\r\n if (hasJwtSecret && hasJwtRefreshSecret) {\r\n log.warn(\"jwt\", \"exists\", \"JWT secrets already exist in the .env file.\");\r\n return;\r\n }\r\n\r\n let secretsToAdd = \"\";\r\n\r\n if (!hasJwtSecret) {\r\n const jwtSecret = Random.string(32);\r\n secretsToAdd += `\r\n# JWT Secret\r\nJWT_SECRET=${jwtSecret}\r\n`;\r\n log.success(\"jwt\", \"generated\", \"JWT_SECRET generated and added to the .env file.\");\r\n } else {\r\n log.info(\"jwt\", \"exists\", \"JWT_SECRET already exists in the .env file.\");\r\n }\r\n\r\n if (!hasJwtRefreshSecret) {\r\n const jwtRefreshSecret = Random.string(32);\r\n secretsToAdd += `\r\n# JWT Refresh Secret\r\nJWT_REFRESH_SECRET=${jwtRefreshSecret}\r\n`;\r\n log.success(\"jwt\", \"generated\", \"JWT_REFRESH_SECRET generated and added to the .env file.\");\r\n } else {\r\n log.info(\"jwt\", \"exists\", \"JWT_REFRESH_SECRET already exists in the .env file.\");\r\n }\r\n\r\n if (secretsToAdd) {\r\n contents += secretsToAdd;\r\n await putFileAsync(envFile, contents);\r\n }\r\n}\r\n","import { command } from \"@warlock.js/core\";\r\nimport { generateJWTSecret } from \"../services/generate-jwt-secret\";\r\n\r\nexport function registerJWTSecretGeneratorCommand() {\r\n return command({\r\n name: \"jwt.generate\",\r\n description: \"Generate JWT Secret key in .env file\",\r\n action: generateJWTSecret,\r\n });\r\n}\r\n","export enum AuthErrorCodes {\n /**\n * Missing Access Token Error Code EC001\n * EC001 = Missing Access Token\n */\n MissingAccessToken = \"EC001\", // Error Code 001\n /**\n * Invalid Access Token Error Code EC002\n * EC002 = Invalid Access Token\n */\n InvalidAccessToken = \"EC002\", // Error Code 002\n /**\n * Unauthorized Error Code EC003\n * EC003 = Unauthorized\n */\n Unauthorized = \"EC003\", // Error Code 003\n}\n","import { config, t, type Middleware, type Request, type Response } from \"@warlock.js/core\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { AccessToken } from \"../models/access-token\";\r\nimport { jwt } from \"../services/jwt\";\r\nimport { AuthErrorCodes } from \"../utils/auth-error-codes\";\r\n\r\nexport function authMiddleware(allowedUserType?: string | string[]) {\r\n const allowedTypes = !allowedUserType\r\n ? []\r\n : Array.isArray(allowedUserType)\r\n ? allowedUserType\r\n : [allowedUserType];\r\n\r\n const auth: Middleware = async (request: Request, response: Response) => {\r\n try {\r\n const authorizationValue = request.authorizationValue;\r\n\r\n if (!allowedTypes.length && !authorizationValue) return;\r\n\r\n if (!authorizationValue) {\r\n return response.unauthorized({\r\n error: t(\"auth.errors.missingAccessToken\"),\r\n errorCode: AuthErrorCodes.MissingAccessToken,\r\n });\r\n }\r\n\r\n // get current user jwt\r\n const user = await jwt.verify(authorizationValue);\r\n\r\n // store decoded access token object in request object\r\n request.decodedAccessToken = user;\r\n // use our own jwt verify to verify the token\r\n const accessToken = await AccessToken.first({\r\n token: authorizationValue,\r\n });\r\n\r\n if (!accessToken) {\r\n return response.unauthorized({\r\n error: t(\"auth.errors.invalidAccessToken\"),\r\n errorCode: AuthErrorCodes.InvalidAccessToken,\r\n });\r\n }\r\n\r\n // now, we need to get an instance of user using its corresponding model\r\n const userType = user.userType || accessToken.get(\"userType\");\r\n\r\n // check if the user type is allowed\r\n if (allowedTypes.length && !allowedTypes.includes(userType)) {\r\n return response.unauthorized({\r\n error: t(\"auth.errors.unauthorized\"),\r\n errorCode: AuthErrorCodes.Unauthorized,\r\n });\r\n }\r\n\r\n // get user model class\r\n const UserModel = config.key(`auth.userType.${userType}`);\r\n\r\n if (!UserModel) {\r\n throw new Error(`User type ${userType} is unknown type.`);\r\n }\r\n\r\n // get user model instance\r\n const currentUser = await UserModel.find(user.id);\r\n\r\n if (!currentUser) {\r\n accessToken.destroy();\r\n return response.unauthorized({\r\n error: t(\"auth.errors.invalidAccessToken\"),\r\n errorCode: AuthErrorCodes.InvalidAccessToken,\r\n });\r\n }\r\n\r\n // update last access\r\n accessToken.set(\"lastAccess\", new Date());\r\n await accessToken.save({ skipEvents: true });\r\n\r\n // set current user\r\n request.user = currentUser;\r\n } catch (err: any) {\r\n log.error(\"http\", \"auth\", err);\r\n\r\n // unset current user\r\n request.clearCurrentUser();\r\n\r\n return response.unauthorized({\r\n error: t(\"auth.errors.invalidAccessToken\"),\r\n errorCode: AuthErrorCodes.InvalidAccessToken,\r\n });\r\n }\r\n };\r\n\r\n return auth;\r\n}\r\n","import { type ChildModel, Model, type ModelSchema } from \"@warlock.js/cascade\";\r\nimport type { DeviceInfo, TokenPair } from \"../contracts/types\";\r\nimport { authService } from \"../services\";\r\nimport type { RefreshToken } from \"./refresh-token/refresh-token\";\r\n\r\nexport abstract class Auth<Schema extends ModelSchema = ModelSchema> extends Model<Schema> {\r\n /**\r\n * Get user type\r\n */\r\n public abstract get userType(): string;\r\n\r\n /**\r\n * Get access token payload\r\n */\r\n public accessTokenPayload() {\r\n return authService.buildAccessTokenPayload(this);\r\n }\r\n\r\n /**\r\n * Create both access and refresh tokens\r\n */\r\n public async createTokenPair(deviceInfo?: DeviceInfo): Promise<TokenPair> {\r\n return authService.createTokenPair(this, deviceInfo);\r\n }\r\n\r\n /**\r\n * Generate access token\r\n */\r\n public async generateAccessToken(data?: any): Promise<string> {\r\n return authService.generateAccessToken(this, data);\r\n }\r\n\r\n /**\r\n * Generate refresh token\r\n */\r\n public async generateRefreshToken(deviceInfo?: DeviceInfo): Promise<RefreshToken> {\r\n return authService.createRefreshToken(this, deviceInfo);\r\n }\r\n\r\n /**\r\n * Remove current access token\r\n */\r\n public async removeAccessToken(token: string): Promise<void> {\r\n return authService.removeAccessToken(this, token);\r\n }\r\n\r\n /**\r\n * Revoke all tokens (logout from all devices)\r\n */\r\n public async revokeAllTokens(): Promise<void> {\r\n return authService.revokeAllTokens(this);\r\n }\r\n\r\n /**\r\n * Get active sessions\r\n */\r\n public async activeSessions(): Promise<RefreshToken[]> {\r\n return authService.getActiveSessions(this);\r\n }\r\n\r\n /**\r\n * Attempt to login the user\r\n */\r\n public static async attempt(this: ChildModel<Auth>, data: any): Promise<Auth | null> {\r\n return authService.attemptLogin(this, data);\r\n }\r\n\r\n /**\r\n * Confirm password\r\n */\r\n public confirmPassword(password: string): boolean {\r\n return authService.verifyPassword(this.string(\"password\")!, password);\r\n }\r\n}\r\n","import { hash } from \"@mongez/password\";\r\nimport type { Model } from \"@warlock.js/cascade\";\r\nimport { config } from \"@warlock.js/core\";\r\n\r\n/**\r\n * Cast password on model save\r\n * If the password is not changed, keep it as is\r\n */\r\nexport function castPassword(value: any, column: string, model: Model) {\r\n return value\r\n ? hash(String(value), config.key(\"auth.password.salt\", 12))\r\n : model.getInitial(column);\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,36 +1,50 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
2
|
+
"name": "@warlock.js/auth",
|
|
3
|
+
"version": "4.0.39",
|
|
4
|
+
"description": "Authentication system for Warlock.js applications",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nodejs",
|
|
7
|
+
"auth",
|
|
8
|
+
"auth.js",
|
|
9
|
+
"authentication",
|
|
10
|
+
"authorization",
|
|
11
|
+
"jwt"
|
|
12
|
+
],
|
|
13
|
+
"author": "hassanzohdy",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/warlockjs/auth"
|
|
18
|
+
},
|
|
19
|
+
"main": "./cjs/index.js",
|
|
20
|
+
"module": "./esm/index.js",
|
|
21
|
+
"types": "./esm/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": {
|
|
25
|
+
"types": "./esm/index.d.ts",
|
|
26
|
+
"default": "./esm/index.js"
|
|
27
|
+
},
|
|
28
|
+
"require": {
|
|
29
|
+
"types": "./cjs/index.d.ts",
|
|
30
|
+
"default": "./cjs/index.js"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"type": "module",
|
|
35
|
+
"sideEffects": false,
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@mongez/password": "^1.0.2",
|
|
38
|
+
"fast-jwt": "^6.1.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@mongez/copper": "^1.0.1",
|
|
42
|
+
"@mongez/events": "^2.1.0",
|
|
43
|
+
"@mongez/fs": "^3.0.5",
|
|
44
|
+
"@mongez/reinforcements": "^2.3.17",
|
|
45
|
+
"@warlock.js/cascade": "4.0.39",
|
|
46
|
+
"@warlock.js/core": "4.0.39",
|
|
47
|
+
"@warlock.js/logger": "4.0.39",
|
|
48
|
+
"@warlock.js/seal": "4.0.39"
|
|
49
|
+
}
|
|
36
50
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-cleanup-command.d.ts","sourceRoot":"","sources":["../../src/commands/auth-cleanup-command.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,QAqBzC"}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
'use strict';var copper=require('@mongez/copper'),core=require('@warlock.js/core'),auth_service=require('../services/auth.service.js');/**
|
|
2
|
-
* Register the auth:cleanup CLI command
|
|
3
|
-
*
|
|
4
|
-
* @example
|
|
5
|
-
* ```bash
|
|
6
|
-
* warlock auth:cleanup
|
|
7
|
-
* ```
|
|
8
|
-
*/
|
|
9
|
-
function registerAuthCleanupCommand() {
|
|
10
|
-
return core.command({
|
|
11
|
-
name: "auth.cleanup",
|
|
12
|
-
description: "Remove expired refresh tokens from the database",
|
|
13
|
-
preload: {
|
|
14
|
-
env: true,
|
|
15
|
-
config: ["auth", "database"],
|
|
16
|
-
connectors: ["database"],
|
|
17
|
-
},
|
|
18
|
-
action: async () => {
|
|
19
|
-
console.log(copper.colors.cyan("๐งน Cleaning up expired tokens..."));
|
|
20
|
-
const count = await auth_service.authService.cleanupExpiredTokens();
|
|
21
|
-
if (count === 0) {
|
|
22
|
-
console.log(copper.colors.green("โ
No expired tokens found."));
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
console.log(copper.colors.green(`โ
Removed ${count} expired token(s).`));
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
}exports.registerAuthCleanupCommand=registerAuthCleanupCommand;//# sourceMappingURL=auth-cleanup-command.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-cleanup-command.js","sources":["../../src/commands/auth-cleanup-command.ts"],"sourcesContent":[null],"names":["command","colors","authService"],"mappings":"uIAIA;;;;;;;AAOG;SACa,0BAA0B,GAAA;AACxC,IAAA,OAAOA,YAAO,CAAC;AACb,QAAA,IAAI,EAAE,cAAc;AACpB,QAAA,WAAW,EAAE,iDAAiD;AAC9D,QAAA,OAAO,EAAE;AACP,YAAA,GAAG,EAAE,IAAI;AACT,YAAA,MAAM,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC;YAC5B,UAAU,EAAE,CAAC,UAAU,CAAC;AACzB,SAAA;QACD,MAAM,EAAE,YAAW;YACjB,OAAO,CAAC,GAAG,CAACC,aAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC,CAAC;AAE7D,YAAA,MAAM,KAAK,GAAG,MAAMC,wBAAW,CAAC,oBAAoB,EAAE,CAAC;YAEvD,IAAI,KAAK,KAAK,CAAC,EAAE;gBACf,OAAO,CAAC,GAAG,CAACD,aAAM,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC,CAAC;AACzD,aAAA;AAAM,iBAAA;AACL,gBAAA,OAAO,CAAC,GAAG,CAACA,aAAM,CAAC,KAAK,CAAC,CAAA,UAAA,EAAa,KAAK,CAAA,kBAAA,CAAoB,CAAC,CAAC,CAAC;AACnE,aAAA;SACF;AACF,KAAA,CAAC,CAAC;AACL"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-secret-generator-command.d.ts","sourceRoot":"","sources":["../../src/commands/jwt-secret-generator-command.ts"],"names":[],"mappings":"AAGA,wBAAgB,iCAAiC,QAMhD"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
'use strict';var core=require('@warlock.js/core'),generateJwtSecret=require('../services/generate-jwt-secret.js');function registerJWTSecretGeneratorCommand() {
|
|
2
|
-
return core.command({
|
|
3
|
-
name: "jwt.generate",
|
|
4
|
-
description: "Generate JWT Secret key in .env file",
|
|
5
|
-
action: generateJwtSecret.generateJWTSecret,
|
|
6
|
-
});
|
|
7
|
-
}exports.registerJWTSecretGeneratorCommand=registerJWTSecretGeneratorCommand;//# sourceMappingURL=jwt-secret-generator-command.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-secret-generator-command.js","sources":["../../src/commands/jwt-secret-generator-command.ts"],"sourcesContent":[null],"names":["command","generateJWTSecret"],"mappings":"2HAGgB,iCAAiC,GAAA;AAC/C,IAAA,OAAOA,YAAO,CAAC;AACb,QAAA,IAAI,EAAE,cAAc;AACpB,QAAA,WAAW,EAAE,sCAAsC;AACnD,QAAA,MAAM,EAAEC,mCAAiB;AAC1B,KAAA,CAAC,CAAC;AACL"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export interface Authenticable {
|
|
2
|
-
/**
|
|
3
|
-
* Generate access token
|
|
4
|
-
*/
|
|
5
|
-
generateAccessToken(): Promise<string>;
|
|
6
|
-
/**
|
|
7
|
-
* Generate refresh token
|
|
8
|
-
*/
|
|
9
|
-
generateRefreshToken(): Promise<string>;
|
|
10
|
-
/**
|
|
11
|
-
* Change password
|
|
12
|
-
*/
|
|
13
|
-
changePassword(password: string): Promise<void>;
|
|
14
|
-
/**
|
|
15
|
-
* Verify Password
|
|
16
|
-
*/
|
|
17
|
-
verifyPassword(password: string): Promise<boolean>;
|
|
18
|
-
/**
|
|
19
|
-
* Get user type
|
|
20
|
-
*/
|
|
21
|
-
getUserType(): string;
|
|
22
|
-
}
|
|
23
|
-
//# sourceMappingURL=auth-contract.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-contract.d.ts","sourceRoot":"","sources":["../../src/contracts/auth-contract.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAEvC;;OAEG;IACH,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAExC;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEnD;;OAEG;IACH,WAAW,IAAI,MAAM,CAAC;CACvB"}
|
package/cjs/contracts/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC"}
|