next-token-auth 1.0.13 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -65
- package/dist/{index-CejL5heu.d.mts → index-Csz5lfEv.d.mts} +30 -2
- package/dist/{index-CejL5heu.d.ts → index-Csz5lfEv.d.ts} +30 -2
- package/dist/index.d.mts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.js +84 -45
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +85 -46
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +4 -4
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.js +53 -526
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +54 -527
- package/dist/react/index.mjs.map +1 -1
- package/dist/server/index.d.mts +45 -7
- package/dist/server/index.d.ts +45 -7
- package/dist/server/index.js +178 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +178 -1
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/crypto.ts","../../src/utils/expiry.ts","../../src/server/getServerSession.ts","../../src/server/withAuth.ts","../../src/server/middleware.ts"],"names":[],"mappings":";;;AAKA,IAAM,IAAA,GAAO,SAAA;AAGb,SAAS,cAAA,GAAiB;AACxB,EAAA,OAAO,IAAI,WAAA,EAAY;AACzB;AAEA,SAAS,cAAA,GAAiB;AACxB,EAAA,OAAO,IAAI,WAAA,EAAY;AACzB;AAEA,eAAe,UAAU,MAAA,EAAoC;AAC3D,EAAA,MAAM,GAAA,GAAM,cAAA,EAAe,CAAE,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,EAAA,EAAI,GAAG,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA;AACvE,EAAA,OAAO,MAAA,CAAO,OAAO,SAAA,CAAU,KAAA,EAAO,KAAK,EAAE,IAAA,EAAM,IAAA,EAAK,EAAG,KAAA,EAAO;AAAA,IAChE,SAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;AAuBA,eAAsB,OAAA,CAAQ,MAAc,MAAA,EAAiC;AAC3E,EAAA,MAAM,CAAC,KAAA,EAAO,SAAS,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACzC,EAAA,IAAI,CAAC,KAAA,IAAS,CAAC,SAAA,EAAW;AACxB,IAAA,MAAM,IAAI,MAAM,oCAAoC,CAAA;AAAA,EACtD;AAEA,EAAA,MAAM,GAAA,GAAM,MAAM,SAAA,CAAU,MAAM,CAAA;AAClC,EAAA,MAAM,OAAA,GAAU,eAAe,KAAK,CAAA;AACpC,EAAA,MAAM,EAAA,GAAK,QAAQ,MAAA,CAAO,KAAA;AAAA,IACxB,OAAA,CAAQ,UAAA;AAAA,IACR,OAAA,CAAQ,aAAa,OAAA,CAAQ;AAAA,GAC/B;AAEA,EAAA,MAAM,WAAA,GAAc,eAAe,SAAS,CAAA;AAC5C,EAAA,MAAM,YAAA,GAAe,YAAY,MAAA,CAAO,KAAA;AAAA,IACtC,WAAA,CAAY,UAAA;AAAA,IACZ,WAAA,CAAY,aAAa,WAAA,CAAY;AAAA,GACvC;AAEA,EAAA,MAAM,WAAA,GAAc,MAAM,MAAA,CAAO,MAAA,CAAO,OAAA,CAAQ,EAAE,IAAA,EAAM,IAAA,EAAM,EAAA,EAAG,EAAG,GAAA,EAAK,YAAY,CAAA;AAErF,EAAA,OAAO,cAAA,EAAe,CAAE,MAAA,CAAO,WAAW,CAAA;AAC5C;AAWA,SAAS,eAAe,GAAA,EAAyB;AAC/C,EAAA,MAAM,MAAA,GAAS,IAAI,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,MAAM,GAAG,CAAA;AACvD,EAAA,MAAM,MAAA,GAAS,KAAK,MAAM,CAAA;AAC1B,EAAA,OAAO,UAAA,CAAW,KAAK,MAAA,EAAQ,CAAC,MAAM,CAAA,CAAE,UAAA,CAAW,CAAC,CAAC,CAAA;AACvD;;;AChFA,IAAM,QAAA,GAAmC;AAAA,EACvC,CAAA,EAAG,CAAA;AAAA,EACH,CAAA,EAAG,EAAA;AAAA,EACH,CAAA,EAAG,IAAA;AAAA,EACH,CAAA,EAAG,KAAA;AAAA,EACH,CAAA,EAAG;AACL,CAAA;AAUO,SAAS,YAAY,KAAA,EAA6B;AACvD,EAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AACzC,IAAA,MAAM,IAAI,MAAM,uCAAuC,CAAA;AAAA,EACzD;AAEA,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,IAAI,KAAA,IAAS,CAAA,EAAG,MAAM,IAAI,MAAM,qCAAqC,CAAA;AACrE,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAG3B,EAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAA,EAAG;AACzB,IAAA,OAAO,QAAA,CAAS,SAAS,EAAE,CAAA;AAAA,EAC7B;AAEA,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,gCAAgC,CAAA;AAC5D,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,qCAAqC,KAAK,CAAA,oEAAA;AAAA,KAE5C;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,KAAA,CAAM,CAAC,CAAC,CAAA;AACjC,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,CAAC,CAAA,CAAE,WAAA,EAAY;AAClC,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,QAAA,CAAS,IAAI,CAAC,CAAA;AAC1C;AAKO,SAAS,eAAA,CACd,KAAA,EACA,eAAA,GAAkB,GAAA,EACV;AACR,EAAA,IAAI;AACF,IAAA,OAAO,YAAY,KAAK,CAAA;AAAA,EAC1B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,eAAA;AAAA,EACT;AACF;AAMO,SAAS,wBAAA,CACd,QAAA,EACA,YAAA,EACA,QAAA,GAA2B,QAAA,EACnB;AACR,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAErB,EAAA,MAAM,WAAA,GACJ,QAAA,CAAS,oBAAA,IAAwB,QAAA,CAAS,SAAA,IAAa,MAAA;AAEzD,EAAA,IAAI,aAAa,SAAA,EAAW;AAC1B,IAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,GAAA,GAAM,WAAA,CAAY,WAAW,CAAA,GAAI,GAAA;AAAA,EAC1C;AAEA,EAAA,IAAI,aAAa,QAAA,EAAU;AACzB,IAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,GAAA,GAAM,WAAA,CAAY,YAAY,CAAA,GAAI,GAAA;AAAA,EAC3C;AAGA,EAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,IAAA,OAAO,GAAA,GAAM,eAAA,CAAgB,WAAW,CAAA,GAAI,GAAA;AAAA,EAC9C;AACA,EAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,IAAA,OAAO,GAAA,GAAM,eAAA,CAAgB,YAAY,CAAA,GAAI,GAAA;AAAA,EAC/C;AAGA,EAAA,OAAO,MAAM,GAAA,GAAM,GAAA;AACrB;AAKO,SAAS,yBAAA,CACd,QAAA,EACA,YAAA,EACA,QAAA,GAA2B,QAAA,EACP;AACpB,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,MAAM,cAAc,QAAA,CAAS,qBAAA;AAE7B,EAAA,IAAI,aAAa,SAAA,EAAW;AAC1B,IAAA,OAAO,gBAAgB,MAAA,GACnB,GAAA,GAAM,WAAA,CAAY,WAAW,IAAI,GAAA,GACjC,MAAA;AAAA,EACN;AAEA,EAAA,IAAI,aAAa,QAAA,EAAU;AACzB,IAAA,OAAO,iBAAiB,MAAA,GACpB,GAAA,GAAM,WAAA,CAAY,YAAY,IAAI,GAAA,GAClC,MAAA;AAAA,EACN;AAGA,EAAA,IAAI,gBAAgB,MAAA,EAAW;AAC7B,IAAA,OAAO,GAAA,GAAM,eAAA,CAAgB,WAAW,CAAA,GAAI,GAAA;AAAA,EAC9C;AACA,EAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,IAAA,OAAO,GAAA,GAAM,eAAA,CAAgB,YAAY,CAAA,GAAI,GAAA;AAAA,EAC/C;AAEA,EAAA,OAAO,MAAA;AACT;;;AC7GA,eAAsB,gBAAA,CACpB,KACA,MAAA,EAC4B;AAC5B,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,KAAA,CAAM,UAAA,IAAc,yBAAA;AAC9C,EAAA,MAAM,WAAA,GAAc,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,UAAU,CAAA,EAAG,KAAA;AAEjD,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,OAAO,EAAE,IAAA,EAAM,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,iBAAiB,KAAA,EAAM;AAAA,EAC5D;AAEA,EAAA,IAAI,MAAA,GAA4B,IAAA;AAEhC,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,WAAA,EAAa,OAAO,MAAM,CAAA;AACrD,IAAA,MAAA,GAAS,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,EAC1B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAE,IAAA,EAAM,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,iBAAiB,KAAA,EAAM;AAAA,EAC5D;AAEA,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,MAAM,SAAA,GAAA,CAAa,MAAA,CAAO,gBAAA,IAAoB,EAAA,IAAM,GAAA;AACpD,EAAA,MAAM,aAAA,GAAgB,GAAA,IAAO,MAAA,CAAO,oBAAA,GAAuB,SAAA;AAC3D,EAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,qBAAA,GAC1B,GAAA,IAAO,OAAO,qBAAA,GACd,KAAA;AAGJ,EAAA,IAAI,iBAAiB,cAAA,EAAgB;AACnC,IAAA,OAAO,EAAE,IAAA,EAAM,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,iBAAiB,KAAA,EAAM;AAAA,EAC5D;AAGA,EAAA,IAAI,aAAA,IAAiB,CAAC,cAAA,EAAgB;AACpC,IAAA,MAAM,SAAA,GAAY,MAAM,aAAA,CAAoB,MAAA,EAAQ,MAAM,CAAA;AAC1D,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,OAAO,EAAE,IAAA,EAAM,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,iBAAiB,KAAA,EAAM;AAAA,IAC5D;AACA,IAAA,MAAA,GAAS,SAAA;AAAA,EACX;AAEA,EAAA,MAAM,IAAA,GAAO,MAAM,SAAA,CAAgB,MAAA,CAAO,aAAa,MAAM,CAAA;AAE7D,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,MAAA;AAAA,IACA,eAAA,EAAiB;AAAA,GACnB;AACF;AAIA,eAAe,aAAA,CACb,QACA,MAAA,EAC4B;AAC5B,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,OAAO,OAAA,IAAW,KAAA;AAClC,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,OAAO,EAAE,CAAA;AAChD,IAAA,MAAM,cAAc,MAAA,CAAO,SAAA,CAAU,OAAA,CAAQ,OAAA,CAAQ,OAAO,EAAE,CAAA;AAE9D,IAAA,MAAM,MAAM,MAAM,OAAA,CAAQ,GAAG,OAAO,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA,EAAI;AAAA,MACrD,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,MAAM,IAAA,CAAK,SAAA,CAAU,EAAE,YAAA,EAAc,MAAA,CAAO,cAAc;AAAA,KAC3D,CAAA;AAED,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,OAAO,IAAA;AAEpB,IAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,MAAA,EAAQ,QAAA,IAAY,QAAA;AAE5C,IAAA,OAAO;AAAA,MACL,aAAa,IAAA,CAAK,WAAA;AAAA,MAClB,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB,oBAAA,EAAsB,wBAAA;AAAA,QACpB,IAAA;AAAA,QACA,OAAO,MAAA,EAAQ,oBAAA;AAAA,QACf;AAAA,OACF;AAAA,MACA,qBAAA,EAAuB,yBAAA;AAAA,QACrB,IAAA;AAAA,QACA,OAAO,MAAA,EAAQ,qBAAA;AAAA,QACf;AAAA;AACF,KACF;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEA,eAAe,SAAA,CACb,aACA,MAAA,EACsB;AACtB,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,EAAA,EAAI,OAAO,IAAA;AAEjC,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,OAAO,OAAA,IAAW,KAAA;AAClC,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,OAAO,EAAE,CAAA;AAChD,IAAA,MAAM,SAAS,MAAA,CAAO,SAAA,CAAU,EAAA,CAAG,OAAA,CAAQ,OAAO,EAAE,CAAA;AAEpD,IAAA,MAAM,MAAM,MAAM,OAAA,CAAQ,GAAG,OAAO,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,EAAI;AAAA,MAChD,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA;AAAG,KACnD,CAAA;AAED,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,OAAO,IAAA;AACpB,IAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AAAA,EACzB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;;;AChHO,SAAS,QAAA,CACd,MAAA,EACA,OAAA,EACA,OAAA,GAAmC,EAAC,EACpC;AACA,EAAA,OAAO,OAAO,GAAA,KAAwC;AACpD,IAAA,MAAM,OAAA,GAAU,MAAM,gBAAA,CAAuB,GAAA,EAAK,MAAM,CAAA;AAExD,IAAA,IAAI,CAAC,QAAQ,eAAA,EAAiB;AAC5B,MAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,QAAA;AACzC,MAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,UAAA,EAAY,WAAW,GAAA,CAAI,OAAA,CAAQ,QAAQ,CAAA,CAAE,CAAA;AACtE,MAAA,OAAO,QAAA,CAAS,SAAS,QAAQ,CAAA;AAAA,IACnC;AAEA,IAAA,OAAO,OAAA,CAAQ,KAAK,OAAO,CAAA;AAAA,EAC7B,CAAA;AACF;;;AC1BO,SAAS,eAA+B,UAAA,EAA8B;AAC3E,EAAA,OAAO,eAAe,WAAW,OAAA,EAIX;AACpB,IAAA,MAAM,EAAE,YAAA,EAAa,GAAI,MAAM,OAAO,aAAa,CAAA;AAEnD,IAAA,MAAM,QAAA,GAAW,QAAQ,OAAA,CAAQ,QAAA;AACjC,IAAA,MAAM,UAAA,GAAa,UAAA,CAAW,KAAA,CAAM,UAAA,IAAc,yBAAA;AAClD,IAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,UAAU,CAAA,EAAG,KAAA;AAErD,IAAA,MAAM,eAAA,GAAkB,MAAM,YAAA,CAAa,WAAA,EAAa,WAAW,MAAM,CAAA;AAKzE,IAAA,MAAM,eAAA,GAAkB,UAAA,CAAW,MAAA,EAAQ,SAAA,IAAa,EAAC;AACzD,IAAA,IAAI,gBAAA,CAAiB,QAAA,EAAU,eAAe,CAAA,EAAG;AAC/C,MAAA,IAAI,eAAA,EAAiB;AACnB,QAAA,MAAM,UAAA,GAAa,UAAA,CAAW,MAAA,EAAQ,uBAAA,IAA2B,YAAA;AACjE,QAAA,OAAO,YAAA,CAAa,SAAS,IAAI,GAAA,CAAI,YAAY,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,MAC1E;AACA,MAAA,OAAO,aAAa,IAAA,EAAK;AAAA,IAC3B;AAGA,IAAA,MAAM,YAAA,GAAe,UAAA,CAAW,MAAA,EAAQ,MAAA,IAAU,EAAC;AACnD,IAAA,IAAI,UAAA,CAAW,QAAA,EAAU,YAAY,CAAA,EAAG;AACtC,MAAA,OAAO,aAAa,IAAA,EAAK;AAAA,IAC3B;AAGA,IAAA,MAAM,eAAA,GAAkB,UAAA,CAAW,MAAA,EAAQ,SAAA,IAAa,EAAC;AACzD,IAAA,MAAM,eACJ,eAAA,CAAgB,MAAA,KAAW,CAAA,IAAK,UAAA,CAAW,UAAU,eAAe,CAAA;AAEtE,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,OAAO,aAAa,IAAA,EAAK;AAAA,IAC3B;AAEA,IAAA,IAAI,CAAC,eAAA,EAAiB;AAEpB,MAAA,MAAM,SAAA,GAAY,UAAA,CAAW,MAAA,EAAQ,SAAA,IAAa,QAAA;AAClD,MAAA,OAAO,YAAA,CAAa,SAAS,IAAI,GAAA,CAAI,WAAW,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,IACzE;AAEA,IAAA,OAAO,aAAa,IAAA,EAAK;AAAA,EAC3B,CAAA;AACF;AAIA,eAAe,YAAA,CACb,aACA,MAAA,EACkB;AAClB,EAAA,IAAI,CAAC,aAAa,OAAO,KAAA;AAEzB,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,WAAA,EAAa,MAAM,CAAA;AAC9C,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC9B,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,qBAAA,GAC1B,GAAA,IAAO,OAAO,qBAAA,GACd,KAAA;AACJ,IAAA,OAAO,CAAC,cAAA;AAAA,EACV,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAIA,SAAS,gBAAA,CAAiB,UAAkB,MAAA,EAA2B;AACrE,EAAA,OAAO,UAAA,CAAW,UAAU,MAAM,CAAA;AACpC;AAMA,SAAS,UAAA,CAAW,UAAkB,QAAA,EAA6B;AACjE,EAAA,OAAO,SAAS,IAAA,CAAK,CAAC,YAAY,UAAA,CAAW,QAAA,EAAU,OAAO,CAAC,CAAA;AACjE;AAEA,SAAS,UAAA,CAAW,UAAkB,OAAA,EAA0B;AAC9D,EAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AACzB,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAEhC,IAAA,OAAO,QAAA,KAAa,QAAQ,QAAA,CAAS,UAAA,CAAW,OAAO,GAAG,CAAA,IAAK,QAAA,CAAS,UAAA,CAAW,IAAI,CAAA;AAAA,EACzF;AACA,EAAA,OAAO,QAAA,KAAa,OAAA;AACtB","file":"index.js","sourcesContent":["/**\n * Lightweight symmetric encryption using AES-GCM via the Web Crypto API.\n * Works in both browser and Node.js (>=18) / Edge runtimes.\n */\n\nconst ALGO = \"AES-GCM\";\nconst IV_LENGTH = 12; // bytes\n\nfunction getTextEncoder() {\n return new TextEncoder();\n}\n\nfunction getTextDecoder() {\n return new TextDecoder();\n}\n\nasync function deriveKey(secret: string): Promise<CryptoKey> {\n const raw = getTextEncoder().encode(secret.padEnd(32, \"0\").slice(0, 32));\n return crypto.subtle.importKey(\"raw\", raw, { name: ALGO }, false, [\n \"encrypt\",\n \"decrypt\",\n ]);\n}\n\n/**\n * Encrypts a plaintext string using AES-GCM.\n * Returns a base64url-encoded string: `<iv>.<ciphertext>`\n */\nexport async function encrypt(data: string, secret: string): Promise<string> {\n const key = await deriveKey(secret);\n const ivArray = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n // Ensure we have a plain ArrayBuffer for SubtleCrypto\n const iv = ivArray.buffer.slice(0, IV_LENGTH) as ArrayBuffer;\n const encoded = getTextEncoder().encode(data);\n\n const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);\n\n const ivB64 = bufferToBase64(new Uint8Array(iv));\n const cipherB64 = bufferToBase64(new Uint8Array(cipherBuffer));\n return `${ivB64}.${cipherB64}`;\n}\n\n/**\n * Decrypts a string produced by `encrypt`.\n */\nexport async function decrypt(data: string, secret: string): Promise<string> {\n const [ivB64, cipherB64] = data.split(\".\");\n if (!ivB64 || !cipherB64) {\n throw new Error(\"decrypt: invalid ciphertext format\");\n }\n\n const key = await deriveKey(secret);\n const ivBytes = base64ToBuffer(ivB64);\n const iv = ivBytes.buffer.slice(\n ivBytes.byteOffset,\n ivBytes.byteOffset + ivBytes.byteLength\n ) as ArrayBuffer;\n\n const cipherBytes = base64ToBuffer(cipherB64);\n const cipherBuffer = cipherBytes.buffer.slice(\n cipherBytes.byteOffset,\n cipherBytes.byteOffset + cipherBytes.byteLength\n ) as ArrayBuffer;\n\n const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBuffer);\n\n return getTextDecoder().decode(plainBuffer);\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction bufferToBase64(buffer: Uint8Array): string {\n return btoa(String.fromCharCode(...buffer))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction base64ToBuffer(b64: string): Uint8Array {\n const padded = b64.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded);\n return Uint8Array.from(binary, (c) => c.charCodeAt(0));\n}\n","import type { ExpiryInput, ExpiryStrategy, LoginResponse } from \"../types\";\n\nconst UNIT_MAP: Record<string, number> = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n};\n\n/**\n * Parses an expiry value into seconds.\n * Accepts:\n * - number → treated as seconds\n * - string → e.g. \"15m\", \"2h\", \"2d\", \"7d\", \"1w\"\n *\n * @throws if the format is unrecognised\n */\nexport function parseExpiry(input?: ExpiryInput): number {\n if (input === undefined || input === null) {\n throw new Error(\"parseExpiry: no expiry value provided\");\n }\n\n if (typeof input === \"number\") {\n if (input <= 0) throw new Error(\"parseExpiry: value must be positive\");\n return input;\n }\n\n const trimmed = input.trim();\n\n // Pure numeric string\n if (/^\\d+$/.test(trimmed)) {\n return parseInt(trimmed, 10);\n }\n\n const match = trimmed.match(/^(\\d+(?:\\.\\d+)?)\\s*([smhdw])$/i);\n if (!match) {\n throw new Error(\n `parseExpiry: unrecognised format \"${input}\". ` +\n `Expected a number or a string like \"15m\", \"2h\", \"2d\", \"7d\", \"1w\".`\n );\n }\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n return Math.floor(value * UNIT_MAP[unit]);\n}\n\n/**\n * Safely parses an expiry value, returning a fallback on failure.\n */\nexport function safeParseExpiry(\n input?: ExpiryInput,\n fallbackSeconds = 900\n): number {\n try {\n return parseExpiry(input);\n } catch {\n return fallbackSeconds;\n }\n}\n\n/**\n * Resolves the access token expiry timestamp (ms) from a login response\n * using the configured strategy.\n */\nexport function resolveAccessTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number {\n const now = Date.now();\n\n const fromBackend =\n response.accessTokenExpiresIn ?? response.expiresIn ?? undefined;\n\n if (strategy === \"backend\") {\n if (fromBackend === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"backend\" but API returned no expiry'\n );\n }\n return now + parseExpiry(fromBackend) * 1000;\n }\n\n if (strategy === \"config\") {\n if (configExpiry === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"config\" but no expiry configured'\n );\n }\n return now + parseExpiry(configExpiry) * 1000;\n }\n\n // hybrid: backend first, fallback to config\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n // Last resort: 15 minutes\n return now + 900 * 1000;\n}\n\n/**\n * Resolves the refresh token expiry timestamp (ms).\n */\nexport function resolveRefreshTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number | undefined {\n const now = Date.now();\n const fromBackend = response.refreshTokenExpiresIn;\n\n if (strategy === \"backend\") {\n return fromBackend !== undefined\n ? now + parseExpiry(fromBackend) * 1000\n : undefined;\n }\n\n if (strategy === \"config\") {\n return configExpiry !== undefined\n ? now + parseExpiry(configExpiry) * 1000\n : undefined;\n }\n\n // hybrid\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n return undefined;\n}\n","import type { AuthConfig, AuthSession, AuthTokens, LoginResponse } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n};\n\n/**\n * Retrieves and validates the auth session on the server side.\n * Reads the encrypted session cookie, validates token expiry,\n * and refreshes if needed.\n *\n * Compatible with Next.js App Router (NextRequest) and Pages Router (IncomingMessage).\n *\n * @example\n * // app/dashboard/page.tsx\n * import { getServerSession } from \"next-token-auth/server\";\n *\n * export default async function Page({ request }) {\n * const session = await getServerSession(request, config);\n * if (!session.isAuthenticated) redirect(\"/login\");\n * }\n */\nexport async function getServerSession<User = unknown>(\n req: NextRequest,\n config: AuthConfig<User>\n): Promise<AuthSession<User>> {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = req.cookies.get(cookieName)?.value;\n\n if (!cookieValue) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n let tokens: AuthTokens | null = null;\n\n try {\n const json = await decrypt(cookieValue, config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n const now = Date.now();\n const threshold = (config.refreshThreshold ?? 60) * 1000;\n const accessExpired = now >= tokens.accessTokenExpiresAt - threshold;\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n\n // Both expired → clear session\n if (accessExpired && refreshExpired) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n // Access expired but refresh valid → attempt server-side refresh\n if (accessExpired && !refreshExpired) {\n const refreshed = await serverRefresh<User>(tokens, config);\n if (!refreshed) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n tokens = refreshed;\n }\n\n const user = await fetchUser<User>(tokens.accessToken, config);\n\n return {\n user,\n tokens,\n isAuthenticated: true,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function serverRefresh<User>(\n tokens: AuthTokens,\n config: AuthConfig<User>\n): Promise<AuthTokens | null> {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const refreshPath = config.endpoints.refresh.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${refreshPath}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as LoginResponse<User>;\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(\n data,\n config.expiry?.accessTokenExpiresIn,\n strategy\n ),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(\n data,\n config.expiry?.refreshTokenExpiresIn,\n strategy\n ),\n };\n } catch {\n return null;\n }\n}\n\nasync function fetchUser<User>(\n accessToken: string,\n config: AuthConfig<User>\n): Promise<User | null> {\n if (!config.endpoints.me) return null;\n\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const mePath = config.endpoints.me.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${mePath}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!res.ok) return null;\n return (await res.json()) as User;\n } catch {\n return null;\n }\n}\n","import type { AuthConfig, AuthSession } from \"../types\";\nimport { getServerSession } from \"./getServerSession\";\n\ntype NextRequest = {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string };\n};\n\ntype NextResponse = {\n redirect(url: URL): NextResponse;\n next(): NextResponse;\n};\n\ntype RouteHandler<User = unknown> = (\n req: NextRequest,\n session: AuthSession<User>\n) => Promise<Response> | Response;\n\n/**\n * Higher-order function that wraps a Next.js route handler with auth protection.\n * Redirects unauthenticated requests to the login page.\n *\n * @example\n * // app/api/protected/route.ts\n * export const GET = withAuth(config, async (req, session) => {\n * return Response.json({ user: session.user });\n * });\n */\nexport function withAuth<User = unknown>(\n config: AuthConfig<User>,\n handler: RouteHandler<User>,\n options: { redirectTo?: string } = {}\n) {\n return async (req: NextRequest): Promise<Response> => {\n const session = await getServerSession<User>(req, config);\n\n if (!session.isAuthenticated) {\n const redirectTo = options.redirectTo ?? \"/login\";\n const loginUrl = new URL(redirectTo, `https://${req.nextUrl.pathname}`);\n return Response.redirect(loginUrl);\n }\n\n return handler(req, session);\n };\n}\n","import type { AuthConfig } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport type { AuthTokens } from \"../types\";\n\n/**\n * Next.js middleware factory for route protection.\n *\n * @example\n * // middleware.ts (project root)\n * import { authMiddleware } from \"next-token-auth/server\";\n * import { authConfig } from \"./lib/auth\";\n *\n * export const middleware = authMiddleware(authConfig);\n *\n * export const config = {\n * matcher: [\"/auth/login\", \"/auth/register\", \"/dashboard*\", \"/profile*\"],\n * };\n */\nexport function authMiddleware<User = unknown>(authConfig: AuthConfig<User>) {\n return async function middleware(request: {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string; origin: string };\n url: string;\n }): Promise<Response> {\n const { NextResponse } = await import(\"next/server\");\n\n const pathname = request.nextUrl.pathname;\n const cookieName = authConfig.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = request.cookies.get(cookieName)?.value;\n\n const isAuthenticated = await checkSession(cookieValue, authConfig.secret);\n\n // ── Guest-only routes ────────────────────────────────────────────────────\n // Accessible only when NOT authenticated.\n // Authenticated users are redirected to redirectAuthenticatedTo.\n const guestOnlyRoutes = authConfig.routes?.guestOnly ?? [];\n if (isGuestOnlyRoute(pathname, guestOnlyRoutes)) {\n if (isAuthenticated) {\n const redirectTo = authConfig.routes?.redirectAuthenticatedTo ?? \"/dashboard\";\n return NextResponse.redirect(new URL(redirectTo, request.nextUrl.origin));\n }\n return NextResponse.next();\n }\n\n // ── Public routes ────────────────────────────────────────────────────────\n const publicRoutes = authConfig.routes?.public ?? [];\n if (matchesAny(pathname, publicRoutes)) {\n return NextResponse.next();\n }\n\n // ── Protected routes ─────────────────────────────────────────────────────\n const protectedRoutes = authConfig.routes?.protected ?? [];\n const requiresAuth =\n protectedRoutes.length === 0 || matchesAny(pathname, protectedRoutes);\n\n if (!requiresAuth) {\n return NextResponse.next();\n }\n\n if (!isAuthenticated) {\n // Use the configured login endpoint path, falling back to \"/login\"\n const loginPath = authConfig.routes?.loginPath ?? \"/login\";\n return NextResponse.redirect(new URL(loginPath, request.nextUrl.origin));\n }\n\n return NextResponse.next();\n };\n}\n\n// ─── Session check ────────────────────────────────────────────────────────────\n\nasync function checkSession(\n cookieValue: string | undefined,\n secret: string\n): Promise<boolean> {\n if (!cookieValue) return false;\n\n try {\n const json = await decrypt(cookieValue, secret);\n const tokens = JSON.parse(json) as AuthTokens;\n const now = Date.now();\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n return !refreshExpired;\n } catch {\n return false;\n }\n}\n\n// ─── Route matchers ───────────────────────────────────────────────────────────\n\nfunction isGuestOnlyRoute(pathname: string, routes: string[]): boolean {\n return matchesAny(pathname, routes);\n}\n\n/**\n * Matches a pathname against a list of patterns.\n * Supports wildcards: \"/dashboard*\" matches \"/dashboard\", \"/dashboard/\", \"/dashboard/settings\"\n */\nfunction matchesAny(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchRoute(pathname, pattern));\n}\n\nfunction matchRoute(pathname: string, pattern: string): boolean {\n if (pattern.endsWith(\"*\")) {\n const base = pattern.slice(0, -1); // e.g. \"/dashboard\"\n // matches \"/dashboard\", \"/dashboard/\", \"/dashboard/anything\"\n return pathname === base || pathname.startsWith(base + \"/\") || pathname.startsWith(base);\n }\n return pathname === pattern;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/crypto.ts","../../src/utils/expiry.ts","../../src/server/getServerSession.ts","../../src/server/withAuth.ts","../../src/server/middleware.ts","../../src/server/handlers.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Lightweight symmetric encryption using AES-GCM via the Web Crypto API.\n * Works in both browser and Node.js (>=18) / Edge runtimes.\n */\n\nconst ALGO = \"AES-GCM\";\nconst IV_LENGTH = 12; // bytes\n\nfunction getTextEncoder() {\n return new TextEncoder();\n}\n\nfunction getTextDecoder() {\n return new TextDecoder();\n}\n\nasync function deriveKey(secret: string): Promise<CryptoKey> {\n if (!secret) {\n throw new Error(\n \"[next-token-auth] `secret` is undefined. \" +\n \"If using cookie storage, ensure AUTH_SECRET is set in your environment.\"\n );\n }\n const raw = getTextEncoder().encode(secret.padEnd(32, \"0\").slice(0, 32));\n return crypto.subtle.importKey(\"raw\", raw, { name: ALGO }, false, [\n \"encrypt\",\n \"decrypt\",\n ]);\n}\n\n/**\n * Encrypts a plaintext string using AES-GCM.\n * Returns a base64url-encoded string: `<iv>.<ciphertext>`\n */\nexport async function encrypt(data: string, secret: string): Promise<string> {\n const key = await deriveKey(secret);\n const ivArray = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n // Ensure we have a plain ArrayBuffer for SubtleCrypto\n const iv = ivArray.buffer.slice(0, IV_LENGTH) as ArrayBuffer;\n const encoded = getTextEncoder().encode(data);\n\n const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);\n\n const ivB64 = bufferToBase64(new Uint8Array(iv));\n const cipherB64 = bufferToBase64(new Uint8Array(cipherBuffer));\n return `${ivB64}.${cipherB64}`;\n}\n\n/**\n * Decrypts a string produced by `encrypt`.\n */\nexport async function decrypt(data: string, secret: string): Promise<string> {\n const [ivB64, cipherB64] = data.split(\".\");\n if (!ivB64 || !cipherB64) {\n throw new Error(\"decrypt: invalid ciphertext format\");\n }\n\n const key = await deriveKey(secret);\n const ivBytes = base64ToBuffer(ivB64);\n const iv = ivBytes.buffer.slice(\n ivBytes.byteOffset,\n ivBytes.byteOffset + ivBytes.byteLength\n ) as ArrayBuffer;\n\n const cipherBytes = base64ToBuffer(cipherB64);\n const cipherBuffer = cipherBytes.buffer.slice(\n cipherBytes.byteOffset,\n cipherBytes.byteOffset + cipherBytes.byteLength\n ) as ArrayBuffer;\n\n const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBuffer);\n\n return getTextDecoder().decode(plainBuffer);\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction bufferToBase64(buffer: Uint8Array): string {\n return btoa(String.fromCharCode(...buffer))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction base64ToBuffer(b64: string): Uint8Array {\n const padded = b64.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded);\n return Uint8Array.from(binary, (c) => c.charCodeAt(0));\n}\n","import type { ExpiryInput, ExpiryStrategy, LoginResponse } from \"../types\";\n\nconst UNIT_MAP: Record<string, number> = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n};\n\n/**\n * Parses an expiry value into seconds.\n * Accepts:\n * - number → treated as seconds\n * - string → e.g. \"15m\", \"2h\", \"2d\", \"7d\", \"1w\"\n *\n * @throws if the format is unrecognised\n */\nexport function parseExpiry(input?: ExpiryInput): number {\n if (input === undefined || input === null) {\n throw new Error(\"parseExpiry: no expiry value provided\");\n }\n\n if (typeof input === \"number\") {\n if (input <= 0) throw new Error(\"parseExpiry: value must be positive\");\n return input;\n }\n\n const trimmed = input.trim();\n\n // Pure numeric string\n if (/^\\d+$/.test(trimmed)) {\n return parseInt(trimmed, 10);\n }\n\n const match = trimmed.match(/^(\\d+(?:\\.\\d+)?)\\s*([smhdw])$/i);\n if (!match) {\n throw new Error(\n `parseExpiry: unrecognised format \"${input}\". ` +\n `Expected a number or a string like \"15m\", \"2h\", \"2d\", \"7d\", \"1w\".`\n );\n }\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n return Math.floor(value * UNIT_MAP[unit]);\n}\n\n/**\n * Safely parses an expiry value, returning a fallback on failure.\n */\nexport function safeParseExpiry(\n input?: ExpiryInput,\n fallbackSeconds = 900\n): number {\n try {\n return parseExpiry(input);\n } catch {\n return fallbackSeconds;\n }\n}\n\n/**\n * Resolves the access token expiry timestamp (ms) from a login response\n * using the configured strategy.\n */\nexport function resolveAccessTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number {\n const now = Date.now();\n\n const fromBackend =\n response.accessTokenExpiresIn ?? response.expiresIn ?? undefined;\n\n if (strategy === \"backend\") {\n if (fromBackend === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"backend\" but API returned no expiry'\n );\n }\n return now + parseExpiry(fromBackend) * 1000;\n }\n\n if (strategy === \"config\") {\n if (configExpiry === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"config\" but no expiry configured'\n );\n }\n return now + parseExpiry(configExpiry) * 1000;\n }\n\n // hybrid: backend first, fallback to config\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n // Last resort: 15 minutes\n return now + 900 * 1000;\n}\n\n/**\n * Resolves the refresh token expiry timestamp (ms).\n */\nexport function resolveRefreshTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number | undefined {\n const now = Date.now();\n const fromBackend = response.refreshTokenExpiresIn;\n\n if (strategy === \"backend\") {\n return fromBackend !== undefined\n ? now + parseExpiry(fromBackend) * 1000\n : undefined;\n }\n\n if (strategy === \"config\") {\n return configExpiry !== undefined\n ? now + parseExpiry(configExpiry) * 1000\n : undefined;\n }\n\n // hybrid\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n return undefined;\n}\n","import type { AuthConfig, AuthSession, AuthTokens, LoginResponse } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n};\n\n/**\n * Retrieves and validates the auth session on the server side.\n * Reads the encrypted session cookie, validates token expiry,\n * and refreshes if needed.\n *\n * Compatible with Next.js App Router (NextRequest) and Pages Router (IncomingMessage).\n *\n * @example\n * // app/dashboard/page.tsx\n * import { getServerSession } from \"next-token-auth/server\";\n *\n * export default async function Page({ request }) {\n * const session = await getServerSession(request, config);\n * if (!session.isAuthenticated) redirect(\"/login\");\n * }\n */\nexport async function getServerSession<User = unknown>(\n req: NextRequest,\n config: AuthConfig<User>\n): Promise<AuthSession<User>> {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = req.cookies.get(cookieName)?.value;\n\n if (!cookieValue) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n let tokens: AuthTokens | null = null;\n\n try {\n const json = await decrypt(cookieValue, config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n const now = Date.now();\n const threshold = (config.refreshThreshold ?? 60) * 1000;\n const accessExpired = now >= tokens.accessTokenExpiresAt - threshold;\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n\n // Both expired → clear session\n if (accessExpired && refreshExpired) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n // Access expired but refresh valid → attempt server-side refresh\n if (accessExpired && !refreshExpired) {\n const refreshed = await serverRefresh<User>(tokens, config);\n if (!refreshed) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n tokens = refreshed;\n }\n\n const user = await fetchUser<User>(tokens.accessToken, config);\n\n return {\n user,\n tokens,\n isAuthenticated: true,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function serverRefresh<User>(\n tokens: AuthTokens,\n config: AuthConfig<User>\n): Promise<AuthTokens | null> {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const refreshPath = config.endpoints.refresh.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${refreshPath}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as LoginResponse<User>;\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(\n data,\n config.expiry?.accessTokenExpiresIn,\n strategy\n ),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(\n data,\n config.expiry?.refreshTokenExpiresIn,\n strategy\n ),\n };\n } catch {\n return null;\n }\n}\n\nasync function fetchUser<User>(\n accessToken: string,\n config: AuthConfig<User>\n): Promise<User | null> {\n if (!config.endpoints.me) return null;\n\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const mePath = config.endpoints.me.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${mePath}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!res.ok) return null;\n return (await res.json()) as User;\n } catch {\n return null;\n }\n}\n","import type { AuthConfig, AuthSession } from \"../types\";\nimport { getServerSession } from \"./getServerSession\";\n\ntype NextRequest = {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string };\n};\n\ntype NextResponse = {\n redirect(url: URL): NextResponse;\n next(): NextResponse;\n};\n\ntype RouteHandler<User = unknown> = (\n req: NextRequest,\n session: AuthSession<User>\n) => Promise<Response> | Response;\n\n/**\n * Higher-order function that wraps a Next.js route handler with auth protection.\n * Redirects unauthenticated requests to the login page.\n *\n * @example\n * // app/api/protected/route.ts\n * export const GET = withAuth(config, async (req, session) => {\n * return Response.json({ user: session.user });\n * });\n */\nexport function withAuth<User = unknown>(\n config: AuthConfig<User>,\n handler: RouteHandler<User>,\n options: { redirectTo?: string } = {}\n) {\n return async (req: NextRequest): Promise<Response> => {\n const session = await getServerSession<User>(req, config);\n\n if (!session.isAuthenticated) {\n const redirectTo = options.redirectTo ?? \"/login\";\n const loginUrl = new URL(redirectTo, `https://${req.nextUrl.pathname}`);\n return Response.redirect(loginUrl);\n }\n\n return handler(req, session);\n };\n}\n","import type { AuthConfig } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport type { AuthTokens } from \"../types\";\n\n/**\n * Next.js middleware factory for route protection.\n *\n * @example\n * // middleware.ts (project root)\n * import { authMiddleware } from \"next-token-auth/server\";\n * import { authConfig } from \"./lib/auth\";\n *\n * export const middleware = authMiddleware(authConfig);\n *\n * export const config = {\n * matcher: [\"/auth/login\", \"/auth/register\", \"/dashboard*\", \"/profile*\"],\n * };\n */\nexport function authMiddleware<User = unknown>(authConfig: AuthConfig<User>) {\n return async function middleware(request: {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string; origin: string };\n url: string;\n }): Promise<Response> {\n const { NextResponse } = await import(\"next/server\");\n\n const pathname = request.nextUrl.pathname;\n const cookieName = authConfig.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = request.cookies.get(cookieName)?.value;\n\n const isAuthenticated = await checkSession(cookieValue, authConfig.secret);\n\n // ── Guest-only routes ────────────────────────────────────────────────────\n // Accessible only when NOT authenticated.\n // Authenticated users are redirected to redirectAuthenticatedTo.\n const guestOnlyRoutes = authConfig.routes?.guestOnly ?? [];\n if (isGuestOnlyRoute(pathname, guestOnlyRoutes)) {\n if (isAuthenticated) {\n const redirectTo = authConfig.routes?.redirectAuthenticatedTo ?? \"/dashboard\";\n return NextResponse.redirect(new URL(redirectTo, request.nextUrl.origin));\n }\n return NextResponse.next();\n }\n\n // ── Public routes ────────────────────────────────────────────────────────\n const publicRoutes = authConfig.routes?.public ?? [];\n if (matchesAny(pathname, publicRoutes)) {\n return NextResponse.next();\n }\n\n // ── Protected routes ─────────────────────────────────────────────────────\n const protectedRoutes = authConfig.routes?.protected ?? [];\n const requiresAuth =\n protectedRoutes.length === 0 || matchesAny(pathname, protectedRoutes);\n\n if (!requiresAuth) {\n return NextResponse.next();\n }\n\n if (!isAuthenticated) {\n // Use the configured login endpoint path, falling back to \"/login\"\n const loginPath = authConfig.routes?.loginPath ?? \"/login\";\n return NextResponse.redirect(new URL(loginPath, request.nextUrl.origin));\n }\n\n return NextResponse.next();\n };\n}\n\n// ─── Session check ────────────────────────────────────────────────────────────\n\nasync function checkSession(\n cookieValue: string | undefined,\n secret: string\n): Promise<boolean> {\n if (!cookieValue) return false;\n\n try {\n const json = await decrypt(cookieValue, secret);\n const tokens = JSON.parse(json) as AuthTokens;\n const now = Date.now();\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n return !refreshExpired;\n } catch {\n return false;\n }\n}\n\n// ─── Route matchers ───────────────────────────────────────────────────────────\n\nfunction isGuestOnlyRoute(pathname: string, routes: string[]): boolean {\n return matchesAny(pathname, routes);\n}\n\n/**\n * Matches a pathname against a list of patterns.\n * Supports wildcards: \"/dashboard*\" matches \"/dashboard\", \"/dashboard/\", \"/dashboard/settings\"\n */\nfunction matchesAny(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchRoute(pathname, pattern));\n}\n\nfunction matchRoute(pathname: string, pattern: string): boolean {\n if (pattern.endsWith(\"*\")) {\n const base = pattern.slice(0, -1); // e.g. \"/dashboard\"\n // matches \"/dashboard\", \"/dashboard/\", \"/dashboard/anything\"\n return pathname === base || pathname.startsWith(base + \"/\") || pathname.startsWith(base);\n }\n return pathname === pattern;\n}\n","import type { AuthConfig, AuthTokens, LoginResponse } from \"../types\";\nimport { encrypt, decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n json(): Promise<unknown>;\n cookies: { get(name: string): { value: string } | undefined };\n};\n\n/**\n * Creates Next.js Route Handlers for login, logout, session, and refresh.\n * Mount these at `app/api/auth/[action]/route.ts`.\n *\n * All encryption happens server-side — the secret never leaves the server.\n *\n * @example\n * // app/api/auth/[action]/route.ts\n * import { createAuthHandlers } from \"next-token-auth/server\";\n * import { authConfig } from \"@/lib/auth\";\n *\n * export const { GET, POST } = createAuthHandlers(authConfig);\n */\nexport function createAuthHandlers<User = unknown>(config: AuthConfig<User>) {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n\n return {\n POST: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/login ─────────────────────────────────────────────────────\n if (action === \"login\") {\n const body = await req.json();\n const fetchFn = config.fetchFn ?? fetch;\n\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.login}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n const text = await res.text();\n return NextResponse.json({ error: text }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const tokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(tokens), config.secret);\n const maxAge = tokens.refreshTokenExpiresAt\n ? Math.floor((tokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n return NextResponse.json(\n { ok: true, user: data.user },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n // ── /api/auth/logout ────────────────────────────────────────────────────\n if (action === \"logout\") {\n if (config.endpoints.logout) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n await fetchFn(`${config.baseUrl}${config.endpoints.logout}`, {\n method: \"POST\",\n });\n } catch {\n // Best-effort\n }\n }\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": `${cookieName}=; Max-Age=0; Path=/`,\n },\n }\n );\n }\n\n // ── /api/auth/refresh ───────────────────────────────────────────────────\n if (action === \"refresh\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ error: \"No session\" }, { status: 401 });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ error: \"Invalid session\" }, { status: 401 });\n }\n\n if (tokens.refreshTokenExpiresAt && Date.now() >= tokens.refreshTokenExpiresAt) {\n return NextResponse.json({ error: \"Refresh token expired\" }, { status: 401 });\n }\n\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.refresh}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) {\n return NextResponse.json({ error: \"Refresh failed\" }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const newTokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(newTokens), config.secret);\n const maxAge = newTokens.refreshTokenExpiresAt\n ? Math.floor((newTokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n\n GET: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/session ───────────────────────────────────────────────────\n if (action === \"session\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? Date.now() >= tokens.refreshTokenExpiresAt\n : false;\n\n if (refreshExpired) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n // Fetch user from backend\n let user: User | null = null;\n if (config.endpoints.me) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.me}`, {\n headers: { Authorization: `Bearer ${tokens.accessToken}` },\n });\n if (res.ok) user = (await res.json()) as User;\n } catch {\n // User fetch failed — still return authenticated: true if tokens are valid\n }\n }\n\n return NextResponse.json({ user, isAuthenticated: true });\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction buildTokens<User>(\n data: LoginResponse<User>,\n config: AuthConfig<User>\n): AuthTokens {\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n const configAccess = config.expiry?.accessTokenExpiresIn;\n const configRefresh = config.expiry?.refreshTokenExpiresIn;\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(data, configAccess, strategy),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(data, configRefresh, strategy),\n };\n}\n"]}
|
package/dist/server/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/utils/crypto.ts
|
|
2
2
|
var ALGO = "AES-GCM";
|
|
3
|
+
var IV_LENGTH = 12;
|
|
3
4
|
function getTextEncoder() {
|
|
4
5
|
return new TextEncoder();
|
|
5
6
|
}
|
|
@@ -7,12 +8,27 @@ function getTextDecoder() {
|
|
|
7
8
|
return new TextDecoder();
|
|
8
9
|
}
|
|
9
10
|
async function deriveKey(secret) {
|
|
11
|
+
if (!secret) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
"[next-token-auth] `secret` is undefined. If using cookie storage, ensure AUTH_SECRET is set in your environment."
|
|
14
|
+
);
|
|
15
|
+
}
|
|
10
16
|
const raw = getTextEncoder().encode(secret.padEnd(32, "0").slice(0, 32));
|
|
11
17
|
return crypto.subtle.importKey("raw", raw, { name: ALGO }, false, [
|
|
12
18
|
"encrypt",
|
|
13
19
|
"decrypt"
|
|
14
20
|
]);
|
|
15
21
|
}
|
|
22
|
+
async function encrypt(data, secret) {
|
|
23
|
+
const key = await deriveKey(secret);
|
|
24
|
+
const ivArray = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
|
|
25
|
+
const iv = ivArray.buffer.slice(0, IV_LENGTH);
|
|
26
|
+
const encoded = getTextEncoder().encode(data);
|
|
27
|
+
const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);
|
|
28
|
+
const ivB64 = bufferToBase64(new Uint8Array(iv));
|
|
29
|
+
const cipherB64 = bufferToBase64(new Uint8Array(cipherBuffer));
|
|
30
|
+
return `${ivB64}.${cipherB64}`;
|
|
31
|
+
}
|
|
16
32
|
async function decrypt(data, secret) {
|
|
17
33
|
const [ivB64, cipherB64] = data.split(".");
|
|
18
34
|
if (!ivB64 || !cipherB64) {
|
|
@@ -32,6 +48,9 @@ async function decrypt(data, secret) {
|
|
|
32
48
|
const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBuffer);
|
|
33
49
|
return getTextDecoder().decode(plainBuffer);
|
|
34
50
|
}
|
|
51
|
+
function bufferToBase64(buffer) {
|
|
52
|
+
return btoa(String.fromCharCode(...buffer)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
53
|
+
}
|
|
35
54
|
function base64ToBuffer(b64) {
|
|
36
55
|
const padded = b64.replace(/-/g, "+").replace(/_/g, "/");
|
|
37
56
|
const binary = atob(padded);
|
|
@@ -273,6 +292,164 @@ function matchRoute(pathname, pattern) {
|
|
|
273
292
|
return pathname === pattern;
|
|
274
293
|
}
|
|
275
294
|
|
|
276
|
-
|
|
295
|
+
// src/server/handlers.ts
|
|
296
|
+
function createAuthHandlers(config) {
|
|
297
|
+
const cookieName = config.token.cookieName ?? "next-token-auth.session";
|
|
298
|
+
return {
|
|
299
|
+
POST: async (req, context) => {
|
|
300
|
+
const { NextResponse } = await import('next/server');
|
|
301
|
+
const params = await Promise.resolve(context.params);
|
|
302
|
+
const action = params.action;
|
|
303
|
+
if (action === "login") {
|
|
304
|
+
const body = await req.json();
|
|
305
|
+
const fetchFn = config.fetchFn ?? fetch;
|
|
306
|
+
const res = await fetchFn(`${config.baseUrl}${config.endpoints.login}`, {
|
|
307
|
+
method: "POST",
|
|
308
|
+
headers: { "Content-Type": "application/json" },
|
|
309
|
+
body: JSON.stringify(body)
|
|
310
|
+
});
|
|
311
|
+
if (!res.ok) {
|
|
312
|
+
const text = await res.text();
|
|
313
|
+
return NextResponse.json({ error: text }, { status: res.status });
|
|
314
|
+
}
|
|
315
|
+
const data = await res.json();
|
|
316
|
+
const tokens = buildTokens(data, config);
|
|
317
|
+
const encrypted = await encrypt(JSON.stringify(tokens), config.secret);
|
|
318
|
+
const maxAge = tokens.refreshTokenExpiresAt ? Math.floor((tokens.refreshTokenExpiresAt - Date.now()) / 1e3) : 604800;
|
|
319
|
+
const secure = config.token.secure !== false ? "Secure; " : "";
|
|
320
|
+
const sameSite = config.token.sameSite ?? "lax";
|
|
321
|
+
return NextResponse.json(
|
|
322
|
+
{ ok: true, user: data.user },
|
|
323
|
+
{
|
|
324
|
+
headers: {
|
|
325
|
+
"Set-Cookie": [
|
|
326
|
+
`${cookieName}=${encodeURIComponent(encrypted)}`,
|
|
327
|
+
`HttpOnly`,
|
|
328
|
+
`Max-Age=${maxAge}`,
|
|
329
|
+
`Path=/`,
|
|
330
|
+
`SameSite=${sameSite}`,
|
|
331
|
+
secure
|
|
332
|
+
].filter(Boolean).join("; ")
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
if (action === "logout") {
|
|
338
|
+
if (config.endpoints.logout) {
|
|
339
|
+
try {
|
|
340
|
+
const fetchFn = config.fetchFn ?? fetch;
|
|
341
|
+
await fetchFn(`${config.baseUrl}${config.endpoints.logout}`, {
|
|
342
|
+
method: "POST"
|
|
343
|
+
});
|
|
344
|
+
} catch {
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return NextResponse.json(
|
|
348
|
+
{ ok: true },
|
|
349
|
+
{
|
|
350
|
+
headers: {
|
|
351
|
+
"Set-Cookie": `${cookieName}=; Max-Age=0; Path=/`
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
if (action === "refresh") {
|
|
357
|
+
const raw = req.cookies.get(cookieName)?.value;
|
|
358
|
+
if (!raw) {
|
|
359
|
+
return NextResponse.json({ error: "No session" }, { status: 401 });
|
|
360
|
+
}
|
|
361
|
+
let tokens;
|
|
362
|
+
try {
|
|
363
|
+
const json = await decrypt(decodeURIComponent(raw), config.secret);
|
|
364
|
+
tokens = JSON.parse(json);
|
|
365
|
+
} catch {
|
|
366
|
+
return NextResponse.json({ error: "Invalid session" }, { status: 401 });
|
|
367
|
+
}
|
|
368
|
+
if (tokens.refreshTokenExpiresAt && Date.now() >= tokens.refreshTokenExpiresAt) {
|
|
369
|
+
return NextResponse.json({ error: "Refresh token expired" }, { status: 401 });
|
|
370
|
+
}
|
|
371
|
+
const fetchFn = config.fetchFn ?? fetch;
|
|
372
|
+
const res = await fetchFn(`${config.baseUrl}${config.endpoints.refresh}`, {
|
|
373
|
+
method: "POST",
|
|
374
|
+
headers: { "Content-Type": "application/json" },
|
|
375
|
+
body: JSON.stringify({ refreshToken: tokens.refreshToken })
|
|
376
|
+
});
|
|
377
|
+
if (!res.ok) {
|
|
378
|
+
return NextResponse.json({ error: "Refresh failed" }, { status: res.status });
|
|
379
|
+
}
|
|
380
|
+
const data = await res.json();
|
|
381
|
+
const newTokens = buildTokens(data, config);
|
|
382
|
+
const encrypted = await encrypt(JSON.stringify(newTokens), config.secret);
|
|
383
|
+
const maxAge = newTokens.refreshTokenExpiresAt ? Math.floor((newTokens.refreshTokenExpiresAt - Date.now()) / 1e3) : 604800;
|
|
384
|
+
const secure = config.token.secure !== false ? "Secure; " : "";
|
|
385
|
+
const sameSite = config.token.sameSite ?? "lax";
|
|
386
|
+
return NextResponse.json(
|
|
387
|
+
{ ok: true },
|
|
388
|
+
{
|
|
389
|
+
headers: {
|
|
390
|
+
"Set-Cookie": [
|
|
391
|
+
`${cookieName}=${encodeURIComponent(encrypted)}`,
|
|
392
|
+
`HttpOnly`,
|
|
393
|
+
`Max-Age=${maxAge}`,
|
|
394
|
+
`Path=/`,
|
|
395
|
+
`SameSite=${sameSite}`,
|
|
396
|
+
secure
|
|
397
|
+
].filter(Boolean).join("; ")
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
return NextResponse.json({ error: "Unknown action" }, { status: 400 });
|
|
403
|
+
},
|
|
404
|
+
GET: async (req, context) => {
|
|
405
|
+
const { NextResponse } = await import('next/server');
|
|
406
|
+
const params = await Promise.resolve(context.params);
|
|
407
|
+
const action = params.action;
|
|
408
|
+
if (action === "session") {
|
|
409
|
+
const raw = req.cookies.get(cookieName)?.value;
|
|
410
|
+
if (!raw) {
|
|
411
|
+
return NextResponse.json({ user: null, isAuthenticated: false });
|
|
412
|
+
}
|
|
413
|
+
let tokens;
|
|
414
|
+
try {
|
|
415
|
+
const json = await decrypt(decodeURIComponent(raw), config.secret);
|
|
416
|
+
tokens = JSON.parse(json);
|
|
417
|
+
} catch {
|
|
418
|
+
return NextResponse.json({ user: null, isAuthenticated: false });
|
|
419
|
+
}
|
|
420
|
+
const refreshExpired = tokens.refreshTokenExpiresAt ? Date.now() >= tokens.refreshTokenExpiresAt : false;
|
|
421
|
+
if (refreshExpired) {
|
|
422
|
+
return NextResponse.json({ user: null, isAuthenticated: false });
|
|
423
|
+
}
|
|
424
|
+
let user = null;
|
|
425
|
+
if (config.endpoints.me) {
|
|
426
|
+
try {
|
|
427
|
+
const fetchFn = config.fetchFn ?? fetch;
|
|
428
|
+
const res = await fetchFn(`${config.baseUrl}${config.endpoints.me}`, {
|
|
429
|
+
headers: { Authorization: `Bearer ${tokens.accessToken}` }
|
|
430
|
+
});
|
|
431
|
+
if (res.ok) user = await res.json();
|
|
432
|
+
} catch {
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return NextResponse.json({ user, isAuthenticated: true });
|
|
436
|
+
}
|
|
437
|
+
return NextResponse.json({ error: "Unknown action" }, { status: 400 });
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
function buildTokens(data, config) {
|
|
442
|
+
const strategy = config.expiry?.strategy ?? "hybrid";
|
|
443
|
+
const configAccess = config.expiry?.accessTokenExpiresIn;
|
|
444
|
+
const configRefresh = config.expiry?.refreshTokenExpiresIn;
|
|
445
|
+
return {
|
|
446
|
+
accessToken: data.accessToken,
|
|
447
|
+
refreshToken: data.refreshToken,
|
|
448
|
+
accessTokenExpiresAt: resolveAccessTokenExpiry(data, configAccess, strategy),
|
|
449
|
+
refreshTokenExpiresAt: resolveRefreshTokenExpiry(data, configRefresh, strategy)
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export { authMiddleware, createAuthHandlers, getServerSession, withAuth };
|
|
277
454
|
//# sourceMappingURL=index.mjs.map
|
|
278
455
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/crypto.ts","../../src/utils/expiry.ts","../../src/server/getServerSession.ts","../../src/server/withAuth.ts","../../src/server/middleware.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Lightweight symmetric encryption using AES-GCM via the Web Crypto API.\n * Works in both browser and Node.js (>=18) / Edge runtimes.\n */\n\nconst ALGO = \"AES-GCM\";\nconst IV_LENGTH = 12; // bytes\n\nfunction getTextEncoder() {\n return new TextEncoder();\n}\n\nfunction getTextDecoder() {\n return new TextDecoder();\n}\n\nasync function deriveKey(secret: string): Promise<CryptoKey> {\n const raw = getTextEncoder().encode(secret.padEnd(32, \"0\").slice(0, 32));\n return crypto.subtle.importKey(\"raw\", raw, { name: ALGO }, false, [\n \"encrypt\",\n \"decrypt\",\n ]);\n}\n\n/**\n * Encrypts a plaintext string using AES-GCM.\n * Returns a base64url-encoded string: `<iv>.<ciphertext>`\n */\nexport async function encrypt(data: string, secret: string): Promise<string> {\n const key = await deriveKey(secret);\n const ivArray = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n // Ensure we have a plain ArrayBuffer for SubtleCrypto\n const iv = ivArray.buffer.slice(0, IV_LENGTH) as ArrayBuffer;\n const encoded = getTextEncoder().encode(data);\n\n const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);\n\n const ivB64 = bufferToBase64(new Uint8Array(iv));\n const cipherB64 = bufferToBase64(new Uint8Array(cipherBuffer));\n return `${ivB64}.${cipherB64}`;\n}\n\n/**\n * Decrypts a string produced by `encrypt`.\n */\nexport async function decrypt(data: string, secret: string): Promise<string> {\n const [ivB64, cipherB64] = data.split(\".\");\n if (!ivB64 || !cipherB64) {\n throw new Error(\"decrypt: invalid ciphertext format\");\n }\n\n const key = await deriveKey(secret);\n const ivBytes = base64ToBuffer(ivB64);\n const iv = ivBytes.buffer.slice(\n ivBytes.byteOffset,\n ivBytes.byteOffset + ivBytes.byteLength\n ) as ArrayBuffer;\n\n const cipherBytes = base64ToBuffer(cipherB64);\n const cipherBuffer = cipherBytes.buffer.slice(\n cipherBytes.byteOffset,\n cipherBytes.byteOffset + cipherBytes.byteLength\n ) as ArrayBuffer;\n\n const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBuffer);\n\n return getTextDecoder().decode(plainBuffer);\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction bufferToBase64(buffer: Uint8Array): string {\n return btoa(String.fromCharCode(...buffer))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction base64ToBuffer(b64: string): Uint8Array {\n const padded = b64.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded);\n return Uint8Array.from(binary, (c) => c.charCodeAt(0));\n}\n","import type { ExpiryInput, ExpiryStrategy, LoginResponse } from \"../types\";\n\nconst UNIT_MAP: Record<string, number> = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n};\n\n/**\n * Parses an expiry value into seconds.\n * Accepts:\n * - number → treated as seconds\n * - string → e.g. \"15m\", \"2h\", \"2d\", \"7d\", \"1w\"\n *\n * @throws if the format is unrecognised\n */\nexport function parseExpiry(input?: ExpiryInput): number {\n if (input === undefined || input === null) {\n throw new Error(\"parseExpiry: no expiry value provided\");\n }\n\n if (typeof input === \"number\") {\n if (input <= 0) throw new Error(\"parseExpiry: value must be positive\");\n return input;\n }\n\n const trimmed = input.trim();\n\n // Pure numeric string\n if (/^\\d+$/.test(trimmed)) {\n return parseInt(trimmed, 10);\n }\n\n const match = trimmed.match(/^(\\d+(?:\\.\\d+)?)\\s*([smhdw])$/i);\n if (!match) {\n throw new Error(\n `parseExpiry: unrecognised format \"${input}\". ` +\n `Expected a number or a string like \"15m\", \"2h\", \"2d\", \"7d\", \"1w\".`\n );\n }\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n return Math.floor(value * UNIT_MAP[unit]);\n}\n\n/**\n * Safely parses an expiry value, returning a fallback on failure.\n */\nexport function safeParseExpiry(\n input?: ExpiryInput,\n fallbackSeconds = 900\n): number {\n try {\n return parseExpiry(input);\n } catch {\n return fallbackSeconds;\n }\n}\n\n/**\n * Resolves the access token expiry timestamp (ms) from a login response\n * using the configured strategy.\n */\nexport function resolveAccessTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number {\n const now = Date.now();\n\n const fromBackend =\n response.accessTokenExpiresIn ?? response.expiresIn ?? undefined;\n\n if (strategy === \"backend\") {\n if (fromBackend === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"backend\" but API returned no expiry'\n );\n }\n return now + parseExpiry(fromBackend) * 1000;\n }\n\n if (strategy === \"config\") {\n if (configExpiry === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"config\" but no expiry configured'\n );\n }\n return now + parseExpiry(configExpiry) * 1000;\n }\n\n // hybrid: backend first, fallback to config\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n // Last resort: 15 minutes\n return now + 900 * 1000;\n}\n\n/**\n * Resolves the refresh token expiry timestamp (ms).\n */\nexport function resolveRefreshTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number | undefined {\n const now = Date.now();\n const fromBackend = response.refreshTokenExpiresIn;\n\n if (strategy === \"backend\") {\n return fromBackend !== undefined\n ? now + parseExpiry(fromBackend) * 1000\n : undefined;\n }\n\n if (strategy === \"config\") {\n return configExpiry !== undefined\n ? now + parseExpiry(configExpiry) * 1000\n : undefined;\n }\n\n // hybrid\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n return undefined;\n}\n","import type { AuthConfig, AuthSession, AuthTokens, LoginResponse } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n};\n\n/**\n * Retrieves and validates the auth session on the server side.\n * Reads the encrypted session cookie, validates token expiry,\n * and refreshes if needed.\n *\n * Compatible with Next.js App Router (NextRequest) and Pages Router (IncomingMessage).\n *\n * @example\n * // app/dashboard/page.tsx\n * import { getServerSession } from \"next-token-auth/server\";\n *\n * export default async function Page({ request }) {\n * const session = await getServerSession(request, config);\n * if (!session.isAuthenticated) redirect(\"/login\");\n * }\n */\nexport async function getServerSession<User = unknown>(\n req: NextRequest,\n config: AuthConfig<User>\n): Promise<AuthSession<User>> {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = req.cookies.get(cookieName)?.value;\n\n if (!cookieValue) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n let tokens: AuthTokens | null = null;\n\n try {\n const json = await decrypt(cookieValue, config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n const now = Date.now();\n const threshold = (config.refreshThreshold ?? 60) * 1000;\n const accessExpired = now >= tokens.accessTokenExpiresAt - threshold;\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n\n // Both expired → clear session\n if (accessExpired && refreshExpired) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n // Access expired but refresh valid → attempt server-side refresh\n if (accessExpired && !refreshExpired) {\n const refreshed = await serverRefresh<User>(tokens, config);\n if (!refreshed) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n tokens = refreshed;\n }\n\n const user = await fetchUser<User>(tokens.accessToken, config);\n\n return {\n user,\n tokens,\n isAuthenticated: true,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function serverRefresh<User>(\n tokens: AuthTokens,\n config: AuthConfig<User>\n): Promise<AuthTokens | null> {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const refreshPath = config.endpoints.refresh.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${refreshPath}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as LoginResponse<User>;\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(\n data,\n config.expiry?.accessTokenExpiresIn,\n strategy\n ),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(\n data,\n config.expiry?.refreshTokenExpiresIn,\n strategy\n ),\n };\n } catch {\n return null;\n }\n}\n\nasync function fetchUser<User>(\n accessToken: string,\n config: AuthConfig<User>\n): Promise<User | null> {\n if (!config.endpoints.me) return null;\n\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const mePath = config.endpoints.me.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${mePath}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!res.ok) return null;\n return (await res.json()) as User;\n } catch {\n return null;\n }\n}\n","import type { AuthConfig, AuthSession } from \"../types\";\nimport { getServerSession } from \"./getServerSession\";\n\ntype NextRequest = {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string };\n};\n\ntype NextResponse = {\n redirect(url: URL): NextResponse;\n next(): NextResponse;\n};\n\ntype RouteHandler<User = unknown> = (\n req: NextRequest,\n session: AuthSession<User>\n) => Promise<Response> | Response;\n\n/**\n * Higher-order function that wraps a Next.js route handler with auth protection.\n * Redirects unauthenticated requests to the login page.\n *\n * @example\n * // app/api/protected/route.ts\n * export const GET = withAuth(config, async (req, session) => {\n * return Response.json({ user: session.user });\n * });\n */\nexport function withAuth<User = unknown>(\n config: AuthConfig<User>,\n handler: RouteHandler<User>,\n options: { redirectTo?: string } = {}\n) {\n return async (req: NextRequest): Promise<Response> => {\n const session = await getServerSession<User>(req, config);\n\n if (!session.isAuthenticated) {\n const redirectTo = options.redirectTo ?? \"/login\";\n const loginUrl = new URL(redirectTo, `https://${req.nextUrl.pathname}`);\n return Response.redirect(loginUrl);\n }\n\n return handler(req, session);\n };\n}\n","import type { AuthConfig } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport type { AuthTokens } from \"../types\";\n\n/**\n * Next.js middleware factory for route protection.\n *\n * @example\n * // middleware.ts (project root)\n * import { authMiddleware } from \"next-token-auth/server\";\n * import { authConfig } from \"./lib/auth\";\n *\n * export const middleware = authMiddleware(authConfig);\n *\n * export const config = {\n * matcher: [\"/auth/login\", \"/auth/register\", \"/dashboard*\", \"/profile*\"],\n * };\n */\nexport function authMiddleware<User = unknown>(authConfig: AuthConfig<User>) {\n return async function middleware(request: {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string; origin: string };\n url: string;\n }): Promise<Response> {\n const { NextResponse } = await import(\"next/server\");\n\n const pathname = request.nextUrl.pathname;\n const cookieName = authConfig.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = request.cookies.get(cookieName)?.value;\n\n const isAuthenticated = await checkSession(cookieValue, authConfig.secret);\n\n // ── Guest-only routes ────────────────────────────────────────────────────\n // Accessible only when NOT authenticated.\n // Authenticated users are redirected to redirectAuthenticatedTo.\n const guestOnlyRoutes = authConfig.routes?.guestOnly ?? [];\n if (isGuestOnlyRoute(pathname, guestOnlyRoutes)) {\n if (isAuthenticated) {\n const redirectTo = authConfig.routes?.redirectAuthenticatedTo ?? \"/dashboard\";\n return NextResponse.redirect(new URL(redirectTo, request.nextUrl.origin));\n }\n return NextResponse.next();\n }\n\n // ── Public routes ────────────────────────────────────────────────────────\n const publicRoutes = authConfig.routes?.public ?? [];\n if (matchesAny(pathname, publicRoutes)) {\n return NextResponse.next();\n }\n\n // ── Protected routes ─────────────────────────────────────────────────────\n const protectedRoutes = authConfig.routes?.protected ?? [];\n const requiresAuth =\n protectedRoutes.length === 0 || matchesAny(pathname, protectedRoutes);\n\n if (!requiresAuth) {\n return NextResponse.next();\n }\n\n if (!isAuthenticated) {\n // Use the configured login endpoint path, falling back to \"/login\"\n const loginPath = authConfig.routes?.loginPath ?? \"/login\";\n return NextResponse.redirect(new URL(loginPath, request.nextUrl.origin));\n }\n\n return NextResponse.next();\n };\n}\n\n// ─── Session check ────────────────────────────────────────────────────────────\n\nasync function checkSession(\n cookieValue: string | undefined,\n secret: string\n): Promise<boolean> {\n if (!cookieValue) return false;\n\n try {\n const json = await decrypt(cookieValue, secret);\n const tokens = JSON.parse(json) as AuthTokens;\n const now = Date.now();\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n return !refreshExpired;\n } catch {\n return false;\n }\n}\n\n// ─── Route matchers ───────────────────────────────────────────────────────────\n\nfunction isGuestOnlyRoute(pathname: string, routes: string[]): boolean {\n return matchesAny(pathname, routes);\n}\n\n/**\n * Matches a pathname against a list of patterns.\n * Supports wildcards: \"/dashboard*\" matches \"/dashboard\", \"/dashboard/\", \"/dashboard/settings\"\n */\nfunction matchesAny(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchRoute(pathname, pattern));\n}\n\nfunction matchRoute(pathname: string, pattern: string): boolean {\n if (pattern.endsWith(\"*\")) {\n const base = pattern.slice(0, -1); // e.g. \"/dashboard\"\n // matches \"/dashboard\", \"/dashboard/\", \"/dashboard/anything\"\n return pathname === base || pathname.startsWith(base + \"/\") || pathname.startsWith(base);\n }\n return pathname === pattern;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/crypto.ts","../../src/utils/expiry.ts","../../src/server/getServerSession.ts","../../src/server/withAuth.ts","../../src/server/middleware.ts","../../src/server/handlers.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Lightweight symmetric encryption using AES-GCM via the Web Crypto API.\n * Works in both browser and Node.js (>=18) / Edge runtimes.\n */\n\nconst ALGO = \"AES-GCM\";\nconst IV_LENGTH = 12; // bytes\n\nfunction getTextEncoder() {\n return new TextEncoder();\n}\n\nfunction getTextDecoder() {\n return new TextDecoder();\n}\n\nasync function deriveKey(secret: string): Promise<CryptoKey> {\n if (!secret) {\n throw new Error(\n \"[next-token-auth] `secret` is undefined. \" +\n \"If using cookie storage, ensure AUTH_SECRET is set in your environment.\"\n );\n }\n const raw = getTextEncoder().encode(secret.padEnd(32, \"0\").slice(0, 32));\n return crypto.subtle.importKey(\"raw\", raw, { name: ALGO }, false, [\n \"encrypt\",\n \"decrypt\",\n ]);\n}\n\n/**\n * Encrypts a plaintext string using AES-GCM.\n * Returns a base64url-encoded string: `<iv>.<ciphertext>`\n */\nexport async function encrypt(data: string, secret: string): Promise<string> {\n const key = await deriveKey(secret);\n const ivArray = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n // Ensure we have a plain ArrayBuffer for SubtleCrypto\n const iv = ivArray.buffer.slice(0, IV_LENGTH) as ArrayBuffer;\n const encoded = getTextEncoder().encode(data);\n\n const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, encoded);\n\n const ivB64 = bufferToBase64(new Uint8Array(iv));\n const cipherB64 = bufferToBase64(new Uint8Array(cipherBuffer));\n return `${ivB64}.${cipherB64}`;\n}\n\n/**\n * Decrypts a string produced by `encrypt`.\n */\nexport async function decrypt(data: string, secret: string): Promise<string> {\n const [ivB64, cipherB64] = data.split(\".\");\n if (!ivB64 || !cipherB64) {\n throw new Error(\"decrypt: invalid ciphertext format\");\n }\n\n const key = await deriveKey(secret);\n const ivBytes = base64ToBuffer(ivB64);\n const iv = ivBytes.buffer.slice(\n ivBytes.byteOffset,\n ivBytes.byteOffset + ivBytes.byteLength\n ) as ArrayBuffer;\n\n const cipherBytes = base64ToBuffer(cipherB64);\n const cipherBuffer = cipherBytes.buffer.slice(\n cipherBytes.byteOffset,\n cipherBytes.byteOffset + cipherBytes.byteLength\n ) as ArrayBuffer;\n\n const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, cipherBuffer);\n\n return getTextDecoder().decode(plainBuffer);\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction bufferToBase64(buffer: Uint8Array): string {\n return btoa(String.fromCharCode(...buffer))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction base64ToBuffer(b64: string): Uint8Array {\n const padded = b64.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded);\n return Uint8Array.from(binary, (c) => c.charCodeAt(0));\n}\n","import type { ExpiryInput, ExpiryStrategy, LoginResponse } from \"../types\";\n\nconst UNIT_MAP: Record<string, number> = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n};\n\n/**\n * Parses an expiry value into seconds.\n * Accepts:\n * - number → treated as seconds\n * - string → e.g. \"15m\", \"2h\", \"2d\", \"7d\", \"1w\"\n *\n * @throws if the format is unrecognised\n */\nexport function parseExpiry(input?: ExpiryInput): number {\n if (input === undefined || input === null) {\n throw new Error(\"parseExpiry: no expiry value provided\");\n }\n\n if (typeof input === \"number\") {\n if (input <= 0) throw new Error(\"parseExpiry: value must be positive\");\n return input;\n }\n\n const trimmed = input.trim();\n\n // Pure numeric string\n if (/^\\d+$/.test(trimmed)) {\n return parseInt(trimmed, 10);\n }\n\n const match = trimmed.match(/^(\\d+(?:\\.\\d+)?)\\s*([smhdw])$/i);\n if (!match) {\n throw new Error(\n `parseExpiry: unrecognised format \"${input}\". ` +\n `Expected a number or a string like \"15m\", \"2h\", \"2d\", \"7d\", \"1w\".`\n );\n }\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n return Math.floor(value * UNIT_MAP[unit]);\n}\n\n/**\n * Safely parses an expiry value, returning a fallback on failure.\n */\nexport function safeParseExpiry(\n input?: ExpiryInput,\n fallbackSeconds = 900\n): number {\n try {\n return parseExpiry(input);\n } catch {\n return fallbackSeconds;\n }\n}\n\n/**\n * Resolves the access token expiry timestamp (ms) from a login response\n * using the configured strategy.\n */\nexport function resolveAccessTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number {\n const now = Date.now();\n\n const fromBackend =\n response.accessTokenExpiresIn ?? response.expiresIn ?? undefined;\n\n if (strategy === \"backend\") {\n if (fromBackend === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"backend\" but API returned no expiry'\n );\n }\n return now + parseExpiry(fromBackend) * 1000;\n }\n\n if (strategy === \"config\") {\n if (configExpiry === undefined) {\n throw new Error(\n 'resolveAccessTokenExpiry: strategy is \"config\" but no expiry configured'\n );\n }\n return now + parseExpiry(configExpiry) * 1000;\n }\n\n // hybrid: backend first, fallback to config\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n // Last resort: 15 minutes\n return now + 900 * 1000;\n}\n\n/**\n * Resolves the refresh token expiry timestamp (ms).\n */\nexport function resolveRefreshTokenExpiry(\n response: LoginResponse,\n configExpiry?: ExpiryInput,\n strategy: ExpiryStrategy = \"hybrid\"\n): number | undefined {\n const now = Date.now();\n const fromBackend = response.refreshTokenExpiresIn;\n\n if (strategy === \"backend\") {\n return fromBackend !== undefined\n ? now + parseExpiry(fromBackend) * 1000\n : undefined;\n }\n\n if (strategy === \"config\") {\n return configExpiry !== undefined\n ? now + parseExpiry(configExpiry) * 1000\n : undefined;\n }\n\n // hybrid\n if (fromBackend !== undefined) {\n return now + safeParseExpiry(fromBackend) * 1000;\n }\n if (configExpiry !== undefined) {\n return now + safeParseExpiry(configExpiry) * 1000;\n }\n\n return undefined;\n}\n","import type { AuthConfig, AuthSession, AuthTokens, LoginResponse } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n};\n\n/**\n * Retrieves and validates the auth session on the server side.\n * Reads the encrypted session cookie, validates token expiry,\n * and refreshes if needed.\n *\n * Compatible with Next.js App Router (NextRequest) and Pages Router (IncomingMessage).\n *\n * @example\n * // app/dashboard/page.tsx\n * import { getServerSession } from \"next-token-auth/server\";\n *\n * export default async function Page({ request }) {\n * const session = await getServerSession(request, config);\n * if (!session.isAuthenticated) redirect(\"/login\");\n * }\n */\nexport async function getServerSession<User = unknown>(\n req: NextRequest,\n config: AuthConfig<User>\n): Promise<AuthSession<User>> {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = req.cookies.get(cookieName)?.value;\n\n if (!cookieValue) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n let tokens: AuthTokens | null = null;\n\n try {\n const json = await decrypt(cookieValue, config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n const now = Date.now();\n const threshold = (config.refreshThreshold ?? 60) * 1000;\n const accessExpired = now >= tokens.accessTokenExpiresAt - threshold;\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n\n // Both expired → clear session\n if (accessExpired && refreshExpired) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n\n // Access expired but refresh valid → attempt server-side refresh\n if (accessExpired && !refreshExpired) {\n const refreshed = await serverRefresh<User>(tokens, config);\n if (!refreshed) {\n return { user: null, tokens: null, isAuthenticated: false };\n }\n tokens = refreshed;\n }\n\n const user = await fetchUser<User>(tokens.accessToken, config);\n\n return {\n user,\n tokens,\n isAuthenticated: true,\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function serverRefresh<User>(\n tokens: AuthTokens,\n config: AuthConfig<User>\n): Promise<AuthTokens | null> {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const refreshPath = config.endpoints.refresh.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${refreshPath}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as LoginResponse<User>;\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(\n data,\n config.expiry?.accessTokenExpiresIn,\n strategy\n ),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(\n data,\n config.expiry?.refreshTokenExpiresIn,\n strategy\n ),\n };\n } catch {\n return null;\n }\n}\n\nasync function fetchUser<User>(\n accessToken: string,\n config: AuthConfig<User>\n): Promise<User | null> {\n if (!config.endpoints.me) return null;\n\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n const mePath = config.endpoints.me.replace(/^\\//, \"\");\n\n const res = await fetchFn(`${baseUrl}/${mePath}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!res.ok) return null;\n return (await res.json()) as User;\n } catch {\n return null;\n }\n}\n","import type { AuthConfig, AuthSession } from \"../types\";\nimport { getServerSession } from \"./getServerSession\";\n\ntype NextRequest = {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string };\n};\n\ntype NextResponse = {\n redirect(url: URL): NextResponse;\n next(): NextResponse;\n};\n\ntype RouteHandler<User = unknown> = (\n req: NextRequest,\n session: AuthSession<User>\n) => Promise<Response> | Response;\n\n/**\n * Higher-order function that wraps a Next.js route handler with auth protection.\n * Redirects unauthenticated requests to the login page.\n *\n * @example\n * // app/api/protected/route.ts\n * export const GET = withAuth(config, async (req, session) => {\n * return Response.json({ user: session.user });\n * });\n */\nexport function withAuth<User = unknown>(\n config: AuthConfig<User>,\n handler: RouteHandler<User>,\n options: { redirectTo?: string } = {}\n) {\n return async (req: NextRequest): Promise<Response> => {\n const session = await getServerSession<User>(req, config);\n\n if (!session.isAuthenticated) {\n const redirectTo = options.redirectTo ?? \"/login\";\n const loginUrl = new URL(redirectTo, `https://${req.nextUrl.pathname}`);\n return Response.redirect(loginUrl);\n }\n\n return handler(req, session);\n };\n}\n","import type { AuthConfig } from \"../types\";\nimport { decrypt } from \"../utils/crypto\";\nimport type { AuthTokens } from \"../types\";\n\n/**\n * Next.js middleware factory for route protection.\n *\n * @example\n * // middleware.ts (project root)\n * import { authMiddleware } from \"next-token-auth/server\";\n * import { authConfig } from \"./lib/auth\";\n *\n * export const middleware = authMiddleware(authConfig);\n *\n * export const config = {\n * matcher: [\"/auth/login\", \"/auth/register\", \"/dashboard*\", \"/profile*\"],\n * };\n */\nexport function authMiddleware<User = unknown>(authConfig: AuthConfig<User>) {\n return async function middleware(request: {\n cookies: { get(name: string): { value: string } | undefined };\n nextUrl: { pathname: string; origin: string };\n url: string;\n }): Promise<Response> {\n const { NextResponse } = await import(\"next/server\");\n\n const pathname = request.nextUrl.pathname;\n const cookieName = authConfig.token.cookieName ?? \"next-token-auth.session\";\n const cookieValue = request.cookies.get(cookieName)?.value;\n\n const isAuthenticated = await checkSession(cookieValue, authConfig.secret);\n\n // ── Guest-only routes ────────────────────────────────────────────────────\n // Accessible only when NOT authenticated.\n // Authenticated users are redirected to redirectAuthenticatedTo.\n const guestOnlyRoutes = authConfig.routes?.guestOnly ?? [];\n if (isGuestOnlyRoute(pathname, guestOnlyRoutes)) {\n if (isAuthenticated) {\n const redirectTo = authConfig.routes?.redirectAuthenticatedTo ?? \"/dashboard\";\n return NextResponse.redirect(new URL(redirectTo, request.nextUrl.origin));\n }\n return NextResponse.next();\n }\n\n // ── Public routes ────────────────────────────────────────────────────────\n const publicRoutes = authConfig.routes?.public ?? [];\n if (matchesAny(pathname, publicRoutes)) {\n return NextResponse.next();\n }\n\n // ── Protected routes ─────────────────────────────────────────────────────\n const protectedRoutes = authConfig.routes?.protected ?? [];\n const requiresAuth =\n protectedRoutes.length === 0 || matchesAny(pathname, protectedRoutes);\n\n if (!requiresAuth) {\n return NextResponse.next();\n }\n\n if (!isAuthenticated) {\n // Use the configured login endpoint path, falling back to \"/login\"\n const loginPath = authConfig.routes?.loginPath ?? \"/login\";\n return NextResponse.redirect(new URL(loginPath, request.nextUrl.origin));\n }\n\n return NextResponse.next();\n };\n}\n\n// ─── Session check ────────────────────────────────────────────────────────────\n\nasync function checkSession(\n cookieValue: string | undefined,\n secret: string\n): Promise<boolean> {\n if (!cookieValue) return false;\n\n try {\n const json = await decrypt(cookieValue, secret);\n const tokens = JSON.parse(json) as AuthTokens;\n const now = Date.now();\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? now >= tokens.refreshTokenExpiresAt\n : false;\n return !refreshExpired;\n } catch {\n return false;\n }\n}\n\n// ─── Route matchers ───────────────────────────────────────────────────────────\n\nfunction isGuestOnlyRoute(pathname: string, routes: string[]): boolean {\n return matchesAny(pathname, routes);\n}\n\n/**\n * Matches a pathname against a list of patterns.\n * Supports wildcards: \"/dashboard*\" matches \"/dashboard\", \"/dashboard/\", \"/dashboard/settings\"\n */\nfunction matchesAny(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchRoute(pathname, pattern));\n}\n\nfunction matchRoute(pathname: string, pattern: string): boolean {\n if (pattern.endsWith(\"*\")) {\n const base = pattern.slice(0, -1); // e.g. \"/dashboard\"\n // matches \"/dashboard\", \"/dashboard/\", \"/dashboard/anything\"\n return pathname === base || pathname.startsWith(base + \"/\") || pathname.startsWith(base);\n }\n return pathname === pattern;\n}\n","import type { AuthConfig, AuthTokens, LoginResponse } from \"../types\";\nimport { encrypt, decrypt } from \"../utils/crypto\";\nimport {\n resolveAccessTokenExpiry,\n resolveRefreshTokenExpiry,\n} from \"../utils/expiry\";\n\ntype NextRequest = {\n json(): Promise<unknown>;\n cookies: { get(name: string): { value: string } | undefined };\n};\n\n/**\n * Creates Next.js Route Handlers for login, logout, session, and refresh.\n * Mount these at `app/api/auth/[action]/route.ts`.\n *\n * All encryption happens server-side — the secret never leaves the server.\n *\n * @example\n * // app/api/auth/[action]/route.ts\n * import { createAuthHandlers } from \"next-token-auth/server\";\n * import { authConfig } from \"@/lib/auth\";\n *\n * export const { GET, POST } = createAuthHandlers(authConfig);\n */\nexport function createAuthHandlers<User = unknown>(config: AuthConfig<User>) {\n const cookieName = config.token.cookieName ?? \"next-token-auth.session\";\n\n return {\n POST: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/login ─────────────────────────────────────────────────────\n if (action === \"login\") {\n const body = await req.json();\n const fetchFn = config.fetchFn ?? fetch;\n\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.login}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n const text = await res.text();\n return NextResponse.json({ error: text }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const tokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(tokens), config.secret);\n const maxAge = tokens.refreshTokenExpiresAt\n ? Math.floor((tokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n return NextResponse.json(\n { ok: true, user: data.user },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n // ── /api/auth/logout ────────────────────────────────────────────────────\n if (action === \"logout\") {\n if (config.endpoints.logout) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n await fetchFn(`${config.baseUrl}${config.endpoints.logout}`, {\n method: \"POST\",\n });\n } catch {\n // Best-effort\n }\n }\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": `${cookieName}=; Max-Age=0; Path=/`,\n },\n }\n );\n }\n\n // ── /api/auth/refresh ───────────────────────────────────────────────────\n if (action === \"refresh\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ error: \"No session\" }, { status: 401 });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ error: \"Invalid session\" }, { status: 401 });\n }\n\n if (tokens.refreshTokenExpiresAt && Date.now() >= tokens.refreshTokenExpiresAt) {\n return NextResponse.json({ error: \"Refresh token expired\" }, { status: 401 });\n }\n\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.refresh}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken: tokens.refreshToken }),\n });\n\n if (!res.ok) {\n return NextResponse.json({ error: \"Refresh failed\" }, { status: res.status });\n }\n\n const data = (await res.json()) as LoginResponse<User>;\n const newTokens = buildTokens(data, config);\n\n const encrypted = await encrypt(JSON.stringify(newTokens), config.secret);\n const maxAge = newTokens.refreshTokenExpiresAt\n ? Math.floor((newTokens.refreshTokenExpiresAt - Date.now()) / 1000)\n : 604800;\n\n const secure = config.token.secure !== false ? \"Secure; \" : \"\";\n const sameSite = config.token.sameSite ?? \"lax\";\n\n return NextResponse.json(\n { ok: true },\n {\n headers: {\n \"Set-Cookie\": [\n `${cookieName}=${encodeURIComponent(encrypted)}`,\n `HttpOnly`,\n `Max-Age=${maxAge}`,\n `Path=/`,\n `SameSite=${sameSite}`,\n secure,\n ]\n .filter(Boolean)\n .join(\"; \"),\n },\n }\n );\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n\n GET: async (\n req: NextRequest,\n context: { params: Promise<{ action: string }> | { action: string } }\n ): Promise<Response> => {\n const { NextResponse } = await import(\"next/server\");\n const params = await Promise.resolve(context.params);\n const action = params.action;\n\n // ── /api/auth/session ───────────────────────────────────────────────────\n if (action === \"session\") {\n const raw = req.cookies.get(cookieName)?.value;\n if (!raw) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n let tokens: AuthTokens;\n try {\n const json = await decrypt(decodeURIComponent(raw), config.secret);\n tokens = JSON.parse(json) as AuthTokens;\n } catch {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n const refreshExpired = tokens.refreshTokenExpiresAt\n ? Date.now() >= tokens.refreshTokenExpiresAt\n : false;\n\n if (refreshExpired) {\n return NextResponse.json({ user: null, isAuthenticated: false });\n }\n\n // Fetch user from backend\n let user: User | null = null;\n if (config.endpoints.me) {\n try {\n const fetchFn = config.fetchFn ?? fetch;\n const res = await fetchFn(`${config.baseUrl}${config.endpoints.me}`, {\n headers: { Authorization: `Bearer ${tokens.accessToken}` },\n });\n if (res.ok) user = (await res.json()) as User;\n } catch {\n // User fetch failed — still return authenticated: true if tokens are valid\n }\n }\n\n return NextResponse.json({ user, isAuthenticated: true });\n }\n\n return NextResponse.json({ error: \"Unknown action\" }, { status: 400 });\n },\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction buildTokens<User>(\n data: LoginResponse<User>,\n config: AuthConfig<User>\n): AuthTokens {\n const strategy = config.expiry?.strategy ?? \"hybrid\";\n const configAccess = config.expiry?.accessTokenExpiresIn;\n const configRefresh = config.expiry?.refreshTokenExpiresIn;\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n accessTokenExpiresAt: resolveAccessTokenExpiry(data, configAccess, strategy),\n refreshTokenExpiresAt: resolveRefreshTokenExpiry(data, configRefresh, strategy),\n };\n}\n"]}
|