feat: add GitHub OAuth2 login and protect product mutations with JWT
- add /auth/login and /auth/callback (rewritten to pages/api/auth/*) for the GitHub Authorization Code flow, with a state cookie against login CSRF - issue a 1h JWT after the code exchange and verify it (HS256) in the Apollo context; createProduct, updateProduct and deleteProduct require it - set products.created_by from the logged-in GitHub user (upsert into users) - preset the Authorization header in the embedded Sandbox - add jsonwebtoken, drop unused cors, rename package to week-7
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
// Langkah 3-5 OAuth2: tukar code dengan access token, ambil profil, terbitkan JWT sendiri.
|
||||
module.exports = async function handler(req, res) {
|
||||
const { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, JWT_SECRET } = process.env;
|
||||
if (!GITHUB_CLIENT_ID || !GITHUB_CLIENT_SECRET || !JWT_SECRET) {
|
||||
res.status(500).json({ error: 'GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET / JWT_SECRET belum diatur di environment' });
|
||||
return;
|
||||
}
|
||||
|
||||
const { code, state } = req.query;
|
||||
|
||||
// state sekali pakai: hapus cookie apa pun hasilnya.
|
||||
const expectedState = req.cookies.oauth_state;
|
||||
res.setHeader('Set-Cookie', 'oauth_state=; HttpOnly; SameSite=Lax; Path=/; Max-Age=0');
|
||||
|
||||
if (!code) {
|
||||
res.status(400).json({ error: 'Parameter code tidak ada' });
|
||||
return;
|
||||
}
|
||||
if (!state || !expectedState || state !== expectedState) {
|
||||
res.status(400).json({ error: 'State tidak valid, ulangi login lewat /auth/login' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const tokenRes = await fetch('https://github.com/login/oauth/access_token', {
|
||||
method: 'POST',
|
||||
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
client_id: GITHUB_CLIENT_ID,
|
||||
client_secret: GITHUB_CLIENT_SECRET,
|
||||
code,
|
||||
}),
|
||||
});
|
||||
const { access_token, error_description } = await tokenRes.json();
|
||||
if (!access_token) {
|
||||
res.status(401).json({ error: error_description || 'Gagal menukar code dengan access token' });
|
||||
return;
|
||||
}
|
||||
|
||||
const userRes = await fetch('https://api.github.com/user', {
|
||||
headers: { Authorization: `Bearer ${access_token}`, Accept: 'application/json' },
|
||||
});
|
||||
if (!userRes.ok) {
|
||||
res.status(502).json({ error: 'Gagal mengambil profil dari GitHub' });
|
||||
return;
|
||||
}
|
||||
const githubUser = await userRes.json();
|
||||
|
||||
const token = jwt.sign({ username: githubUser.login }, JWT_SECRET, { expiresIn: '1h' });
|
||||
res.json({ token });
|
||||
} catch (err) {
|
||||
console.error('[OAuth error] /auth/callback:', err);
|
||||
res.status(500).json({ error: 'Terjadi kesalahan pada server, silakan coba lagi' });
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user