Move from instagram to facebook oauth

This commit is contained in:
Will Bradley 2025-07-16 19:35:30 -07:00
parent 08cda89609
commit e9860202a8
6 changed files with 215 additions and 37 deletions

View File

@ -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

View File

@ -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);
});
}
});

19
package-lock.json generated
View File

@ -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"
},

View File

@ -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"
},

View File

@ -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 @@
</div>
<a href="/auth/google" class="btn btn-google" id="googleBtn">Google</a>
<a href="/auth/instagram" class="btn btn-instagram" id="instagramBtn">Instagram</a>
<a href="/auth/facebook" class="btn btn-facebook" id="facebookBtn">Facebook</a>
<div class="toggle-form">
<a href="#" onclick="toggleForm()">Don't have an account? Register</a>
@ -241,7 +241,7 @@
</div>
<a href="/auth/google" class="btn btn-google" id="googleRegBtn">Sign up with Google</a>
<a href="/auth/instagram" class="btn btn-instagram" id="instagramRegBtn">Sign up with Instagram</a>
<a href="/auth/facebook" class="btn btn-facebook" id="facebookRegBtn">Sign up with Facebook</a>
<div class="toggle-form">
<a href="#" onclick="toggleForm()">Already have an account? Login</a>
@ -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}`;
}
});
</script>

View File

@ -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;