import NextAuth from 'next-auth' import Providers from 'next-auth/providers' import Adapters from 'next-auth/adapters' import prisma from '../../../api/models' import nodemailer from 'nodemailer' export default (req, res) => NextAuth(req, res, options) const options = { callbacks: { /** * @param {object} token Decrypted JSON Web Token * @param {object} user User object (only available on sign in) * @param {object} account Provider account (only available on sign in) * @param {object} profile Provider profile (only available on sign in) * @param {boolean} isNewUser True if new user (only available on sign in) * @return {object} JSON Web Token that will be saved */ async jwt (token, user, account, profile, isNewUser) { // Add additional session params if (user?.id) { token.id = user.id } // XXX We need to update the user name incase they update it ... kind of hacky // better if we use user id everywhere an ignore the username ... if (token?.id) { const { name } = await prisma.user.findUnique({ where: { id: token.id } }) token.name = name } return token }, async session (session, token) { // we need to add additional session params here session.user.id = token.id session.user.name = token.name return session } }, providers: [ Providers.Credentials({ // The name to display on the sign in form (e.g. 'Sign in with...') name: 'Lightning', // The credentials is used to generate a suitable form on the sign in page. // You can specify whatever fields you are expecting to be submitted. // e.g. domain, username, password, 2FA token, etc. credentials: { pubkey: { label: 'publickey', type: 'text' }, k1: { label: 'k1', type: 'text' } }, async authorize (credentials, req) { const { k1, pubkey } = credentials try { const lnauth = await prisma.lnAuth.findUnique({ where: { k1 } }) if (lnauth.pubkey === pubkey) { let user = await prisma.user.findUnique({ where: { pubkey } }) if (!user) { user = await prisma.user.create({ data: { name: pubkey.slice(0, 10), pubkey } }) } await prisma.lnAuth.delete({ where: { k1 } }) return user } } catch (error) { console.log(error) } return null } }), Providers.GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET, authorization: 'https://github.com/login/oauth/authorize?scope=read:user', profile: profile => { return { ...profile, name: profile.login } } }), Providers.Twitter({ clientId: process.env.TWITTER_ID, clientSecret: process.env.TWITTER_SECRET, profile: profile => { return { ...profile, name: profile.screen_name } } }), Providers.Email({ server: process.env.LOGIN_EMAIL_SERVER, from: process.env.LOGIN_EMAIL_FROM, sendVerificationRequest, profile: profile => { return profile } }) ], adapter: Adapters.Prisma.Adapter({ prisma }), secret: process.env.NEXTAUTH_SECRET, session: { jwt: true }, jwt: { signingKey: process.env.JWT_SIGNING_PRIVATE_KEY }, pages: { signIn: '/login' } } function sendVerificationRequest ({ identifier: email, url, token, baseUrl, provider }) { return new Promise((resolve, reject) => { const { server, from } = provider // Strip protocol from URL and use domain as site name const site = baseUrl.replace(/^https?:\/\//, '') nodemailer.createTransport(server).sendMail( { to: email, from, subject: `login to ${site}`, text: text({ url, site, email }), html: html({ url, site, email }) }, (error) => { if (error) { return reject(new Error('SEND_VERIFICATION_EMAIL_ERROR', error)) } return resolve() } ) }) } // Email HTML body const html = ({ url, site, email }) => { // Insert invisible space into domains and email address to prevent both the // email address and the domain from being turned into a hyperlink by email // clients like Outlook and Apple mail, as this is confusing because it seems // like they are supposed to click on their email address to sign in. const escapedEmail = `${email.replace(/\./g, '​.')}` const escapedSite = `${site.replace(/\./g, '​.')}` // Some simple styling options const backgroundColor = '#f5f5f5' const textColor = '#212529' const mainBackgroundColor = '#ffffff' const buttonBackgroundColor = '#FADA5E' const buttonBorderColor = '#FADA5E' const buttonTextColor = '#212529' // Uses tables for layout and inline CSS due to email client limitations return `
${escapedSite}
login as ${escapedEmail}
login
Or copy and paste this link: ${url}
If you did not request this email you can safely ignore it.
` } // Email text body –fallback for email clients that don't render HTML const text = ({ url, site }) => `Sign in to ${site}\n${url}\n\n`