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
JavaScript
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