Skip to content

Commit 185f63d

Browse files
authored
Merge pull request code100x#43 from TanmayDhobale/auth
feat: Implement Google, GitHub, and Facebook Authentication with an passport.js code100x#39
2 parents ad8de74 + 87c864a commit 185f63d

17 files changed

Lines changed: 12233 additions & 1013 deletions

File tree

apps/backend/.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
GOOGLE_CLIENT_ID=your_google_client_id
2+
GOOGLE_CLIENT_SECRET=your_google_client_secret
3+
GITHUB_CLIENT_ID=your_github_client_id
4+
GITHUB_CLIENT_SECRET=your_github_client_secret
5+
FACEBOOK_APP_ID=your_facebook_app_id
6+
FACEBOOK_APP_SECRET=your_facebook_app_secret

apps/backend/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,9 @@
1414
"dependencies": {
1515
"@types/express": "^4.17.21",
1616
"express": "^4.19.2"
17+
},
18+
"devDependencies": {
19+
"@types/jsonwebtoken": "^9.0.6",
20+
"@types/passport": "^1.0.16"
1721
}
1822
}

apps/backend/src/index.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,39 @@
1-
import express from "express"
1+
import express from "express";
22
import v1Router from "./router/v1";
3+
const cookieSession = require("cookie-session");
4+
const cors = require("cors");
5+
const passportSetup = require("./passport");
6+
const passport = require("passport");
7+
import authRoute from "./router/auth";
8+
import dotenv from "dotenv";
39

410
const app = express();
511

6-
app.use("/v1", v1Router);
12+
dotenv.config();
13+
14+
app.use(
15+
cookieSession({
16+
name: "session",
17+
keys: ["lama"],
18+
maxAge: 24 * 60 * 60 * 100,
19+
})
20+
);
21+
22+
app.use(passport.initialize());
23+
app.use(passport.session());
24+
25+
app.use(
26+
cors({
27+
origin: "http://localhost:5173/",
28+
methods: "GET,POST,PUT,DELETE",
29+
credentials: true,
30+
})
31+
);
32+
33+
app.use("/auth", authRoute);
34+
app.use("/v1", v1Router);
35+
36+
const PORT = process.env.PORT || 5173;
37+
app.listen(PORT, () => {
38+
console.log(`Server is running on port ${PORT}`);
39+
});

apps/backend/src/passport.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
const GoogleStrategy = require("passport-google-oauth20").Strategy;
2+
const GithubStrategy = require("passport-github2").Strategy;
3+
const FacebookStrategy = require("passport-facebook").Strategy;
4+
import passport from "passport";
5+
import dotenv from "dotenv";
6+
7+
dotenv.config();
8+
9+
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID || "your_google_client_id";
10+
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET || "your_google_client_secret";
11+
const GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID || "your_github_client_id";
12+
const GITHUB_CLIENT_SECRET = process.env.GITHUB_CLIENT_SECRET || "your_github_client_secret";
13+
const FACEBOOK_APP_ID = process.env.FACEBOOK_APP_ID || "your_facebook_app_id";
14+
const FACEBOOK_APP_SECRET = process.env.FACEBOOK_APP_SECRET || "your_facebook_app_secret";
15+
16+
if (!GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET || !GITHUB_CLIENT_ID || !GITHUB_CLIENT_SECRET || !FACEBOOK_APP_ID || !FACEBOOK_APP_SECRET) {
17+
throw new Error('Missing environment variables for authentication providers');
18+
}
19+
passport.use(
20+
new GoogleStrategy(
21+
{
22+
clientID: GOOGLE_CLIENT_ID,
23+
clientSecret: GOOGLE_CLIENT_SECRET,
24+
callbackURL: "/auth/google/callback",
25+
},
26+
function (accessToken: string, refreshToken: string, profile: any, done: (error: any, user?: any) => void) {
27+
done(null, profile);
28+
}
29+
)
30+
);
31+
32+
passport.use(
33+
new GithubStrategy(
34+
{
35+
clientID: GITHUB_CLIENT_ID,
36+
clientSecret: GITHUB_CLIENT_SECRET,
37+
callbackURL: "/auth/github/callback",
38+
},
39+
function (accessToken: string, refreshToken: string, profile: any, done: (error: any, user?: any) => void) {
40+
done(null, profile);
41+
}
42+
)
43+
);
44+
45+
passport.use(
46+
new FacebookStrategy(
47+
{
48+
clientID: FACEBOOK_APP_ID,
49+
clientSecret: FACEBOOK_APP_SECRET,
50+
callbackURL: "/auth/facebook/callback",
51+
},
52+
function (accessToken: string, refreshToken: string, profile: any, done: (error: any, user?: any) => void) {
53+
done(null, profile);
54+
}
55+
)
56+
);
57+
58+
passport.serializeUser((user: any, done: (error: any, id?: any) => void) => {
59+
done(null, user);
60+
});
61+
62+
passport.deserializeUser((user: any, done: (error: any, user?: any) => void) => {
63+
done(null, user);
64+
});

apps/backend/src/router/auth.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Request, Response, Router } from 'express';
2+
const router = Router();
3+
const passport = require('passport');
4+
import jwt from 'jsonwebtoken';
5+
6+
const CLIENT_URL = 'http://localhost:5173/game';
7+
const JWT_SECRET = process.env.JWT_SECRET || 'your_secret_key';
8+
9+
interface User {
10+
_id: string;
11+
}
12+
13+
router.get('/login/success', (req: Request, res: Response) => {
14+
if (req.user) {
15+
const user = req.user as User;
16+
const token = jwt.sign({ userId: user._id }, JWT_SECRET);
17+
res.cookie('jwt', token, { httpOnly: true, sameSite: 'strict' });
18+
res.status(200).json({ success: true, message: 'successful' });
19+
} else {
20+
res.status(401).json({ success: false, message: 'Unauthorized' });
21+
}
22+
});
23+
24+
router.get('/login/failed', (req: Request, res: Response) => {
25+
res.status(401).json({ success: false, message: 'failure' });
26+
});
27+
28+
router.get('/logout', (req: Request, res: Response) => {
29+
req.logout((err) => {
30+
if (err) {
31+
console.error('Error logging out:', err);
32+
res.status(500).json({ error: 'Failed to log out' });
33+
} else {
34+
res.clearCookie('jwt');
35+
res.redirect('http://localhost:5173/');
36+
}
37+
});
38+
});
39+
40+
router.get('/google', passport.authenticate('google', { scope: ['profile'] }));
41+
42+
router.get('/google/callback', passport.authenticate('google', {
43+
successRedirect: CLIENT_URL,
44+
failureRedirect: '/login/failed',
45+
}));
46+
47+
router.get('/github', passport.authenticate('github', { scope: ['profile'] }));
48+
49+
router.get('/github/callback', passport.authenticate('github', {
50+
successRedirect: CLIENT_URL,
51+
failureRedirect: '/login/failed',
52+
}));
53+
54+
router.get('/facebook', passport.authenticate('facebook', { scope: ['profile'] }));
55+
56+
router.get('/facebook/callback', passport.authenticate('facebook', {
57+
successRedirect: CLIENT_URL,
58+
failureRedirect: '/login/failed',
59+
}));
60+
61+
export default router;

apps/backend/tsconfig.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,12 @@
88
"forceConsistentCasingInFileNames": true,
99
"strict": true,
1010
"skipLibCheck": true
11-
}
11+
12+
},
13+
"include": [
14+
"src/**/*",
15+
"routes/**/*",
16+
"**/*.js"
17+
],
18+
"outDir": "dist"
1219
}

apps/frontend/src/App.tsx

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,49 @@
1-
import './App.css'
2-
import { BrowserRouter, Route, Routes } from "react-router-dom";
1+
import './App.css';
2+
import { BrowserRouter, Route, Routes } from 'react-router-dom';
33
import { Landing } from './screens/Landing';
44
import { Game } from './screens/Game';
5+
import Login from './screens/Login';
6+
import { useEffect, useState } from 'react';
57

68
function App() {
9+
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
10+
11+
useEffect(() => {
12+
const fetchToken = async () => {
13+
try {
14+
const response = await fetch('http://localhost:5173/auth/login/success', {
15+
method: 'GET',
16+
credentials: 'include',
17+
headers: {
18+
Accept: 'application/json',
19+
'Content-Type': 'application/json',
20+
},
21+
});
22+
23+
if (response.ok) {
24+
setIsAuthenticated(true);
25+
} else {
26+
throw new Error('Authentication failed');
27+
}
28+
} catch (err) {
29+
console.error(err);
30+
}
31+
};
32+
33+
fetchToken();
34+
}, []);
35+
736
return (
8-
<div className='h-screen bg-slate-950'>
9-
<BrowserRouter>
10-
<Routes>
11-
<Route path="/" element={<Landing />} />
12-
<Route path="/game" element={<Game />} />
13-
</Routes>
14-
</BrowserRouter>
37+
<div className="h-screen bg-slate-950">
38+
<BrowserRouter>
39+
<Routes>
40+
<Route path="/" element={<Landing />} />
41+
<Route path="/login" element={isAuthenticated ? <Game /> : <Login />} />
42+
<Route path="/game" element={isAuthenticated ? <Game /> : <Login />} />
43+
</Routes>
44+
</BrowserRouter>
1545
</div>
16-
)
46+
);
1747
}
1848

19-
export default App
49+
export default App;
558 Bytes
Loading
796 Bytes
Loading
950 Bytes
Loading

0 commit comments

Comments
 (0)