-
-
-
-
-
Passwort eingeben
+
+ Lehrerkonten werden nach der Registrierung von einem Administrator geprüft und freigeschaltet.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Generiertes Passwort – unbedingt speichern:
+
+
+
+
+
+
+
+
+
+
+
+
Gib das generierte Passwort nochmal ein, um sicherzustellen, dass du es dir gespeichert hast.
+
+
+
+
+
+
+
-
Registrierung erfolgreich! Wir haben dir eine Bestätigungs-Mail geschickt. Bitte prüfe dein Postfach und klicke auf den Link.
-
@@ -365,21 +385,111 @@ async function doLogin(e) {
window.location.href = '/app';
}
-async function doRegister(e) {
- e.preventDefault(); clearErr('reg-err');
+let generatedPasswordUsed = false;
+let lastGeneratedPw = '';
+
+const WEAK_PW = new Set([
+ 'passwort','password','passwort1','password1','passwort123','password123',
+ '123456','1234567','12345678','123456789','1234567890','qwerty','qwertz',
+ 'abc123','iloveyou','admin','admin123','letmein','welcome','111111',
+ '000000','123123','hallo','hallo123','test','test1234','schueler','schule',
+ 'klassenportal','ifbschule','ifb',
+]);
+
+function onPassInput(val) {
+ generatedPasswordUsed = false;
+ document.getElementById('gen-display').style.display = 'none';
+ checkStrength(val);
+}
+
+function useGeneratedPassword() {
+ const chars = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#$%&*';
+ const arr = new Uint8Array(16);
+ crypto.getRandomValues(arr);
+ const pw = Array.from(arr, b => chars[b % chars.length]).join('');
+ lastGeneratedPw = pw;
+ generatedPasswordUsed = true;
+ const input = document.getElementById('r-pass');
+ input.value = pw;
+ input.type = 'text';
+ checkStrength(pw);
+ document.getElementById('gen-pw-text').textContent = pw;
+ document.getElementById('gen-display').style.display = '';
+ document.getElementById('copy-btn').textContent = 'Kopieren';
+}
+
+function copyGenPw() {
+ navigator.clipboard.writeText(lastGeneratedPw).catch(() => {
+ const t = document.createElement('textarea');
+ t.value = lastGeneratedPw; document.body.appendChild(t); t.select();
+ document.execCommand('copy'); document.body.removeChild(t);
+ });
+ const btn = document.getElementById('copy-btn');
+ btn.textContent = '✓ Kopiert';
+ setTimeout(() => { btn.textContent = 'Kopieren'; }, 2000);
+}
+
+function validateRegForm() {
+ const email = document.getElementById('r-email').value.trim().toLowerCase();
+ const pw = document.getElementById('r-pass').value;
+ clearErr('reg-err');
+ if (!email) { showErr('reg-err', 'E-Mail-Adresse erforderlich'); return false; }
+ if (!pw) { showErr('reg-err', 'Passwort erforderlich'); return false; }
+ if (pw.length < 8) { showErr('reg-err', 'Passwort muss mindestens 8 Zeichen lang sein'); return false; }
+ const uname = email.split('@')[0].toLowerCase();
+ if (pw.toLowerCase() === uname) { showErr('reg-err', 'Passwort darf nicht dem Benutzernamen entsprechen'); return false; }
+ if (WEAK_PW.has(pw.toLowerCase())) { showErr('reg-err', 'Dieses Passwort ist zu leicht zu erraten'); return false; }
+ if (selectedRole === 'teacher' && !document.getElementById('r-subject').value) {
+ showErr('reg-err', 'Bitte ein Lehrfach auswählen'); return false;
+ }
+ return true;
+}
+
+function regWeiter() {
+ if (!validateRegForm()) return;
+ if (generatedPasswordUsed) {
+ document.getElementById('reg-step-1').style.display = 'none';
+ document.getElementById('reg-step-2').style.display = '';
+ document.getElementById('r-pass-confirm').value = '';
+ document.getElementById('r-pass-confirm').focus();
+ clearErr('reg-confirm-err');
+ } else {
+ doRegister();
+ }
+}
+
+function regBack() {
+ document.getElementById('reg-step-2').style.display = 'none';
+ document.getElementById('reg-step-1').style.display = '';
+ clearErr('reg-confirm-err');
+}
+
+async function doRegister() {
+ if (generatedPasswordUsed) {
+ const confirm = document.getElementById('r-pass-confirm').value;
+ if (confirm !== document.getElementById('r-pass').value) {
+ showErr('reg-confirm-err', 'Passwörter stimmen nicht überein'); return;
+ }
+ clearErr('reg-confirm-err');
+ }
+ const btn = document.getElementById(generatedPasswordUsed ? 'reg-confirm-btn' : 'reg-weiter-btn');
+ btn.disabled = true; btn.textContent = 'Wird gesendet…';
const body = {
- email: document.getElementById('r-email').value,
+ email: document.getElementById('r-email').value.trim().toLowerCase(),
password: document.getElementById('r-pass').value,
role: selectedRole,
};
if (selectedRole === 'teacher') body.subject = document.getElementById('r-subject').value;
- const r = await fetch('/api/register', { method:'POST', headers:{'Content-Type':'application/json'},
- body: JSON.stringify(body) });
+ const r = await fetch('/api/register', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(body) });
const d = await r.json();
- if (!r.ok) { showErr('reg-err', d.error); return; }
+ if (!r.ok) {
+ showErr(generatedPasswordUsed ? 'reg-confirm-err' : 'reg-err', d.error);
+ btn.disabled = false;
+ btn.textContent = generatedPasswordUsed ? 'Account erstellen' : 'Weiter';
+ return;
+ }
document.getElementById('reg-ok').classList.add('show');
- document.getElementById('reg-btn').disabled = true;
- document.getElementById('reg-btn').textContent = 'Registriert';
+ btn.disabled = true; btn.textContent = 'Registriert ✓';
}
function showForgot() {
diff --git a/src/routes.js b/src/routes.js
index 93eb824..4f514cb 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -18,6 +18,14 @@ const RESET_TTL_MS = 60 * 60 * 1000;
const DUMMY_PASSWORD_HASH = bcrypt.hashSync('dummy-placeholder-value', 12);
+const WEAK_PASSWORDS = new Set([
+ 'passwort','password','passwort1','password1','passwort123','password123',
+ '123456','1234567','12345678','123456789','1234567890','qwerty','qwertz',
+ 'abc123','iloveyou','admin','admin123','letmein','welcome','111111',
+ '000000','123123','hallo','hallo123','test','test1234','schueler','schule',
+ 'klassenportal','ifbschule','ifb',
+]);
+
function generateRecoveryCodes() {
const plain = Array.from({ length: 8 }, () => {
const h = crypto.randomBytes(5).toString('hex');
@@ -108,6 +116,9 @@ router.post('/register', registerLimiter, async (req, res) => {
if (!IFB_EMAIL_RE.test(email)) return res.status(403).json({ error: 'Ungültige E-Mail-Adresse' });
if (password.length < 8) return res.status(400).json({ error: 'Passwort zu kurz (min. 8 Zeichen)' });
const username = email.split('@')[0].toLowerCase();
+ const pwLower = password.toLowerCase();
+ if (pwLower === username) return res.status(400).json({ error: 'Passwort darf nicht dem Benutzernamen entsprechen' });
+ if (WEAK_PASSWORDS.has(pwLower)) return res.status(400).json({ error: 'Dieses Passwort ist zu leicht zu erraten. Bitte wähle ein sichereres.' });
const safeRole = (role === 'teacher') ? 'teacher' : 'student';
const initialStatus = safeRole === 'teacher' ? 'pending' : 'active';