|
| 1 | +// backend/config/passport.config.js |
| 2 | +const passport = require("passport"); |
| 3 | +const GoogleStrategy = require("passport-google-oauth20").Strategy; |
| 4 | +// const { User } = require("../models"); |
| 5 | +const User = require("../src/models/User"); |
| 6 | + |
| 7 | +// Google OAuth Strategy |
| 8 | +passport.use( |
| 9 | + new GoogleStrategy( |
| 10 | + { |
| 11 | + clientID: process.env.GOOGLE_CLIENT_ID, |
| 12 | + clientSecret: process.env.GOOGLE_CLIENT_SECRET, |
| 13 | + callbackURL: |
| 14 | + process.env.GOOGLE_CALLBACK_URL || |
| 15 | + "http://localhost:5000/api/v1/auth/google/callback", |
| 16 | + scope: ["profile", "email"], |
| 17 | + }, |
| 18 | + async (accessToken, refreshToken, profile, done) => { |
| 19 | + try { |
| 20 | + // Extract user info from Google profile |
| 21 | + const email = profile.emails[0].value; |
| 22 | + const googleId = profile.id; |
| 23 | + const username = profile.displayName || email.split("@")[0]; |
| 24 | + |
| 25 | + // NEW: Extract name from Google profile |
| 26 | + const firstName = profile.name?.givenName || ""; |
| 27 | + const lastName = profile.name?.familyName || ""; |
| 28 | + |
| 29 | + // Check if user already exists with this Google ID |
| 30 | + let user = await User.findOne({ |
| 31 | + where: { google_id: googleId }, |
| 32 | + }); |
| 33 | + |
| 34 | + if (user) { |
| 35 | + // User exists, return user |
| 36 | + return done(null, user); |
| 37 | + } |
| 38 | + |
| 39 | + // Check if user exists with this email (linking accounts) |
| 40 | + user = await User.findOne({ |
| 41 | + where: { email }, |
| 42 | + }); |
| 43 | + |
| 44 | + if (user) { |
| 45 | + // User exists with email but no Google ID - link the accounts |
| 46 | + user.google_id = googleId; |
| 47 | + // NEW: Update profile fields if they were empty |
| 48 | + if (!user.first_name && firstName) user.first_name = firstName; |
| 49 | + if (!user.last_name && lastName) user.last_name = lastName; |
| 50 | + await user.save(); |
| 51 | + return done(null, user); |
| 52 | + } |
| 53 | + |
| 54 | + // Create new user |
| 55 | + user = await User.create({ |
| 56 | + username: username, |
| 57 | + email: email, |
| 58 | + google_id: googleId, |
| 59 | + hashed_password: null, // OAuth users don't have passwords |
| 60 | + email_verified: true, |
| 61 | + // Set from Google profile, or empty string if not available |
| 62 | + first_name: firstName || "", |
| 63 | + last_name: lastName || "", |
| 64 | + company: "", // Always empty for new OAuth users - must be completed |
| 65 | + }); |
| 66 | + |
| 67 | + return done(null, user); |
| 68 | + } catch (error) { |
| 69 | + console.error("Google OAuth error:", error); |
| 70 | + return done(error, null); |
| 71 | + } |
| 72 | + } |
| 73 | + ) |
| 74 | +); |
| 75 | + |
| 76 | +// ORCID OAuth Strategy (using OAuth2Strategy as base) |
| 77 | +const OAuth2Strategy = require("passport-oauth2"); |
| 78 | + |
| 79 | +// passport.use( |
| 80 | +// "orcid", |
| 81 | +// new OAuth2Strategy( |
| 82 | +// { |
| 83 | +// authorizationURL: "https://orcid.org/oauth/authorize", |
| 84 | +// tokenURL: "https://orcid.org/oauth/token", |
| 85 | +// clientID: process.env.ORCID_CLIENT_ID, |
| 86 | +// clientSecret: process.env.ORCID_CLIENT_SECRET, |
| 87 | +// callbackURL: process.env.ORCID_CALLBACK_URL || "http://localhost:5000/api/v1/auth/orcid/callback", |
| 88 | +// scope: "/authenticate", |
| 89 | +// }, |
| 90 | +// async (accessToken, refreshToken, params, profile, done) => { |
| 91 | +// try { |
| 92 | +// // ORCID returns user info in params, not profile |
| 93 | +// const orcidId = params.orcid; |
| 94 | +// const name = params.name || `ORCID User ${orcidId}`; |
| 95 | + |
| 96 | +// // ORCID doesn't always provide email in the basic scope |
| 97 | +// // You might need to make an additional API call to get email |
| 98 | +// // For now, we'll use ORCID ID as identifier |
| 99 | + |
| 100 | +// // Check if user exists with this ORCID ID |
| 101 | +// let user = await User.findOne({ |
| 102 | +// where: { orcid_id: orcidId }, |
| 103 | +// }); |
| 104 | + |
| 105 | +// if (user) { |
| 106 | +// return done(null, user); |
| 107 | +// } |
| 108 | + |
| 109 | +// // If we have email from ORCID, check for existing user |
| 110 | +// if (params.email) { |
| 111 | +// user = await User.findOne({ |
| 112 | +// where: { email: params.email }, |
| 113 | +// }); |
| 114 | + |
| 115 | +// if (user) { |
| 116 | +// // Link ORCID to existing account |
| 117 | +// user.orcid_id = orcidId; |
| 118 | +// await user.save(); |
| 119 | +// return done(null, user); |
| 120 | +// } |
| 121 | +// } |
| 122 | + |
| 123 | +// // Create new user |
| 124 | +// // Note: ORCID might not provide email, so we use ORCID ID as part of email |
| 125 | +// const email = params.email || `${orcidId}@orcid.placeholder`; |
| 126 | +// const username = name.replace(/\s+/g, "_").toLowerCase() || `orcid_${orcidId}`; |
| 127 | + |
| 128 | +// user = await User.create({ |
| 129 | +// username: username, |
| 130 | +// email: email, |
| 131 | +// orcid_id: orcidId, |
| 132 | +// hashed_password: null, |
| 133 | +// email_verified: true, |
| 134 | +// }); |
| 135 | + |
| 136 | +// return done(null, user); |
| 137 | +// } catch (error) { |
| 138 | +// console.error("ORCID OAuth error:", error); |
| 139 | +// return done(error, null); |
| 140 | +// } |
| 141 | +// } |
| 142 | +// ) |
| 143 | +// ); |
| 144 | + |
| 145 | +// Serialize user for session |
| 146 | +passport.serializeUser((user, done) => { |
| 147 | + done(null, user.id); |
| 148 | +}); |
| 149 | + |
| 150 | +// Deserialize user from session |
| 151 | +passport.deserializeUser(async (id, done) => { |
| 152 | + try { |
| 153 | + const user = await User.findByPk(id); |
| 154 | + done(null, user); |
| 155 | + } catch (error) { |
| 156 | + done(error, null); |
| 157 | + } |
| 158 | +}); |
| 159 | + |
| 160 | +module.exports = passport; |
0 commit comments