fastcheck/server/src/config/passport.ts
2026-04-08 13:58:46 -04:00

153 lines
5.6 KiB
TypeScript

import passport from 'passport';
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
import { Strategy as MicrosoftStrategy } from 'passport-microsoft';
import { User } from '../models/user.model';
import { Tenant } from '../models/tenant.model';
// Serialize user for session
passport.serializeUser((user: any, done) => {
done(null, user._id);
});
// Deserialize user from session
passport.deserializeUser(async (id: string, done) => {
try {
const user = await User.findById(id).populate('tenant');
done(null, user);
} catch (error) {
done(error, null);
}
});
// Google OAuth Strategy
if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_CALLBACK_URL || '/auth/google/callback',
authorizationURL: process.env.GOOGLE_AUTHORIZATION_URL || 'https://accounts.google.com/o/oauth2/v2/auth',
tokenURL: process.env.GOOGLE_TOKEN_URL || 'https://www.googleapis.com/oauth2/v4/token',
userProfileURL: process.env.GOOGLE_USERINFO_URL || 'https://www.googleapis.com/oauth2/v3/userinfo',
},
async (accessToken: string, refreshToken: string, profile: any, done: any) => {
try {
// Check if user already exists with this Google ID
let user = await User.findOne({
oauthProvider: 'google',
oauthId: profile.id,
}).populate('tenant');
if (user) {
return done(null, user);
}
// Check if user exists with same email
user = await User.findOne({ email: profile.emails?.[0]?.value }).populate('tenant');
if (user) {
// Link Google account to existing user
user.oauthProvider = 'google';
user.oauthId = profile.id;
user.profilePicture = profile.photos?.[0]?.value;
await user.save();
return done(null, user);
}
// Create new user - but they need a tenant assignment
// For now, we'll create the user but mark as inactive until admin assigns tenant
const newUser = new User({
name: profile.displayName || `${profile.name?.givenName} ${profile.name?.familyName}`,
email: profile.emails?.[0]?.value,
oauthProvider: 'google',
oauthId: profile.id,
profilePicture: profile.photos?.[0]?.value,
role: 'evaluator',
isActive: false, // Requires admin activation
// We'll need to handle tenant assignment separately
tenant: null as any, // This will need to be handled by admin
});
// For demo purposes, let's find the first available tenant
// In production, you'd want a proper tenant assignment flow
const defaultTenant = await Tenant.findOne({ status: 'active' });
if (defaultTenant) {
newUser.tenant = defaultTenant._id;
}
await newUser.save();
return done(null, newUser as any);
} catch (error) {
return done(error, null);
}
}
)
);
}
// Microsoft OAuth Strategy
if (process.env.MICROSOFT_CLIENT_ID && process.env.MICROSOFT_CLIENT_SECRET) {
passport.use(
new MicrosoftStrategy(
{
clientID: process.env.MICROSOFT_CLIENT_ID,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
callbackURL: process.env.MICROSOFT_CALLBACK_URL || '/auth/microsoft/callback',
authorizationURL: process.env.MICROSOFT_AUTHORIZATION_URL || 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
tokenURL: process.env.MICROSOFT_TOKEN_URL || 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
scope: ['user.read'],
},
async (accessToken: string, refreshToken: string, profile: any, done: any) => {
try {
// Check if user already exists with this Microsoft ID
let user = await User.findOne({
oauthProvider: 'microsoft',
oauthId: profile.id,
}).populate('tenant');
if (user) {
return done(null, user);
}
// Check if user exists with same email
user = await User.findOne({ email: profile.emails?.[0]?.value }).populate('tenant');
if (user) {
// Link Microsoft account to existing user
user.oauthProvider = 'microsoft';
user.oauthId = profile.id;
user.profilePicture = profile.photos?.[0]?.value;
await user.save();
return done(null, user);
}
// Create new user - but they need a tenant assignment
const newUser = new User({
name: profile.displayName || `${profile.name?.givenName} ${profile.name?.familyName}`,
email: profile.emails?.[0]?.value,
oauthProvider: 'microsoft',
oauthId: profile.id,
profilePicture: profile.photos?.[0]?.value,
role: 'evaluator',
isActive: false, // Requires admin activation
tenant: null as any,
});
// For demo purposes, let's find the first available tenant
const defaultTenant = await Tenant.findOne({ status: 'active' });
if (defaultTenant) {
newUser.tenant = defaultTenant._id;
}
await newUser.save();
return done(null, newUser);
} catch (error) {
return done(error, null);
}
}
)
);
}
export default passport;