UNPKG

payload-totp

Version:

Add an extra security layer to PayloadCMS using a Time-based One-time Password (TOTP).

226 lines (225 loc) 12.3 kB
import { removeEndpointHandler } from './api/remove.js'; import { setSecret } from './api/setSecret.js'; import { verifyToken } from './api/verifyToken.js'; import { deleteCookieAfterLogout } from './hooks/deleteCookieAfterLogout.js'; import { setHasTotp } from './hooks/setHasTotp.js'; import { i18n } from './i18n/index.js'; import { strategy } from './strategy.js'; import { totpAccess } from './totpAccess.js'; const payloadTotp = (pluginOptions)=>(config)=>{ return { ...config, admin: { ...config.admin || {}, components: { ...config.admin?.components || {}, providers: [ ...config.admin?.components?.providers || [], { path: 'payload-totp/rsc#TOTPProvider', serverProps: { pluginOptions } } ], views: { // Backslash versions are standard and works in general. // But it doesn't work well when you're using PayloadCMS // without `/admin`, but `/`. SetupTOTP: { Component: { path: 'payload-totp/rsc#TOTPSetup', serverProps: { pluginOptions } }, exact: true, path: '/setup-totp', sensitive: false, strict: true }, SetupTOTPBackslash: { Component: { path: 'payload-totp/rsc#TOTPSetup', serverProps: { pluginOptions } }, exact: true, path: '/setup-totp', sensitive: false, strict: true }, VerifyTOTP: { Component: { path: 'payload-totp/rsc#TOTPVerify', serverProps: { pluginOptions } }, exact: true, path: '/verify-totp', sensitive: false, strict: true }, VerifyTOTPBackslash: { Component: { path: 'payload-totp/rsc#TOTPVerify', serverProps: { pluginOptions } }, exact: true, path: '/verify-totp', sensitive: false, strict: true }, // Fix for https://github.com/GeorgeHulpoi/payload-totp/issues/46 // The order is important! ...config.admin?.components?.views || {} } } }, collections: [ ...(config.collections || []).map((collection)=>{ if (collection.slug === pluginOptions.collection) { return { ...collection, access: { ...collection.access || {}, create: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.create ? collection.access?.create : totpAccess(collection.access?.create), delete: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.delete ? collection.access?.delete : totpAccess(collection.access?.delete), read: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.read ? collection.access?.read : totpAccess(collection.access?.read), readVersions: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.readVersions ? collection.access?.readVersions : totpAccess(collection.access?.readVersions), unlock: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.unlock ? collection.access?.unlock : totpAccess(collection.access?.unlock), update: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.update ? collection.access?.update : totpAccess(collection.access?.update) }, auth: { ...typeof collection.auth === 'object' ? collection.auth : {}, strategies: [ strategy, ...typeof collection.auth === 'object' ? collection.auth?.strategies || [] : [] ] }, fields: [ ...collection.fields || [], { name: 'totpSecret', type: 'text', access: { create: ()=>false, read: ()=>false, update: ()=>false }, admin: { disableBulkEdit: true, disableListColumn: true, disableListFilter: true, hidden: true }, disableBulkEdit: true, disableListColumn: true, disableListFilter: true }, { name: 'totpSecretUI', type: 'ui', admin: { components: { Field: { path: 'payload-totp/rsc#TOTPField', serverProps: { pluginOptions } } }, disableListColumn: true } }, { name: 'hasTotp', type: 'checkbox', access: { read: ({ data, req: { user } })=>data && user && data?.id === user?.id }, admin: { disableBulkEdit: true, disableListColumn: true, disableListFilter: true, hidden: true }, hooks: { afterRead: [ setHasTotp(pluginOptions) ] }, virtual: true } ], hooks: { ...collection.hooks || {}, afterLogout: [ ...collection.hooks?.afterLogout || [], deleteCookieAfterLogout ] } }; } else { return { ...collection, access: { ...collection.access || {}, create: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.create ? collection.access?.create : totpAccess(collection.access?.create), delete: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.delete ? collection.access?.delete : totpAccess(collection.access?.delete), read: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.read ? collection.access?.read : totpAccess(collection.access?.read), readVersions: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.readVersions ? collection.access?.readVersions : totpAccess(collection.access?.readVersions), unlock: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.unlock ? collection.access?.unlock : totpAccess(collection.access?.unlock), update: pluginOptions.disableAccessWrapper || collection.custom?.totp?.disableAccessWrapper?.update ? collection.access?.update : totpAccess(collection.access?.update) } }; } }) ], custom: { ...config.custom || {}, totp: { pluginOptions } }, endpoints: [ ...config.endpoints || [], { handler: setSecret(pluginOptions), method: 'post', path: '/setup-totp' }, { handler: verifyToken(pluginOptions), method: 'post', path: '/verify-totp' }, { handler: removeEndpointHandler(pluginOptions), method: 'post', path: '/remove-totp' } ], globals: [ ...(config.globals || []).map((global)=>{ return { ...global, access: { ...global.access || {}, read: pluginOptions.disableAccessWrapper || global.custom?.totp?.disableAccessWrapper?.read ? global.access?.read : totpAccess(global.access?.read), readDrafts: pluginOptions.disableAccessWrapper || global.custom?.totp?.disableAccessWrapper?.readDrafts ? global.access?.readDrafts : totpAccess(global.access?.readDrafts), readVersions: pluginOptions.disableAccessWrapper || global.custom?.totp?.disableAccessWrapper?.readVersions ? global.access?.readVersions : totpAccess(global.access?.readVersions), update: pluginOptions.disableAccessWrapper || global.custom?.totp?.disableAccessWrapper?.update ? global.access?.update : totpAccess(global.access?.update) } }; }) ], i18n: i18n(config.i18n) }; }; export { payloadTotp, totpAccess }; //# sourceMappingURL=index.js.map