@decked/payload-auth-plugin
Version:
Authentication plugin for Payload CMS
136 lines (123 loc) • 3.41 kB
text/typescript
import { BasePayload, PayloadRequest } from "payload"
import { UserNotFound } from "../errors/consoleErrors.js"
import { AccountInfo } from "../../types.js"
import { hashCode } from "../utils/hash.js"
import {
createSessionCookies,
invalidateOAuthCookies,
} from "../utils/cookies.js"
import { sessionResponse } from "../utils/session.js"
import { UserNotFoundAPIError } from "../errors/apiErrors.js"
type Collections = {
accountsCollectionSlug: string
usersCollectionSlug: string
}
export class PayloadSession {
readonly #collections: Collections
readonly #allowSignUp: boolean
constructor(collections: Collections, allowSignUp?: boolean) {
this.#collections = collections
this.#allowSignUp = !!allowSignUp
}
async #upsertAccount(
accountInfo: AccountInfo,
scope: string,
issuerName: string,
payload: BasePayload,
) {
let userID: string | number
const userQueryResults = await payload.find({
collection: this.#collections.usersCollectionSlug,
where: {
email: {
equals: accountInfo.email,
},
},
})
if (userQueryResults.docs.length === 0) {
if (!this.#allowSignUp) {
return new UserNotFoundAPIError()
}
const newUser = await payload.create({
collection: this.#collections.usersCollectionSlug,
data: {
email: accountInfo.email,
emailVerified: true,
password: hashCode(accountInfo.email + payload.secret).toString(),
},
})
userID = newUser.id
} else {
userID = userQueryResults.docs[0].id as string
}
const accounts = await payload.find({
collection: this.#collections.accountsCollectionSlug,
where: {
sub: { equals: accountInfo.sub },
},
})
const data: Record<string, unknown> = {
scope,
name: accountInfo.name,
picture: accountInfo.picture,
}
if (issuerName === "Passkey" && accountInfo.passKey) {
data["passkey"] = {
...accountInfo.passKey,
}
}
if (accounts.docs.length > 0) {
data["sub"] = accountInfo.sub
data["issuerName"] = issuerName
data["user"] = userID
await payload.update({
collection: this.#collections.accountsCollectionSlug,
where: {
id: {
equals: accounts.docs[0].id,
},
},
data,
})
} else {
data["sub"] = accountInfo.sub
data["issuerName"] = issuerName
data["user"] = userID
await payload.create({
collection: this.#collections.accountsCollectionSlug,
data,
})
}
return userID
}
async createSession(
accountInfo: AccountInfo,
scope: string,
issuerName: string,
request: PayloadRequest,
clientOrigin: string,
) {
const { payload } = request
const userID = await this.#upsertAccount(
accountInfo,
scope,
issuerName,
payload,
)
const fieldsToSign = {
id: userID,
email: accountInfo.email,
collection: this.#collections.usersCollectionSlug,
}
let cookies: string[] = []
cookies = [
...(await createSessionCookies(
`${payload.config.cookiePrefix!}-token`,
payload.secret,
fieldsToSign,
)),
]
cookies = invalidateOAuthCookies(cookies)
return sessionResponse(cookies, clientOrigin)
}
}