From e9860202a86bbd670bf54c482a1f3dd9bf94f5a1 Mon Sep 17 00:00:00 2001 From: Will Bradley Date: Wed, 16 Jul 2025 19:35:30 -0700 Subject: [PATCH] Move from instagram to facebook oauth --- env.dist | 6 +- migrate-instagram-to-facebook.js | 174 +++++++++++++++++++++++++++++++ package-lock.json | 19 ++-- package.json | 5 +- public/login.html | 16 +-- server.js | 32 +++--- 6 files changed, 215 insertions(+), 37 deletions(-) create mode 100644 migrate-instagram-to-facebook.js diff --git a/env.dist b/env.dist index c2e5f5f..a4e5773 100644 --- a/env.dist +++ b/env.dist @@ -5,9 +5,9 @@ SESSION_SECRET=your-super-secret-session-key-change-this-in-production GOOGLE_CLIENT_ID=your-google-client-id GOOGLE_CLIENT_SECRET=your-google-client-secret -# Instagram OAuth credentials -INSTAGRAM_CLIENT_ID=your-instagram-client-id -INSTAGRAM_CLIENT_SECRET=your-instagram-client-secret +# Facebook OAuth credentials +FACEBOOK_CLIENT_ID=your-facebook-app-id +FACEBOOK_CLIENT_SECRET=your-facebook-app-secret # Database configuration DATABASE_URL=./water_stations.db diff --git a/migrate-instagram-to-facebook.js b/migrate-instagram-to-facebook.js new file mode 100644 index 0000000..4a5ce8b --- /dev/null +++ b/migrate-instagram-to-facebook.js @@ -0,0 +1,174 @@ +#!/usr/bin/env node + +const sqlite3 = require('sqlite3').verbose(); +const path = require('path'); + +console.log('šŸ”„ Starting Instagram to Facebook schema migration...'); + +// Open database +const dbPath = path.join(__dirname, 'water_stations.db'); +const db = new sqlite3.Database(dbPath); + +db.serialize(() => { + console.log('šŸ“‹ Checking current database schema...'); + + // Check if instagram_id column exists + db.all("PRAGMA table_info(users)", (err, columns) => { + if (err) { + console.error('āŒ Error checking table schema:', err); + process.exit(1); + } + + const hasInstagramId = columns.some(col => col.name === 'instagram_id'); + const hasFacebookId = columns.some(col => col.name === 'facebook_id'); + + console.log(`šŸ“Š Current schema status:`); + console.log(` - instagram_id column exists: ${hasInstagramId}`); + console.log(` - facebook_id column exists: ${hasFacebookId}`); + + if (!hasInstagramId && hasFacebookId) { + console.log('āœ… Migration already completed! Database is up to date.'); + db.close(); + return; + } + + if (hasInstagramId) { + // Count Instagram users before migration + db.get('SELECT COUNT(*) as count FROM users WHERE instagram_id IS NOT NULL', (err, row) => { + if (err) { + console.error('āŒ Error counting Instagram users:', err); + process.exit(1); + } + + const instagramUserCount = row.count; + console.log(`šŸ“Š Found ${instagramUserCount} Instagram users in database`); + + if (instagramUserCount > 0) { + console.log('āš ļø WARNING: Instagram OAuth is no longer supported.'); + console.log(' These users will need to register again using Facebook or Google OAuth,'); + console.log(' or create a username/password account.'); + } + + performMigration(hasFacebookId, instagramUserCount); + }); + } else { + console.log('šŸ“„ No instagram_id column found, performing clean schema update...'); + performMigration(hasFacebookId, 0); + } + }); + + function performMigration(hasFacebookId, instagramUserCount) { + console.log('\nšŸ”§ Performing database migration...'); + + // Add facebook_id column if it doesn't exist + if (!hasFacebookId) { + console.log('āž• Adding facebook_id column...'); + db.run('ALTER TABLE users ADD COLUMN facebook_id TEXT', (err) => { + if (err) { + console.error('āŒ Error adding facebook_id column:', err); + process.exit(1); + } + + console.log('āœ… facebook_id column added successfully'); + dropInstagramColumn(instagramUserCount); + }); + } else { + dropInstagramColumn(instagramUserCount); + } + } + + function dropInstagramColumn(instagramUserCount) { + // Check if instagram_id column exists before trying to drop it + db.all("PRAGMA table_info(users)", (err, columns) => { + if (err) { + console.error('āŒ Error checking table schema:', err); + process.exit(1); + } + + const hasInstagramId = columns.some(col => col.name === 'instagram_id'); + + if (hasInstagramId) { + console.log('šŸ—‘ļø Removing deprecated instagram_id column...'); + + // SQLite doesn't support DROP COLUMN directly, so we need to recreate the table + const createNewTableSQL = ` + CREATE TABLE users_new ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE, + email TEXT UNIQUE, + password_hash TEXT, + google_id TEXT, + facebook_id TEXT, + display_name TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `; + + db.run(createNewTableSQL, (err) => { + if (err) { + console.error('āŒ Error creating new users table:', err); + process.exit(1); + } + + // Copy data from old table to new table (excluding instagram_id) + const copyDataSQL = ` + INSERT INTO users_new (id, username, email, password_hash, google_id, facebook_id, display_name, created_at) + SELECT id, username, email, password_hash, google_id, NULL, display_name, created_at + FROM users + `; + + db.run(copyDataSQL, (err) => { + if (err) { + console.error('āŒ Error copying data to new table:', err); + process.exit(1); + } + + // Drop old table and rename new table + db.run('DROP TABLE users', (err) => { + if (err) { + console.error('āŒ Error dropping old users table:', err); + process.exit(1); + } + + db.run('ALTER TABLE users_new RENAME TO users', (err) => { + if (err) { + console.error('āŒ Error renaming new users table:', err); + process.exit(1); + } + + completeMigration(instagramUserCount); + }); + }); + }); + }); + } else { + completeMigration(instagramUserCount); + } + }); + } + + function completeMigration(instagramUserCount) { + console.log('\nāœ… Database migration completed successfully!'); + console.log('\nšŸ“Š Migration Summary:'); + console.log(' āœ… facebook_id column added'); + console.log(' āœ… instagram_id column removed (deprecated)'); + + if (instagramUserCount > 0) { + console.log(` āš ļø ${instagramUserCount} Instagram users will need to re-register`); + console.log('\nšŸ“ Next Steps:'); + console.log(' 1. Set up Facebook OAuth credentials in your .env file'); + console.log(' 2. Notify existing Instagram users to register with Facebook/Google OAuth'); + console.log(' 3. Consider adding a migration notice in your app UI'); + } + + console.log('\nšŸŽ‰ Your database is now ready for Facebook OAuth!'); + + db.close((err) => { + if (err) { + console.error('āŒ Error closing database:', err); + process.exit(1); + } + process.exit(0); + }); + } +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3e8ae7a..082398c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,8 +16,8 @@ "express": "^4.18.2", "express-session": "^1.17.3", "passport": "^0.6.0", + "passport-facebook": "^3.0.0", "passport-google-oauth20": "^2.0.0", - "passport-instagram": "^1.0.0", "passport-local": "^1.0.0", "sqlite3": "^5.1.6" }, @@ -1769,10 +1769,11 @@ "url": "https://github.com/sponsors/jaredhanson" } }, - "node_modules/passport-google-oauth20": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", - "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "node_modules/passport-facebook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/passport-facebook/-/passport-facebook-3.0.0.tgz", + "integrity": "sha512-K/qNzuFsFISYAyC1Nma4qgY/12V3RSLFdFVsPKXiKZt434wOvthFW1p7zKa1iQihQMRhaWorVE1o3Vi1o+ZgeQ==", + "license": "MIT", "dependencies": { "passport-oauth2": "1.x.x" }, @@ -1780,10 +1781,10 @@ "node": ">= 0.4.0" } }, - "node_modules/passport-instagram": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-instagram/-/passport-instagram-1.0.0.tgz", - "integrity": "sha512-irnGcLgCa+vYF5k/7+gU/uiLfzs6VJds8cMOFiuwq2mRAiRrEPyM5rIzXurDGCQl7s5uhEcolpBOGqChUGPtAg==", + "node_modules/passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", "dependencies": { "passport-oauth2": "1.x.x" }, diff --git a/package.json b/package.json index 7af0c48..6818391 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "main": "server.js", "scripts": { "start": "node server.js", - "dev": "nodemon server.js" + "dev": "nodemon server.js", + "migrate": "node migrate-instagram-to-facebook.js" }, "dependencies": { "bcryptjs": "^2.4.3", @@ -16,8 +17,8 @@ "express": "^4.18.2", "express-session": "^1.17.3", "passport": "^0.6.0", + "passport-facebook": "^3.0.0", "passport-google-oauth20": "^2.0.0", - "passport-instagram": "^1.0.0", "passport-local": "^1.0.0", "sqlite3": "^5.1.6" }, diff --git a/public/login.html b/public/login.html index 126add3..168303e 100644 --- a/public/login.html +++ b/public/login.html @@ -107,14 +107,14 @@ background: #c23321; } - .btn-instagram { - background: #e4405f; + .btn-facebook { + background: #1877f2; color: white; width: 49%; } - .btn-instagram:hover { - background: #d62d46; + .btn-facebook:hover { + background: #166fe5; } .divider { @@ -212,7 +212,7 @@ Google - Instagram + Facebook
Don't have an account? Register @@ -241,7 +241,7 @@
Sign up with Google - Sign up with Instagram + Sign up with Facebook
Already have an account? Login @@ -361,9 +361,9 @@ if (redirectUrl) { const encodedRedirect = encodeURIComponent(redirectUrl); document.getElementById('googleBtn').href = `/auth/google?redirect=${encodedRedirect}`; - document.getElementById('instagramBtn').href = `/auth/instagram?redirect=${encodedRedirect}`; + document.getElementById('facebookBtn').href = `/auth/facebook?redirect=${encodedRedirect}`; document.getElementById('googleRegBtn').href = `/auth/google?redirect=${encodedRedirect}`; - document.getElementById('instagramRegBtn').href = `/auth/instagram?redirect=${encodedRedirect}`; + document.getElementById('facebookRegBtn').href = `/auth/facebook?redirect=${encodedRedirect}`; } }); diff --git a/server.js b/server.js index 7ff4913..36cf21e 100644 --- a/server.js +++ b/server.js @@ -4,7 +4,7 @@ const session = require('express-session'); const SQLiteStore = require('connect-sqlite3')(session); const passport = require('passport'); const GoogleStrategy = require('passport-google-oauth20').Strategy; -const InstagramStrategy = require('passport-instagram').Strategy; +const FacebookStrategy = require('passport-facebook').Strategy; const LocalStrategy = require('passport-local').Strategy; const bcrypt = require('bcryptjs'); const bodyParser = require('body-parser'); @@ -28,7 +28,7 @@ db.serialize(() => { email TEXT UNIQUE, password_hash TEXT, google_id TEXT, - instagram_id TEXT, + facebook_id TEXT, display_name TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`); @@ -162,7 +162,7 @@ passport.use(new LocalStrategy( function initializeOAuthStrategies(baseUrl = '') { // Clear existing strategies passport.unuse('google'); - passport.unuse('instagram'); + passport.unuse('facebook'); // Google OAuth Strategy passport.use(new GoogleStrategy({ @@ -190,20 +190,22 @@ function initializeOAuthStrategies(baseUrl = '') { }); })); - // Instagram OAuth Strategy - passport.use(new InstagramStrategy({ - clientID: process.env.INSTAGRAM_CLIENT_ID, - clientSecret: process.env.INSTAGRAM_CLIENT_SECRET, - callbackURL: `${baseUrl}/auth/instagram/callback` + // Facebook OAuth Strategy + passport.use(new FacebookStrategy({ + clientID: process.env.FACEBOOK_CLIENT_ID, + clientSecret: process.env.FACEBOOK_CLIENT_SECRET, + callbackURL: `${baseUrl}/auth/facebook/callback`, + profileFields: ['id', 'displayName', 'emails'] }, (accessToken, refreshToken, profile, done) => { - db.get('SELECT * FROM users WHERE instagram_id = ?', [profile.id], (err, user) => { + db.get('SELECT * FROM users WHERE facebook_id = ?', [profile.id], (err, user) => { if (err) return done(err); if (user) { return done(null, user); } else { - db.run('INSERT INTO users (instagram_id, display_name) VALUES (?, ?)', - [profile.id, profile.displayName], + const email = profile.emails && profile.emails[0] ? profile.emails[0].value : null; + db.run('INSERT INTO users (facebook_id, display_name, email) VALUES (?, ?, ?)', + [profile.id, profile.displayName, email], function(err) { if (err) return done(err); @@ -261,7 +263,7 @@ app.get('/auth/google/callback', } ); -app.get('/auth/instagram', (req, res, next) => { +app.get('/auth/facebook', (req, res, next) => { if (req.query.redirect) { req.session.redirectUrl = req.query.redirect; } @@ -270,11 +272,11 @@ app.get('/auth/instagram', (req, res, next) => { const baseUrl = getBaseUrl(req); initializeOAuthStrategies(baseUrl); - passport.authenticate('instagram')(req, res, next); + passport.authenticate('facebook', { scope: ['email'] })(req, res, next); }); -app.get('/auth/instagram/callback', - passport.authenticate('instagram', { failureRedirect: '/login' }), +app.get('/auth/facebook/callback', + passport.authenticate('facebook', { failureRedirect: '/login' }), (req, res) => { const redirectUrl = req.session.redirectUrl || '/city/salem/dashboard'; delete req.session.redirectUrl;