@bivola/refresh-auth 1.1.6 → 1.1.7
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.
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { jwtSign } from 'payload';
|
|
2
|
+
import { fieldAffectsData, fieldHasSubFields } from 'payload/shared';
|
|
1
3
|
import { RevocationReason } from '../collections/refreshTokens';
|
|
2
4
|
import { createRefreshToken, hashToken } from '../utils/crypto';
|
|
3
5
|
import { getRequestMeta } from '../utils/getRequestMeta';
|
|
4
|
-
import { signJWT } from '../utils/jwt';
|
|
5
6
|
export const refreshEndpoint = (options)=>({
|
|
6
7
|
handler: async (req)=>{
|
|
7
8
|
const { deviceId, refresh_token } = await req.json?.() || {};
|
|
@@ -73,11 +74,36 @@ export const refreshEndpoint = (options)=>({
|
|
|
73
74
|
rotatedAt: now
|
|
74
75
|
}
|
|
75
76
|
});
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
const collectionConfig = req.payload.collections[options.entity_slug].config;
|
|
78
|
+
const user = await req.payload.findByID({
|
|
79
|
+
id: session.entity,
|
|
80
|
+
collection: options.entity_slug,
|
|
81
|
+
depth: 0
|
|
82
|
+
});
|
|
83
|
+
const fieldsToSign = collectionConfig.fields.reduce((signedFields, field)=>{
|
|
84
|
+
const result = {
|
|
85
|
+
...signedFields
|
|
86
|
+
};
|
|
87
|
+
if (!fieldAffectsData(field) && fieldHasSubFields(field)) {
|
|
88
|
+
field.fields.forEach((subField)=>{
|
|
89
|
+
if (fieldAffectsData(subField) && subField.saveToJWT) {
|
|
90
|
+
result[subField.name] = user[subField.name];
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (fieldAffectsData(field) && field.saveToJWT) {
|
|
95
|
+
result[field.name] = user[field.name];
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}, {
|
|
99
|
+
id: user.id,
|
|
100
|
+
collection: collectionConfig.slug,
|
|
101
|
+
email: user.email
|
|
102
|
+
});
|
|
103
|
+
const accessJWT = jwtSign({
|
|
104
|
+
fieldsToSign,
|
|
105
|
+
secret: req.payload.secret,
|
|
106
|
+
tokenExpiration: collectionConfig.auth?.tokenExpiration
|
|
81
107
|
});
|
|
82
108
|
return Response.json({
|
|
83
109
|
access_token: accessJWT,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/endpoints/refresh.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\n\nimport type { AuthRefreshPluginOptions } from '../index'\n\nimport { RevocationReason } from '../collections/refreshTokens'\nimport { createRefreshToken, hashToken } from '../utils/crypto'\nimport { getRequestMeta } from '../utils/getRequestMeta'\
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/refresh.ts"],"sourcesContent":["import type { Endpoint, Field } from 'payload'\n\nimport { jwtSign } from 'payload'\nimport { fieldAffectsData, fieldHasSubFields } from 'payload/shared'\n\nimport type { AuthRefreshPluginOptions } from '../index'\n\nimport { RevocationReason } from '../collections/refreshTokens'\nimport { createRefreshToken, hashToken } from '../utils/crypto'\nimport { getRequestMeta } from '../utils/getRequestMeta'\n\ntype RefreshEndpointOptions = Pick<\n AuthRefreshPluginOptions,\n 'entity_slug' | 'identifier_field' | 'pepper' | 'refreshTokenTTL'\n>\n\nexport const refreshEndpoint = (options: RefreshEndpointOptions): Endpoint => ({\n handler: async (req) => {\n const { deviceId, refresh_token } = (await req.json?.()) || {}\n\n if (!refresh_token || !deviceId) {\n return Response.json(\n { message: 'Refresh token and device ID are required.' },\n { status: 400 },\n )\n }\n\n const result = await req.payload.find({\n collection: 'refresh-tokens',\n where: {\n tokenHash: {\n equals: hashToken(refresh_token, options.pepper),\n },\n },\n })\n\n if (!result?.docs?.length) {\n return Response.json({ message: 'Unauthorized.' }, { status: 401 })\n }\n\n const session = result.docs[0]\n\n if (session.deviceId !== deviceId || session.expiresAt < new Date() || session.revokedAt) {\n return Response.json({ message: 'Unauthorized.' }, { status: 401 })\n }\n\n if (session.rotatedAt) {\n await req.payload.update({\n id: session.id,\n collection: 'refresh-tokens',\n data: { revocationReason: RevocationReason.SuspiciousActivity, revokedAt: new Date() },\n })\n return Response.json({ message: 'Unauthorized.' }, { status: 401 })\n }\n\n const { token, tokenHash } = createRefreshToken(options.pepper)\n const now = new Date()\n\n const newSession = await req.payload.create({\n collection: 'refresh-tokens',\n data: {\n deviceId,\n entity: session.entity,\n expiresAt: new Date(now.getTime() + options.refreshTokenTTL * 24 * 60 * 60 * 1000),\n lastUsedAt: now,\n tokenHash,\n ...getRequestMeta(req),\n },\n })\n\n await req.payload.update({\n id: session.id,\n collection: 'refresh-tokens',\n data: {\n lastUsedAt: now,\n replacedBy: newSession.id,\n revocationReason: RevocationReason.TokenRotation,\n rotatedAt: now,\n },\n })\n\n const collectionConfig = req.payload.collections[options.entity_slug].config\n\n const user = await req.payload.findByID({\n id: session.entity,\n collection: options.entity_slug,\n depth: 0,\n })\n\n const fieldsToSign = collectionConfig.fields.reduce(\n (signedFields: Record<string, number | string>, field: Field) => {\n const result = { ...signedFields }\n\n if (!fieldAffectsData(field) && fieldHasSubFields(field)) {\n field.fields.forEach((subField) => {\n if (fieldAffectsData(subField) && subField.saveToJWT) {\n result[subField.name] = user[subField.name]\n }\n })\n }\n\n if (fieldAffectsData(field) && field.saveToJWT) {\n result[field.name] = user[field.name]\n }\n\n return result\n },\n {\n id: user.id,\n collection: collectionConfig.slug,\n email: user.email,\n },\n )\n\n const accessJWT = jwtSign({\n fieldsToSign,\n secret: req.payload.secret,\n tokenExpiration: collectionConfig.auth?.tokenExpiration,\n })\n\n return Response.json({\n access_token: accessJWT,\n refresh_token: token,\n user: session.user,\n })\n },\n method: 'post',\n path: `/auth/refresh`,\n})\n"],"names":["jwtSign","fieldAffectsData","fieldHasSubFields","RevocationReason","createRefreshToken","hashToken","getRequestMeta","refreshEndpoint","options","handler","req","deviceId","refresh_token","json","Response","message","status","result","payload","find","collection","where","tokenHash","equals","pepper","docs","length","session","expiresAt","Date","revokedAt","rotatedAt","update","id","data","revocationReason","SuspiciousActivity","token","now","newSession","create","entity","getTime","refreshTokenTTL","lastUsedAt","replacedBy","TokenRotation","collectionConfig","collections","entity_slug","config","user","findByID","depth","fieldsToSign","fields","reduce","signedFields","field","forEach","subField","saveToJWT","name","slug","email","accessJWT","secret","tokenExpiration","auth","access_token","method","path"],"mappings":"AAEA,SAASA,OAAO,QAAQ,UAAS;AACjC,SAASC,gBAAgB,EAAEC,iBAAiB,QAAQ,iBAAgB;AAIpE,SAASC,gBAAgB,QAAQ,+BAA8B;AAC/D,SAASC,kBAAkB,EAAEC,SAAS,QAAQ,kBAAiB;AAC/D,SAASC,cAAc,QAAQ,0BAAyB;AAOxD,OAAO,MAAMC,kBAAkB,CAACC,UAA+C,CAAA;QAC7EC,SAAS,OAAOC;YACd,MAAM,EAAEC,QAAQ,EAAEC,aAAa,EAAE,GAAG,AAAC,MAAMF,IAAIG,IAAI,QAAS,CAAC;YAE7D,IAAI,CAACD,iBAAiB,CAACD,UAAU;gBAC/B,OAAOG,SAASD,IAAI,CAClB;oBAAEE,SAAS;gBAA4C,GACvD;oBAAEC,QAAQ;gBAAI;YAElB;YAEA,MAAMC,SAAS,MAAMP,IAAIQ,OAAO,CAACC,IAAI,CAAC;gBACpCC,YAAY;gBACZC,OAAO;oBACLC,WAAW;wBACTC,QAAQlB,UAAUO,eAAeJ,QAAQgB,MAAM;oBACjD;gBACF;YACF;YAEA,IAAI,CAACP,QAAQQ,MAAMC,QAAQ;gBACzB,OAAOZ,SAASD,IAAI,CAAC;oBAAEE,SAAS;gBAAgB,GAAG;oBAAEC,QAAQ;gBAAI;YACnE;YAEA,MAAMW,UAAUV,OAAOQ,IAAI,CAAC,EAAE;YAE9B,IAAIE,QAAQhB,QAAQ,KAAKA,YAAYgB,QAAQC,SAAS,GAAG,IAAIC,UAAUF,QAAQG,SAAS,EAAE;gBACxF,OAAOhB,SAASD,IAAI,CAAC;oBAAEE,SAAS;gBAAgB,GAAG;oBAAEC,QAAQ;gBAAI;YACnE;YAEA,IAAIW,QAAQI,SAAS,EAAE;gBACrB,MAAMrB,IAAIQ,OAAO,CAACc,MAAM,CAAC;oBACvBC,IAAIN,QAAQM,EAAE;oBACdb,YAAY;oBACZc,MAAM;wBAAEC,kBAAkBhC,iBAAiBiC,kBAAkB;wBAAEN,WAAW,IAAID;oBAAO;gBACvF;gBACA,OAAOf,SAASD,IAAI,CAAC;oBAAEE,SAAS;gBAAgB,GAAG;oBAAEC,QAAQ;gBAAI;YACnE;YAEA,MAAM,EAAEqB,KAAK,EAAEf,SAAS,EAAE,GAAGlB,mBAAmBI,QAAQgB,MAAM;YAC9D,MAAMc,MAAM,IAAIT;YAEhB,MAAMU,aAAa,MAAM7B,IAAIQ,OAAO,CAACsB,MAAM,CAAC;gBAC1CpB,YAAY;gBACZc,MAAM;oBACJvB;oBACA8B,QAAQd,QAAQc,MAAM;oBACtBb,WAAW,IAAIC,KAAKS,IAAII,OAAO,KAAKlC,QAAQmC,eAAe,GAAG,KAAK,KAAK,KAAK;oBAC7EC,YAAYN;oBACZhB;oBACA,GAAGhB,eAAeI,IAAI;gBACxB;YACF;YAEA,MAAMA,IAAIQ,OAAO,CAACc,MAAM,CAAC;gBACvBC,IAAIN,QAAQM,EAAE;gBACdb,YAAY;gBACZc,MAAM;oBACJU,YAAYN;oBACZO,YAAYN,WAAWN,EAAE;oBACzBE,kBAAkBhC,iBAAiB2C,aAAa;oBAChDf,WAAWO;gBACb;YACF;YAEA,MAAMS,mBAAmBrC,IAAIQ,OAAO,CAAC8B,WAAW,CAACxC,QAAQyC,WAAW,CAAC,CAACC,MAAM;YAE5E,MAAMC,OAAO,MAAMzC,IAAIQ,OAAO,CAACkC,QAAQ,CAAC;gBACtCnB,IAAIN,QAAQc,MAAM;gBAClBrB,YAAYZ,QAAQyC,WAAW;gBAC/BI,OAAO;YACT;YAEA,MAAMC,eAAeP,iBAAiBQ,MAAM,CAACC,MAAM,CACjD,CAACC,cAA+CC;gBAC9C,MAAMzC,SAAS;oBAAE,GAAGwC,YAAY;gBAAC;gBAEjC,IAAI,CAACxD,iBAAiByD,UAAUxD,kBAAkBwD,QAAQ;oBACxDA,MAAMH,MAAM,CAACI,OAAO,CAAC,CAACC;wBACpB,IAAI3D,iBAAiB2D,aAAaA,SAASC,SAAS,EAAE;4BACpD5C,MAAM,CAAC2C,SAASE,IAAI,CAAC,GAAGX,IAAI,CAACS,SAASE,IAAI,CAAC;wBAC7C;oBACF;gBACF;gBAEA,IAAI7D,iBAAiByD,UAAUA,MAAMG,SAAS,EAAE;oBAC9C5C,MAAM,CAACyC,MAAMI,IAAI,CAAC,GAAGX,IAAI,CAACO,MAAMI,IAAI,CAAC;gBACvC;gBAEA,OAAO7C;YACT,GACA;gBACEgB,IAAIkB,KAAKlB,EAAE;gBACXb,YAAY2B,iBAAiBgB,IAAI;gBACjCC,OAAOb,KAAKa,KAAK;YACnB;YAGF,MAAMC,YAAYjE,QAAQ;gBACxBsD;gBACAY,QAAQxD,IAAIQ,OAAO,CAACgD,MAAM;gBAC1BC,iBAAiBpB,iBAAiBqB,IAAI,EAAED;YAC1C;YAEA,OAAOrD,SAASD,IAAI,CAAC;gBACnBwD,cAAcJ;gBACdrD,eAAeyB;gBACfc,MAAMxB,QAAQwB,IAAI;YACpB;QACF;QACAmB,QAAQ;QACRC,MAAM,CAAC,aAAa,CAAC;IACvB,CAAA,EAAE"}
|
package/dist/utils/jwt.js
CHANGED
package/dist/utils/jwt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/jwt.ts"],"sourcesContent":["import type { Secret, SignOptions } from 'jsonwebtoken'\n\nimport crypto from 'crypto'\nimport jwt from 'jsonwebtoken'\n\nconst derivePayloadJWTSecret = (secret: string) => {\n const hash = crypto.createHash('sha256').update(secret).digest('hex')\n return hash.slice(0, 32)\n}\n\ntype SignJWTOptions = {\n collectionId: string\n collectionSlug: string\n expiresIn: number | string\n rawSecret: string\n}\n\nexport const signJWT = ({ collectionId, collectionSlug, expiresIn, rawSecret }: SignJWTOptions) => {\n const signingKey = derivePayloadJWTSecret(rawSecret)\n\n const token = jwt.sign(\n {\n id: collectionId,\n collection: collectionSlug,\n },\n
|
|
1
|
+
{"version":3,"sources":["../../src/utils/jwt.ts"],"sourcesContent":["import type { Secret, SignOptions } from 'jsonwebtoken'\nimport type { Field } from 'payload'\n\nimport crypto from 'crypto'\nimport jwt from 'jsonwebtoken'\nimport { fieldAffectsData, fieldHasSubFields } from 'payload/shared'\n\nconst derivePayloadJWTSecret = (secret: string) => {\n const hash = crypto.createHash('sha256').update(secret).digest('hex')\n return hash.slice(0, 32)\n}\n\ntype SignJWTOptions = {\n collectionId: string\n collectionSlug: string\n expiresIn: number | string\n rawSecret: string\n}\n\nexport const signJWT = ({ collectionId, collectionSlug, expiresIn, rawSecret }: SignJWTOptions) => {\n const signingKey = derivePayloadJWTSecret(rawSecret)\n\n const token = jwt.sign(\n {\n id: collectionId,\n collection: collectionSlug,\n },\n rawSecret,\n { expiresIn } as SignOptions,\n )\n\n return token\n}\n"],"names":["crypto","jwt","derivePayloadJWTSecret","secret","hash","createHash","update","digest","slice","signJWT","collectionId","collectionSlug","expiresIn","rawSecret","signingKey","token","sign","id","collection"],"mappings":"AAGA,OAAOA,YAAY,SAAQ;AAC3B,OAAOC,SAAS,eAAc;AAG9B,MAAMC,yBAAyB,CAACC;IAC9B,MAAMC,OAAOJ,OAAOK,UAAU,CAAC,UAAUC,MAAM,CAACH,QAAQI,MAAM,CAAC;IAC/D,OAAOH,KAAKI,KAAK,CAAC,GAAG;AACvB;AASA,OAAO,MAAMC,UAAU,CAAC,EAAEC,YAAY,EAAEC,cAAc,EAAEC,SAAS,EAAEC,SAAS,EAAkB;IAC5F,MAAMC,aAAaZ,uBAAuBW;IAE1C,MAAME,QAAQd,IAAIe,IAAI,CACpB;QACEC,IAAIP;QACJQ,YAAYP;IACd,GACAE,WACA;QAAED;IAAU;IAGd,OAAOG;AACT,EAAC"}
|