@youngshand/payload-auth-plugin
Version:
A temporary fork for testing of Authentication plugin for Payload CMS, use @payload-auth-plugin
233 lines (209 loc) • 5.67 kB
text/typescript
/**
* The App plugin is used for authenticating users in the frontent app of the Payload CMS application.
* It support magic link, password, OAuth, and Passkey based authentications.
*
* On top of it, to add additional security it also support 2FA using OTP, and TOTP.
*
* The set up is very lean and flexible to tailor the auth process in a specific way.
*
* ```ts
* import {appAuthPlugin} from "payload-auth-plugin";
*
* // TODO: Need complete implementation
*
* ```
* @packageDocumentation
*/
import { Config, Endpoint, PayloadRequest, Plugin } from "payload"
import {
AccountInfo,
AuthenticationStrategy,
PasswordProviderConfig,
OAuthProviderConfig,
PasskeyProviderConfig,
} from "../types.js"
import {
InvalidServerURL,
MissingEmailAdapter,
} from "../core/errors/consoleErrors.js"
import {
getPasswordProvider,
getOAuthProviders,
getPasskeyProvider,
} from "../providers/utils.js"
import {
PasswordAuthEndpointStrategy,
EndpointsFactory,
OAuthEndpointStrategy,
PasskeyEndpointStrategy,
SessionEndpointStrategy,
} from "../core/endpoints.js"
import { AppSession } from "../core/session/app.js"
import { formatSlug } from "../core/utils/slug.js"
import { preflightCollectionCheck } from "../core/preflights/collections.js"
/**
* The App plugin to set up authentication to the intengrated frontend of Payload CMS.
*
* Add the plugin to your Payload project plugins.
*
*/
interface PluginOptions {
/**
* Enable or disable plugin
*
* @default true
*
*/
enabled?: boolean | undefined
/**
* Unique name for your frontend app.
*
* This name will be used to created endpoints, tokens, and etc.
*/
name: string
/**
* Auth providers supported by the plugin
*
*/
providers: (
| OAuthProviderConfig
| PasskeyProviderConfig
| PasswordProviderConfig
)[]
/**
* @description
* App users collection slug.
*
* This collection will be used to store all the app user records.
*
*/
usersCollectionSlug: string
/**
* App user accounts collection slug.
*
* This collection will be used to store all the app user account records.
* Multiple accounts can belong to one user
*
*/
accountsCollectionSlug: string
/**
* Allow auto signup if user doesn't have an account.
*
* @default false
*
*/
allowAutoSignUp?: boolean | undefined
/**
* Authentication strategies can be either JWT or Cookie based
*
* @default Cookie
*
*/
authenticationStrategy?: AuthenticationStrategy
/**
* Secret to use for JWT signing and decryption
*/
secret: string
}
/**
* App plugin funtion.
*
* @param {PluginOptions} pluginOptions
* @returns {Plugin}
*/
export const appAuthPlugin =
(pluginOptions: PluginOptions): Plugin =>
(incomingConfig: Config): Config => {
const config = { ...incomingConfig }
if (pluginOptions.enabled === false) {
return config
}
if (!config.serverURL) {
throw new InvalidServerURL()
}
const {
usersCollectionSlug,
accountsCollectionSlug,
providers,
allowAutoSignUp,
authenticationStrategy,
secret,
} = pluginOptions
preflightCollectionCheck(
[usersCollectionSlug, accountsCollectionSlug],
config.collections,
)
const name = formatSlug(pluginOptions.name)
const oauthProviders = getOAuthProviders(providers)
const passkeyProvider = getPasskeyProvider(providers)
const passwordProvider = getPasswordProvider(providers)
const session = new AppSession(
name,
{
usersCollection: usersCollectionSlug,
accountsCollection: accountsCollectionSlug,
},
allowAutoSignUp ?? false,
authenticationStrategy ?? "Cookie",
secret,
)
const endpointsFactory = new EndpointsFactory(name)
let oauthEndpoints: Endpoint[] = []
let passkeyEndpoints: Endpoint[] = []
let passwordEndpoints: Endpoint[] = []
if (Object.keys(oauthProviders).length > 0) {
endpointsFactory.registerStrategy(
"oauth",
new OAuthEndpointStrategy(oauthProviders),
)
oauthEndpoints = endpointsFactory.createEndpoints("oauth", {
sessionCallback: (
oauthAccountInfo: AccountInfo,
scope: string,
issuerName: string,
request: PayloadRequest,
clientOrigin: string,
) =>
session.oauthSessionCallback(
oauthAccountInfo,
scope,
issuerName,
request,
clientOrigin,
),
})
}
if (passkeyProvider) {
endpointsFactory.registerStrategy(
"passkey",
new PasskeyEndpointStrategy(),
)
passkeyEndpoints = endpointsFactory.createEndpoints("passkey")
}
if (passwordProvider) {
if (!!!config.email) {
throw new MissingEmailAdapter()
}
endpointsFactory.registerStrategy(
"password",
new PasswordAuthEndpointStrategy({ usersCollectionSlug }, secret),
)
passwordEndpoints = endpointsFactory.createEndpoints("password", {
sessionCallback: (user: { id: string; email: string }) =>
session.passwordSessionCallback(user),
})
}
endpointsFactory.registerStrategy(
"session",
new SessionEndpointStrategy(secret, { usersCollectionSlug }),
)
const sessionEndpoints = endpointsFactory.createEndpoints("session")
config.endpoints = [
...(config.endpoints ?? []),
...oauthEndpoints,
...passkeyEndpoints,
...passwordEndpoints,
...sessionEndpoints,
]
return config
}