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