End-to-End Example: Google OAuth + JWT + WebSocket Auth with Socket.IO

Project Structure

oauth-websocket-app/

├── server.js
├── auth/
│ └── google.js
├── middleware/
│ └── socketAuth.js
├── public/
│ └── index.html
├── .env
├── package.json

Prerequisites

  • Node.js installed
  • Google Developer Console project with OAuth credentials (Client ID and Secret)
  • Install dependencies using:
npm install express socket.io passport passport-google-oauth20 jsonwebtoken dotenv express-session

Step 1: Environment Variables (.env)

GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
JWT_SECRET=your_jwt_secret

Step 2: Google OAuth Setup (auth/google.js)

const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;

passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "/auth/google/callback"
}, (accessToken, refreshToken, profile, done) => {
const user = {
id: profile.id,
name: profile.displayName,
email: profile.emails[0].value
};
done(null, user);
}));

passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((obj, done) => done(null, obj));

Step 3: Socket Middleware (middleware/socketAuth.js)

const jwt = require('jsonwebtoken');

module.exports = function (socket, next) {
const token = socket.handshake.auth.token;

if (!token) {
return next(new Error('Authentication token required'));
}

try {
const user = jwt.verify(token, process.env.JWT_SECRET);
socket.user = user;
next();
} catch (err) {
next(new Error('Invalid token'));
}
};

Step 4: Express + Socket.IO App (server.js)

require('dotenv').config();
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const session = require('express-session');
const passport = require('passport');
const jwt = require('jsonwebtoken');
const socketAuth = require('./middleware/socketAuth');

require('./auth/google');

const app = express();
const server = http.createServer(app);
const io = new Server(server);

app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());

app.use(express.static('public'));

app.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] }));

app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/' }),
(req, res) => {
const token = jwt.sign(req.user, process.env.JWT_SECRET, { expiresIn: '1h' });
res.redirect(`/index.html?token=${token}`);
}
);

io.use(socketAuth);

io.on('connection', (socket) => {
console.log(`User connected: ${socket.user.name}`);

socket.on('chat message', (msg) => {
io.emit('chat message', { user: socket.user.name, message: msg });
});
});

server.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});

Step 5: Frontend (public/index.html)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OAuth + WebSocket Chat</title>
</head>
<body>
<h1>WebSocket Chat</h1>
<ul id="messages"></ul>
<input id="msg" autocomplete="off" />
<button onclick="send()">Send</button>

<script src="/socket.io/socket.io.js"></script>
<script>
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');

if (!token) {
window.location.href = "/auth/google";
}

const socket = io({ auth: { token } });

socket.on('connect', () => {
console.log("Connected to server");
});

socket.on('chat message', (data) => {
const item = document.createElement('li');
item.textContent = `${data.user}: ${data.message}`;
document.getElementById('messages').appendChild(item);
});

function send() {
const msg = document.getElementById('msg').value;
socket.emit('chat message', msg);
document.getElementById('msg').value = '';
}
</script>
</body>
</html>

Security Recommendations

  • Use HTTPS and WSS in production environments
  • Avoid storing JWTs in localStorage; prefer in-memory or HttpOnly cookies
  • Add input validation and rate-limiting
  • Implement logout and token blacklisting if necessary

Summary

  • OAuth handles authentication and generates a secure JWT
  • JWT is passed to the frontend and used for WebSocket connection
  • Token is verified server-side via middleware before any real-time communication is allowed