@@ -24,3 +24,4 @@ Gestion de friendship dans POSTGRESQL:
|
|||||||
Ressource:
|
Ressource:
|
||||||
https://www.postgresql.org/docs/
|
https://www.postgresql.org/docs/
|
||||||
https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
|
https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
|
||||||
|
https://docs.github.com/fr/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app
|
||||||
+34
-2
@@ -59,6 +59,16 @@ async function createTables()
|
|||||||
FOREIGN KEY (id_user1) REFERENCES users(id) ON DELETE CASCADE,
|
FOREIGN KEY (id_user1) REFERENCES users(id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (id_user2) REFERENCES users(id) ON DELETE CASCADE
|
FOREIGN KEY (id_user2) REFERENCES users(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS oauth_clients (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
provider VARCHAR(50) NOT NULL,
|
||||||
|
client_id VARCHAR(200) NOT NULL,
|
||||||
|
client_secret TEXT,
|
||||||
|
redirect_uri VARCHAR(255),
|
||||||
|
created_at TIMESTAMP DEFAULT NOW(),
|
||||||
|
UNIQUE(provider, client_id)
|
||||||
|
);
|
||||||
`);
|
`);
|
||||||
console.log('Tables created!');
|
console.log('Tables created!');
|
||||||
}
|
}
|
||||||
@@ -73,9 +83,31 @@ async function query(text, params)
|
|||||||
return (pool.query(text, params));
|
return (pool.query(text, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports =
|
async function ensureOauthClient(provider, client_id, client_secret, redirect_uri)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const res = await pool.query(
|
||||||
|
`SELECT id FROM oauth_clients WHERE provider = $1 AND client_id = $2`, [provider, client_id]
|
||||||
|
);
|
||||||
|
if (res.rows.length > 0)
|
||||||
|
return res.rows[0];
|
||||||
|
const insert = await pool.query(
|
||||||
|
`INSERT INTO oauth_clients (provider, client_id, client_secret, redirect_uri) VALUES ($1, $2, $3, $4) RETURNING id`,
|
||||||
|
[provider, client_id, client_secret, redirect_uri]
|
||||||
|
);
|
||||||
|
return insert.rows[0];
|
||||||
|
}
|
||||||
|
catch (err)
|
||||||
|
{
|
||||||
|
console.error('Error ensuring oauth client:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
waitForDb,
|
waitForDb,
|
||||||
createTables,
|
createTables,
|
||||||
query
|
query,
|
||||||
|
ensureOauthClient
|
||||||
};
|
};
|
||||||
@@ -4,7 +4,7 @@ const cors = require('cors');
|
|||||||
const {Server} = require('socket.io');
|
const {Server} = require('socket.io');
|
||||||
const authRouter = require('./routes/auth');
|
const authRouter = require('./routes/auth');
|
||||||
const chatRouter = require('./routes/global_chat');
|
const chatRouter = require('./routes/global_chat');
|
||||||
const {waitForDb, createTables} = require('./db');
|
const {waitForDb, createTables, ensureOauthClient} = require('./db');
|
||||||
const setupSocketIO = require('./services/socket');
|
const setupSocketIO = require('./services/socket');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
@@ -28,6 +28,13 @@ async function startServer()
|
|||||||
await waitForDb();
|
await waitForDb();
|
||||||
await createTables();
|
await createTables();
|
||||||
|
|
||||||
|
// Ensure GitHub OAuth client is registered in DB
|
||||||
|
try {
|
||||||
|
await ensureOauthClient('github', process.env.GITHUB_CLIENT_ID, process.env.GITHUB_CLIENT_SECRET, process.env.GITHUB_CALLBACK_URL || process.env.GITHUB_REDIRECT_URI);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('OAuth client might already exist or failed to register:', e.message);
|
||||||
|
}
|
||||||
|
|
||||||
app.use('/api/auth', authRouter);
|
app.use('/api/auth', authRouter);
|
||||||
app.use('/api/global_chat', chatRouter);
|
app.use('/api/global_chat', chatRouter);
|
||||||
app.get('/api', (req, res) => res.send('Backend running'));
|
app.get('/api', (req, res) => res.send('Backend running'));
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ const express = require('express');
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const authService = require('../services/auth');
|
const authService = require('../services/auth');
|
||||||
const fetch = require('node-fetch');
|
const fetch = require('node-fetch');
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const {query} = require('../db');
|
||||||
|
|
||||||
router.post('/register', async(req, res) =>
|
router.post('/register', async(req, res) =>
|
||||||
{
|
{
|
||||||
@@ -24,10 +27,61 @@ router.post('/login', async(req, res) =>
|
|||||||
router.get('/github', (req, res) => {
|
router.get('/github', (req, res) => {
|
||||||
const githubAuthUrl = `https://github.com/login/oauth/authorize?` +
|
const githubAuthUrl = `https://github.com/login/oauth/authorize?` +
|
||||||
`client_id=${process.env.GITHUB_CLIENT_ID}&` +
|
`client_id=${process.env.GITHUB_CLIENT_ID}&` +
|
||||||
`redirect_uri=${encodeURIComponent(process.env.GITHUB_REDIRECT_URI)}&` +
|
`redirect_uri=${encodeURIComponent(process.env.GITHUB_CALLBACK_URL || process.env.GITHUB_REDIRECT_URI)}&` +
|
||||||
`scope=user:email`;
|
`scope=user:email`;
|
||||||
|
|
||||||
res.redirect(githubAuthUrl);
|
res.redirect(githubAuthUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/github/callback', async (req, res) => {
|
||||||
|
const code = req.query.code;
|
||||||
|
if (!code) {
|
||||||
|
return res.status(400).send('Missing code');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
client_id: process.env.GITHUB_CLIENT_ID,
|
||||||
|
client_secret: process.env.GITHUB_CLIENT_SECRET,
|
||||||
|
code: code
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const tokenData = await tokenResponse.json();
|
||||||
|
const accessToken = tokenData.access_token;
|
||||||
|
if (!accessToken) throw new Error('No access token');
|
||||||
|
|
||||||
|
|
||||||
|
const userResponse = await fetch('https://api.github.com/user', {
|
||||||
|
headers: { 'Authorization': `Bearer ${accessToken}`, 'User-Agent': 'Transcendence' }
|
||||||
|
});
|
||||||
|
const ghUser = await userResponse.json();
|
||||||
|
const ghUsername = ghUser.login || `github_${ghUser.id}`;
|
||||||
|
|
||||||
|
|
||||||
|
let result = await query(`SELECT id FROM users WHERE username = $1`, [ghUsername]);
|
||||||
|
let userId;
|
||||||
|
if (result.rows.length > 0) {
|
||||||
|
userId = result.rows[0].id;
|
||||||
|
} else {
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const randomPwd = crypto.randomBytes(16).toString('hex');
|
||||||
|
const passwordHash = await bcrypt.hash(randomPwd, 10);
|
||||||
|
await query(`INSERT INTO users (username, password_hash) VALUES ($1, $2)`, [ghUsername, passwordHash]);
|
||||||
|
const inserted = await query(`SELECT id FROM users WHERE username = $1`, [ghUsername]);
|
||||||
|
userId = inserted.rows[0].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue JWT
|
||||||
|
const token = jwt.sign({ userId: userId, username: ghUsername }, process.env.JWT_SECRET, { expiresIn: '1h' });
|
||||||
|
|
||||||
|
// Send token to opener window and close popup
|
||||||
|
res.send(`<!doctype html><html><body><script>window.opener && window.opener.postMessage({token: '${token}'}, '*'); window.close();</script></body></html>`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send('GitHub OAuth error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
@@ -40,12 +40,25 @@ export class LoginWindow extends fenetre {
|
|||||||
this.githubBtn.style.backgroundColor = "#24292e";
|
this.githubBtn.style.backgroundColor = "#24292e";
|
||||||
this.githubBtn.style.color = "white";
|
this.githubBtn.style.color = "white";
|
||||||
this.githubBtn.onclick = () => {
|
this.githubBtn.onclick = () => {
|
||||||
window.location.href = "/api/auth/github";
|
// Ouvre le OAuth GitHub dans une popup et reçoit le token via postMessage
|
||||||
|
const w = 600;
|
||||||
|
const h = 700;
|
||||||
|
const left = (screen.width - w) / 2;
|
||||||
|
const top = (screen.height - h) / 2;
|
||||||
|
const popup = window.open('/api/auth/github', 'githubOAuth', `width=${w},height=${h},left=${left},top=${top}`);
|
||||||
|
const listener = (ev) => {
|
||||||
|
if (ev.data && ev.data.token) {
|
||||||
|
localStorage.setItem('auth_token', ev.data.token);
|
||||||
|
this.message.innerText = 'Connexion GitHub réussie ! Bienvenue.';
|
||||||
|
this.message.style.color = '#3cff01';
|
||||||
|
window.removeEventListener('message', listener);
|
||||||
|
if (popup) popup.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('message', listener, {once: true});
|
||||||
};
|
};
|
||||||
this.body.appendChild(this.githubBtn);
|
this.body.appendChild(this.githubBtn);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.checkIfAlreadyLoggedIn(); //verifie si l'utilisateur est connecté au démarrage
|
this.checkIfAlreadyLoggedIn(); //verifie si l'utilisateur est connecté au démarrage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user