211 lines
6.5 KiB
JavaScript
211 lines
6.5 KiB
JavaScript
const passport = require('passport');
|
|
const OpenIDConnectStrategy = require('passport-openidconnect').Strategy;
|
|
const { userOps } = require('./database');
|
|
|
|
// Configure OpenID Connect strategy
|
|
function configureAuth(app) {
|
|
// Check if OIDC is configured
|
|
const isOIDCConfigured = process.env.OIDC_ISSUER &&
|
|
process.env.OIDC_CLIENT_ID &&
|
|
process.env.OIDC_CLIENT_SECRET &&
|
|
process.env.OIDC_AUTH_URL &&
|
|
process.env.OIDC_TOKEN_URL &&
|
|
process.env.OIDC_USERINFO_URL;
|
|
|
|
if (!isOIDCConfigured) {
|
|
console.log('OpenID Connect not configured. Authentication features disabled.');
|
|
console.log('To enable authentication, set the following environment variables:');
|
|
console.log('- OIDC_ISSUER');
|
|
console.log('- OIDC_CLIENT_ID');
|
|
console.log('- OIDC_CLIENT_SECRET');
|
|
console.log('- OIDC_AUTH_URL');
|
|
console.log('- OIDC_TOKEN_URL');
|
|
console.log('- OIDC_USERINFO_URL');
|
|
console.log('- OIDC_CALLBACK_URL (optional, defaults to /auth/callback)');
|
|
|
|
// Add disabled auth routes
|
|
app.get('/auth/login', (req, res) => {
|
|
res.status(501).json({ error: 'Authentication not configured' });
|
|
});
|
|
|
|
app.get('/auth/user', (req, res) => {
|
|
res.status(401).json({ error: 'Authentication not configured' });
|
|
});
|
|
|
|
app.post('/auth/logout', (req, res) => {
|
|
res.status(501).json({ error: 'Authentication not configured' });
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
// Session configuration
|
|
const session = require('express-session');
|
|
|
|
app.use(session({
|
|
secret: process.env.SESSION_SECRET || 'your-secret-key-change-in-production',
|
|
resave: false,
|
|
saveUninitialized: false,
|
|
cookie: {
|
|
secure: false, // Set to false for development/HTTP
|
|
httpOnly: true,
|
|
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
|
sameSite: 'lax'
|
|
}
|
|
}));
|
|
|
|
// Initialize Passport
|
|
app.use(passport.initialize());
|
|
app.use(passport.session());
|
|
|
|
// Log OIDC configuration for debugging
|
|
console.log('OIDC Configuration:');
|
|
console.log('Issuer:', process.env.OIDC_ISSUER);
|
|
console.log('Auth URL:', process.env.OIDC_AUTH_URL);
|
|
console.log('Token URL:', process.env.OIDC_TOKEN_URL);
|
|
console.log('UserInfo URL:', process.env.OIDC_USERINFO_URL);
|
|
console.log('Client ID:', process.env.OIDC_CLIENT_ID);
|
|
console.log('Callback URL:', process.env.OIDC_CALLBACK_URL || '/auth/callback');
|
|
|
|
// Configure OpenID Connect strategy
|
|
passport.use('oidc', new OpenIDConnectStrategy({
|
|
issuer: process.env.OIDC_ISSUER,
|
|
authorizationURL: process.env.OIDC_AUTH_URL,
|
|
tokenURL: process.env.OIDC_TOKEN_URL,
|
|
userInfoURL: process.env.OIDC_USERINFO_URL,
|
|
clientID: process.env.OIDC_CLIENT_ID,
|
|
clientSecret: process.env.OIDC_CLIENT_SECRET,
|
|
callbackURL: process.env.OIDC_CALLBACK_URL || '/auth/callback',
|
|
scope: 'openid email profile',
|
|
skipUserProfile: false
|
|
}, (issuer, sub, profile, accessToken, refreshToken, done) => {
|
|
console.log('OIDC Strategy callback:');
|
|
console.log('Issuer:', issuer);
|
|
console.log('Profile data (sub):', sub);
|
|
|
|
// Based on debugging: profile is actually the callback function
|
|
const callback = profile;
|
|
console.log('Using profile parameter as callback:', typeof callback);
|
|
|
|
// Extract user info from sub parameter (which contains the actual profile data)
|
|
const userProfile = {
|
|
sub: sub.id,
|
|
email: sub.emails?.[0]?.value,
|
|
name: sub.displayName || sub.name?.givenName || sub.username
|
|
};
|
|
|
|
console.log('Extracted user profile:', userProfile);
|
|
|
|
userOps.findOrCreateUser(userProfile, (err, user) => {
|
|
if (err) {
|
|
console.error('Database error in findOrCreateUser:', err);
|
|
return callback(err);
|
|
}
|
|
console.log('User from database:', user);
|
|
callback(null, user);
|
|
});
|
|
}));
|
|
|
|
// Serialize user for session
|
|
passport.serializeUser((user, done) => {
|
|
done(null, user.id);
|
|
});
|
|
|
|
// Deserialize user from session
|
|
passport.deserializeUser((id, done) => {
|
|
userOps.findById(id, (err, user) => {
|
|
done(err, user);
|
|
});
|
|
});
|
|
|
|
// Auth routes
|
|
app.get('/auth/login', passport.authenticate('oidc'));
|
|
|
|
app.get('/auth/callback', (req, res, next) => {
|
|
console.log('Auth callback received');
|
|
console.log('Query params:', req.query);
|
|
console.log('Body:', req.body);
|
|
|
|
passport.authenticate('oidc', (err, user, info) => {
|
|
console.log('Passport authenticate callback:');
|
|
console.log('Error:', err);
|
|
console.log('User:', user);
|
|
console.log('Info:', info);
|
|
|
|
if (err) {
|
|
console.error('Authentication error:', err);
|
|
return res.redirect('/?error=auth_failed');
|
|
}
|
|
|
|
if (!user) {
|
|
console.error('No user returned from authentication');
|
|
return res.redirect('/?error=no_user');
|
|
}
|
|
|
|
req.logIn(user, (err) => {
|
|
if (err) {
|
|
console.error('Login error:', err);
|
|
return res.redirect('/?error=login_failed');
|
|
}
|
|
|
|
console.log('User successfully logged in:', user);
|
|
res.redirect('/');
|
|
});
|
|
})(req, res, next);
|
|
});
|
|
|
|
app.post('/auth/logout', (req, res) => {
|
|
req.logout((err) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Logout failed' });
|
|
}
|
|
req.session.destroy((err) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: 'Session destruction failed' });
|
|
}
|
|
res.json({ message: 'Logged out successfully' });
|
|
});
|
|
});
|
|
});
|
|
|
|
// Get current user
|
|
app.get('/auth/user', (req, res) => {
|
|
console.log('Auth check - Session ID:', req.sessionID);
|
|
console.log('Auth check - Is authenticated:', req.isAuthenticated());
|
|
console.log('Auth check - User:', req.user);
|
|
console.log('Auth check - Session:', req.session);
|
|
|
|
if (req.isAuthenticated()) {
|
|
res.json({
|
|
user: {
|
|
id: req.user.id,
|
|
email: req.user.email,
|
|
name: req.user.name
|
|
}
|
|
});
|
|
} else {
|
|
res.status(401).json({ error: 'Not authenticated' });
|
|
}
|
|
});
|
|
}
|
|
|
|
// Middleware to require authentication
|
|
function requireAuth(req, res, next) {
|
|
if (req.isAuthenticated()) {
|
|
return next();
|
|
}
|
|
res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
// Middleware to optionally include user info
|
|
function optionalAuth(req, res, next) {
|
|
// Always proceed, but user info will be available if authenticated
|
|
next();
|
|
}
|
|
|
|
module.exports = {
|
|
configureAuth,
|
|
requireAuth,
|
|
optionalAuth
|
|
};
|