UNPKG

mlgta

Version:

Can log in via google, x, discord. ![alt text](https://raw.githubusercontent.com/takayamaekawa/branding/refs/heads/master/repo/mlgta/login_form.png) After login successfully, redirecting. ![alt text](https://raw.githubusercontent.com/takayamaekawa/b

194 lines (160 loc) 6.11 kB
import path from 'path'; import passport from 'passport'; import bcrypt from 'bcrypt'; import config from '../config'; import knex from '../config/knex'; import User from '../models/user'; import { sendOneTimePass } from '../controllers/emailController'; function generateOTP(): string { return Math.floor(100000 + Math.random() * 900000).toString(); } const getCallBackURL = (type: string): string => { return config.server.url + path.join(config.server.root, `/auth/${type}/callback`); } passport.serializeUser((user, done) => { if (typeof user !== 'object' || user === null) { throw new Error('User must be a non-null object'); } const user_info = user as { id: number;[key: string]: any }; if (!('id' in user_info) || typeof user_info.id !== 'number') { throw new Error('User object does not contain a valid id'); } done(null, user_info.id); }); passport.deserializeUser(async (id: number, done) => { try { const user: Express.User = await User.findById(id); done(null, user); } catch (error) { done(error, null); } }); import { Strategy as DiscordStrategy, Profile as DiscordProfile } from 'passport-discord'; const discordClientId = config.server.modules.passport.discord.key; const discordClientSecret = config.server.modules.passport.discord.secret; const discordCallbackURL = getCallBackURL('discord'); if (!discordClientId || !discordClientSecret || !discordCallbackURL) { throw new Error('Discord OAuth settings are missing in .env file.'); } passport.use(new DiscordStrategy({ clientID: discordClientId, clientSecret: discordClientSecret, callbackURL: discordCallbackURL, scope: ['identify', 'email'] // 'guilds', 'guild.join' }, async (accessToken, _, profile: DiscordProfile, done) => { try { const existingUser = await knex('users').where({ discordId: profile.id }).first(); if (existingUser) { return done(null, existingUser); } const [newUserId] = await knex('users').insert({ discordId: profile.id, name: profile.username, email: profile.email, avatar: profile.avatar, accessToken, }); const newUser = await knex('users').where({ id: newUserId }).first(); return done(null, newUser) } catch (err) { console.error('Error in DiscordStrategy: ', err) return done(null, false, { message: err }); } })); import { Strategy as GoogleStrategy } from 'passport-google-oauth20'; const googleClientId = config.server.modules.passport.google.key; const googleClientSecret = config.server.modules.passport.google.secret; const googleCallbackURL = getCallBackURL('google'); if (!googleClientId || !googleClientSecret || !googleCallbackURL) { throw new Error('Google OAuth setting are missing in .env file'); } passport.use(new GoogleStrategy({ clientID: googleClientId, clientSecret: googleClientSecret, callbackURL: googleCallbackURL, scope: ['profile', 'email'], }, async (accessToken, _, profile, done) => { try { const existingUser = await knex('users').where({ googleId: profile.id }).first(); if (existingUser) { return done(null, existingUser); } const [newUserId] = await knex('users').insert({ googleId: profile.id, name: profile.displayName, email: profile.emails?.[0].value, avatar: profile.photos?.[0].value, accessToken, }); const newUser = await knex('users').where({ id: newUserId }).first(); return done(null, newUser); } catch (err) { console.error('Error in GoogleStrategy: ', err) return done(err, false); } })); import { Strategy as XStrategy } from 'passport-twitter'; const xConsumerKey = config.server.modules.passport.x.key; const xConsumerSecret = config.server.modules.passport.x.secret; const xCallbackURL = getCallBackURL("x"); if (!xConsumerKey || !xConsumerSecret || !xCallbackURL) { throw new Error('X OAuth settings are missing in .env file.'); } passport.use('x', new XStrategy({ consumerKey: xConsumerKey, consumerSecret: xConsumerSecret, callbackURL: xCallbackURL, includeEmail: true, }, async (token, _, profile, done) => { try { const existingUser = await knex('users').where({ xId: profile.id }).first(); if (existingUser) { console.log('Existing user found: ', existingUser); return done(null, existingUser); } const [newUserId] = await knex('users').insert({ xId: profile.id, name: profile.displayName, xname: profile.username, email: profile.emails?.[0].value, avatar: profile.photos?.[0].value, accessToken: token, }); const newUser = await knex('users').where({ id: newUserId }).first(); return done(null, newUser); } catch (err) { console.error('Error in XStrategy: ', err); return done(err); } })); import { IVerifyOptions, Strategy as LocalStrategy } from 'passport-local'; import { generateToken } from './jwt'; passport.use(new LocalStrategy({ usernameField: 'username', passwordField: 'password', }, async (username: string, password: string, done) => { try { const user = await knex('users').where({ name: username }).first(); if (!user) { return done(null, false, { message: 'Invalid username or password' }); } const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) { return done(null, false, { message: 'Invalid username or password' }); } const token = await generateToken(user, true); if (!user.email) { const redirectUrl = `${config.server.root}/auth/set-email?token=${token}`; return done(null, false, { message: 'Email not set', redirectUrl } as IVerifyOptions); } const otp = generateOTP(); await sendOneTimePass(user.email, otp); await knex('users').where({ name: username }).update({ otp }); const redirectUrl: string = `${config.server.root}/auth/verify-otp?token=${token}`; return done(null, false, { message: 'Email not set', redirectUrl } as IVerifyOptions); } catch (err) { console.error('Error in LocalStrategy: ', err); return done(err); } })); export default passport;