Add OpenID Connect authentication and SQLite database with user preferences and favorites system
This commit is contained in:
156
backend/src/auth.js
Normal file
156
backend/src/auth.js
Normal file
@@ -0,0 +1,156 @@
|
||||
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: process.env.NODE_ENV === 'production',
|
||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
||||
}
|
||||
}));
|
||||
|
||||
// Initialize Passport
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
// 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'
|
||||
}, (issuer, sub, profile, accessToken, refreshToken, done) => {
|
||||
// Extract user info from profile
|
||||
const userProfile = {
|
||||
sub: profile.id,
|
||||
email: profile.emails?.[0]?.value,
|
||||
name: profile.displayName
|
||||
};
|
||||
|
||||
userOps.findOrCreateUser(userProfile, (err, user) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
return done(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',
|
||||
passport.authenticate('oidc', { failureRedirect: '/login' }),
|
||||
(req, res) => {
|
||||
// Successful authentication, redirect to frontend
|
||||
res.redirect(process.env.FRONTEND_URL || '/');
|
||||
}
|
||||
);
|
||||
|
||||
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) => {
|
||||
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
|
||||
};
|
||||
Reference in New Issue
Block a user