UNPKG

keystone-6-oauth

Version:

Keystone6 Plugin that enables social logins such as Google, Twitter, Github, Facebook and others.

233 lines (218 loc) 6.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties'); var _objectSpread = require('@babel/runtime/helpers/objectSpread2'); var _includesInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/includes'); var NextAuth = require('next-auth'); function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; } var _includesInstanceProperty__default = /*#__PURE__*/_interopDefault(_includesInstanceProperty); var NextAuth__default = /*#__PURE__*/_interopDefault(NextAuth); async function findMatchingIdentity(identityField, identity, queryAPI) { const item = await queryAPI.findOne({ where: { [identityField]: identity } }); // Identity failures with helpful errors let code; if (!item) { code = 'IDENTITY_NOT_FOUND'; } if (code) { return { success: false, code }; } return { success: true, item }; } async function validateNextAuth(identityField, identity, allowAccountLinks, listQueryAPI) { const match = await findMatchingIdentity(identityField, identity, listQueryAPI); const { item } = match; if (item) { return { item, success: true }; } return { // TODO: Review below to link accounts code: !allowAccountLinks ? 'FAILURE' : 'SUBJECT_NOT_FOUND', success: false }; } const _excluded = ["user", "account", "profile"]; function NextAuthPage(props) { const { autoCreate = true, cookies, events, identityField, jwt, listKey, onSignIn, onSignUp, pages, providers, context, sessionData, sessionSecret } = props; if (!context) { // eslint-disable-next-line no-console console.error('NextAuthPage got no context.'); return null; } if (!providers || !providers.length) { // eslint-disable-next-line no-console console.error('You need to provide at least one provider.'); return null; } const listQueryAPI = context.query[listKey]; const allowAccountLinks = true; // TODO: Review + implement account links return NextAuth__default["default"]({ callbacks: { async jwt(_ref) { let { token } = _ref; const identity = token.sub; if (!identity) return token; const nextAuthValidationResult = await validateNextAuth(identityField, identity, allowAccountLinks, listQueryAPI); if (!nextAuthValidationResult.success) { token.itemId = undefined; } else { token.itemId = nextAuthValidationResult.item.id; if (!token.itemId) { console.error("NextAuthPage: Couldn't find itemId in token.", { nextAuthValidationResult, token }); throw new Error('No itemId found in token'); } const data = await context.query[listKey].findOne({ query: sessionData || 'id', where: { id: token.itemId } // TODO: Q: is `tokenItemId` the same as `Authenticated.id`? }); token.data = data; } const returnToken = _objectSpread(_objectSpread({}, token), {}, { listKey, subject: token.sub }); return returnToken; }, async redirect(_ref2) { let { url } = _ref2; return url; }, async session(_ref3) { let { session, token } = _ref3; let returnSession = session; if (!token.itemId) { return session; } returnSession = _objectSpread(_objectSpread({}, session), {}, { data: token.data, itemId: token.itemId, listKey: token.listKey, subject: token.sub }); // TODO: [TYPES] Add typings? return returnSession; }, async signIn(_ref4) { let { user, account, profile } = _ref4; _objectWithoutProperties(_ref4, _excluded); let identity; if (typeof user.id === 'string') { identity = user.id; } else if (typeof user.id === 'number') { identity = user.id; } else { identity = 0; } const nextAuthValidationResult = await validateNextAuth(identityField, identity, allowAccountLinks, listQueryAPI); // Authenticated Item is not found (does not exist) - unless the default `autoCreate=true` is overwritten. if (!nextAuthValidationResult.success) { if (!autoCreate) { return false; } const userProvidedSignUpFields = onSignUp ? await onSignUp({ account, db: context.db, profile, user, query: context.query, session: context.session }) : {}; const data = _objectSpread({ [identityField]: identity }, userProvidedSignUpFields); const findExistingUser = await listQueryAPI.findOne({ where: { email: user.email }, query: 'id email signUpProvider' }); if (findExistingUser) { throw new Error(`Email already in use with ${findExistingUser.signUpProvider}`); } const createUser = await listQueryAPI.createOne({ data }).then(returned => ({ success: true, user: returned })).catch(error => { var _context; // eslint-disable-next-line no-console console.error(error); if (_includesInstanceProperty__default["default"](_context = error.message.toLowerCase()).call(_context, 'unique constraint')) { throw new Error(`Email already in use with ${data.signUpProvider}`); } throw new Error(error); }); return createUser.success; } // if nextAuthValidationResult.success is true, we have an authenticated item, and we sign in with optional `onSignIn` resolver. if (onSignIn) { // TODO: Add docs that developer needs to return true or false from `onSignIn` resolver. // Could have a flag in DB (eg. `disabledUser` etc) return await onSignIn({ account, db: context.db, profile, query: context.query, session: context.session, user }); } return true; } }, cookies, events: events || {}, jwt: jwt || {}, pages: pages || {}, providers, secret: sessionSecret }); } const getNextAuthPage = props => () => NextAuthPage(_objectSpread({}, props)); exports["default"] = NextAuthPage; exports.getNextAuthPage = getNextAuthPage;