Changeset 247988e in Klonkt for src/routes
- Timestamp:
- 06/19/2026 09:55:26 AM (3 months ago)
- Branches:
- main
- Children:
- f33b24a
- Parents:
- 535f955
- Location:
- src/routes
- Files:
-
- 2 edited
-
account.js (modified) (4 diffs)
-
auth.js (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/account.js
r535f955 r247988e 24 24 import { renderPage } from '../middleware/render.js'; 25 25 import { requireAuth } from '../middleware/auth.js'; 26 import { googleConfigured } from '../config/google.js'; 26 27 27 28 const __dirname = path.dirname(fileURLToPath(import.meta.url)); … … 58 59 router.get('/', requireAuth, (req, res) => { 59 60 const account = db.prepare(` 60 SELECT id, username, email, role, bio, avatar_url, created_at, password_hash 61 SELECT id, username, email, role, bio, avatar_url, created_at, password_hash, google_sub 61 62 FROM users WHERE id = ? 62 63 `).get(req.session.user.id); 63 64 const hasPassword = !!(account && account.password_hash && account.password_hash !== '!google-oauth'); 64 if (account) delete account.password_hash; // niet naar de view lekken 65 const googleLinked = !!(account && account.google_sub); 66 if (account) { delete account.password_hash; delete account.google_sub; } // niet naar de view lekken 65 67 66 68 renderPage(req, res, 'pages/account', { … … 69 71 account, 70 72 hasPassword, 73 googleLinked, 74 googleAvailable: googleConfigured(), 71 75 editableSite: ownedSite(req.session.user), 72 76 success: req.query.success || null, … … 144 148 }); 145 149 150 // Google-account ontkoppelen. Alleen toegestaan als er nog een wachtwoord is, 151 // anders zou je jezelf buitensluiten (geen login-methode meer over). 152 router.post('/google/unlink', requireAuth, (req, res) => { 153 const row = db.prepare('SELECT password_hash, google_sub FROM users WHERE id = ?').get(req.session.user.id); 154 if (!row || !row.google_sub) { 155 return res.redirect('/account?error=' + encodeURIComponent('Er is geen Google-account gekoppeld')); 156 } 157 if (!row.password_hash || row.password_hash === '!google-oauth') { 158 return res.redirect('/account?error=' + encodeURIComponent('Stel eerst een wachtwoord in — anders kun je niet meer inloggen.')); 159 } 160 db.prepare('UPDATE users SET google_sub = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(req.session.user.id); 161 res.redirect('/account?success=' + encodeURIComponent('Google-account ontkoppeld')); 162 }); 163 146 164 // ==================== UPLOAD AVATAR ==================== 147 165 router.post('/avatar', requireAuth, (req, res) => { -
src/routes/auth.js
r535f955 r247988e 6 6 import { renderPage } from '../middleware/render.js'; 7 7 import { loginLimiter, registerLimiter } from '../middleware/rate-limit.js'; 8 import { safeNext } from '../middleware/auth.js';8 import { safeNext, requireAuth } from '../middleware/auth.js'; 9 9 import { googleConfigured, authorizeUrl, exchangeCode, fetchUserinfo } from '../config/google.js'; 10 10 import { premiumUnlocked } from '../services/PatreonService.js'; … … 268 268 req.session.oauthState = state; 269 269 req.session.oauthNext = safeNext(req.query.next) || ''; 270 delete req.session.oauthLink; 271 res.redirect(authorizeUrl(state)); 272 }); 273 274 // Google KOPPELEN aan het huidige (ingelogde) account — bv. een beheerder die 275 // voortaan óók met Google wil inloggen. Vereist dat je al ingelogd bent (met 276 // wachtwoord); de koppeling slaat de google_sub op het eigen account op. 277 // Alleen googleConfigured() nodig (geen premium-gate — dit is geen fan-login). 278 router.get('/google/link', requireAuth, (req, res) => { 279 if (!googleConfigured()) { 280 return res.redirect('/account?error=' + encodeURIComponent('Google-login is op deze site niet ingesteld.')); 281 } 282 const state = crypto.randomBytes(16).toString('hex'); 283 req.session.oauthState = state; 284 req.session.oauthLink = true; // koppel-modus i.p.v. login-modus 270 285 res.redirect(authorizeUrl(state)); 271 286 }); … … 283 298 284 299 router.get('/google/callback', async (req, res) => { 285 const fail = (code) => res.redirect('/auth/login?gerr=' + code); 286 if (!fanLoginReady()) return fail('unavailable'); 300 const linking = !!req.session.oauthLink; 301 const failLogin = (code) => res.redirect('/auth/login?gerr=' + code); 302 const failLink = (msg) => res.redirect('/account?error=' + encodeURIComponent(msg)); 303 287 304 try { 288 305 const { code, state } = req.query; 289 if (!code || !state || state !== req.session.oauthState) return fail('session');290 const next = safeNext(req.session.oauthNext) || '';291 delete req.session.oauthState;292 delete req.session.oauthNext;306 if (!code || !state || state !== req.session.oauthState) { 307 delete req.session.oauthState; delete req.session.oauthLink; delete req.session.oauthNext; 308 return linking ? failLink('Google-koppeling afgebroken of sessie verlopen. Probeer opnieuw.') : failLogin('session'); 309 } 293 310 294 311 const tok = await exchangeCode(String(code)); 295 312 const info = await fetchUserinfo(tok.access_token); 296 313 const email = (info.email || '').trim().toLowerCase(); 297 if (!email || info.email_verified === false) return fail('email'); 314 315 // ── KOPPEL-MODUS: Google aan het huidige (ingelogde) account hangen ── 316 if (linking) { 317 delete req.session.oauthState; delete req.session.oauthLink; 318 if (!req.session.user) return failLogin('session'); 319 if (!info.sub) return failLink('Google gaf geen account-id terug. Probeer opnieuw.'); 320 if (info.email && info.email_verified === false) return failLink('Je Google-adres is niet geverifieerd.'); 321 // Dit Google-account mag niet al aan een ANDER account hangen. 322 const other = db.prepare('SELECT id FROM users WHERE google_sub = ? AND id != ?').get(info.sub, req.session.user.id); 323 if (other) return failLink('Dit Google-account is al aan een andere gebruiker gekoppeld.'); 324 db.prepare(` 325 UPDATE users SET google_sub = ?, avatar_url = COALESCE(avatar_url, ?), 326 updated_at = CURRENT_TIMESTAMP WHERE id = ? 327 `).run(info.sub, info.picture || null, req.session.user.id); 328 return res.redirect('/account?success=' + encodeURIComponent('Google-account gekoppeld — je kunt nu ook met Google inloggen.')); 329 } 330 331 // ── LOGIN-MODUS (luisteraars/fans + gekoppelde beheerder) ── 332 if (!fanLoginReady()) return failLogin('unavailable'); 333 const next = safeNext(req.session.oauthNext) || ''; 334 delete req.session.oauthState; delete req.session.oauthNext; 335 if (!email || info.email_verified === false) return failLogin('email'); 298 336 299 337 let user = db.prepare('SELECT * FROM users WHERE LOWER(email) = ?').get(email); 300 338 301 if (user) { 302 // Strikte scheiding: Google geeft nooit beheer. Hoort dit adres bij een 303 // beheerder, dan moet diegene met wachtwoord inloggen (geen Google-bypass). 304 if (user.role === 'god' || user.role === 'admin') { 305 // Eigen, duidelijke uitleg-pagina (zie auth-login.ejs) i.p.v. een kale melding. 306 return fail('admin'); 339 if (user && (user.role === 'god' || user.role === 'admin')) { 340 // Beheerder mag ALLEEN met Google in als 'ie z'n Google expliciet gekoppeld 341 // heeft (matchende google_sub). Anders blijft gelden: Google = nooit beheer. 342 if (!(user.google_sub && info.sub && user.google_sub === info.sub)) { 343 return failLogin('admin'); 307 344 } 345 // gekoppeld + match → doorgaan met de eigen (beheer)rol. 346 } else if (user) { 308 347 // Bestaande luisteraar: koppel google_sub/avatar als die ontbreken; weiger 309 348 // als al aan een ander Google-account gekoppeld. 310 if (user.google_sub && info.sub && user.google_sub !== info.sub) { 311 return fail('linked'); 312 } 349 if (user.google_sub && info.sub && user.google_sub !== info.sub) return failLogin('linked'); 313 350 db.prepare(` 314 351 UPDATE users SET google_sub = COALESCE(google_sub, ?), avatar_url = COALESCE(avatar_url, ?), … … 334 371 } catch (e) { 335 372 console.error('[auth/google/callback]', e.message); 336 fail('failed'); 373 delete req.session.oauthState; delete req.session.oauthLink; delete req.session.oauthNext; 374 return linking 375 ? res.redirect('/account?error=' + encodeURIComponent('Google koppelen mislukt — probeer opnieuw.')) 376 : failLogin('failed'); 337 377 } 338 378 });
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)