🔒 refactor(bots): remove !login command and enforce OIDC-only auth

- Remove !login and !logout commands from all 16+ Matrix bots
- Remove login/logout references from all help/welcome messages
- Disable password login in Synapse (password_config.enabled: false)
- System is now OIDC-only via Mana Core authentication

Users must authenticate via "Sign in with Mana Core" in Element.
Existing bot access tokens remain valid.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-02-14 11:26:58 +01:00
parent c698318c91
commit d81b8aebf2
30 changed files with 158 additions and 786 deletions

View file

@ -15,6 +15,7 @@ import {
I18nService,
Language,
LANGUAGE_NAMES,
LOGIN_MESSAGES,
} from '@manacore/bot-services';
import { HELP_TEXT, WELCOME_TEXT } from '../config/configuration';
@ -168,14 +169,6 @@ export class MatrixService extends BaseMatrixService {
await this.sendReply(roomId, event, HELP_TEXT);
break;
case 'login':
await this.handleLogin(roomId, event, userId, args);
break;
case 'logout':
await this.handleLogout(roomId, event, userId);
break;
case 'timer':
await this.handleTimerCommand(roomId, event, userId, args);
break;
@ -263,11 +256,8 @@ export class MatrixService extends BaseMatrixService {
const label = args.replace(/[\d\s]*[hms]+/gi, '').trim() || null;
try {
const token = await this.getToken(userId);
if (!token) {
await this.sendReply(roomId, event, 'Keine Authentifizierung. Bitte zuerst `!login`.');
return;
}
const token = await this.requireLogin(roomId, event, userId);
if (!token) return;
// Create and start timer
const timer = await this.clockService.createTimer(durationSeconds, label, token);
@ -287,11 +277,8 @@ export class MatrixService extends BaseMatrixService {
private async handleStopCommand(roomId: string, event: MatrixRoomEvent, userId: string) {
try {
const token = await this.getToken(userId);
if (!token) {
await this.sendReply(roomId, event, 'Keine Authentifizierung.');
return;
}
const token = await this.requireLogin(roomId, event, userId);
if (!token) return;
const runningTimer = await this.clockService.getRunningTimer(token);
if (!runningTimer) {
@ -324,11 +311,8 @@ export class MatrixService extends BaseMatrixService {
private async handleResumeCommand(roomId: string, event: MatrixRoomEvent, userId: string) {
try {
const token = await this.getToken(userId);
if (!token) {
await this.sendReply(roomId, event, 'Keine Authentifizierung.');
return;
}
const token = await this.requireLogin(roomId, event, userId);
if (!token) return;
const pausedTimer = await this.clockService.getRunningTimer(token);
if (!pausedTimer || pausedTimer.status !== 'paused') {
@ -348,11 +332,8 @@ export class MatrixService extends BaseMatrixService {
private async handleResetCommand(roomId: string, event: MatrixRoomEvent, userId: string) {
try {
const token = await this.getToken(userId);
if (!token) {
await this.sendReply(roomId, event, 'Keine Authentifizierung.');
return;
}
const token = await this.requireLogin(roomId, event, userId);
if (!token) return;
const activeTimer = await this.clockService.getRunningTimer(token);
if (!activeTimer) {
@ -368,37 +349,6 @@ export class MatrixService extends BaseMatrixService {
}
}
private async handleLogin(roomId: string, event: MatrixRoomEvent, userId: string, args: string) {
const parts = args.split(' ');
if (parts.length < 2 || !parts[0] || !parts[1]) {
await this.sendReply(roomId, event, 'Verwendung: `!login email passwort`');
return;
}
const [email, password] = parts;
const result = await this.sessionService.login(userId, email, password);
if (result.success) {
const token = await this.sessionService.getToken(userId);
if (token) {
const balance = await this.creditService.getBalance(token);
await this.sendReply(
roomId,
event,
`✅ Erfolgreich angemeldet als **${email}**\n⚡ Credits: ${balance.balance.toFixed(2)}`
);
} else {
await this.sendReply(roomId, event, `✅ Erfolgreich angemeldet als **${email}**`);
}
} else {
await this.sendReply(roomId, event, `❌ Anmeldung fehlgeschlagen: ${result.error}`);
}
}
private async handleLogout(roomId: string, event: MatrixRoomEvent, userId: string) {
await this.sessionService.logout(userId);
await this.sendReply(roomId, event, '👋 Erfolgreich abgemeldet.');
}
private async handleStatusCommand(roomId: string, event: MatrixRoomEvent, userId: string) {
// Auth-Status zuerst
const loggedIn = await this.sessionService.isLoggedIn(userId);
@ -412,8 +362,7 @@ export class MatrixService extends BaseMatrixService {
response += `👤 Angemeldet als: ${session.email}\n`;
response += `⚡ Credits: ${balance.balance.toFixed(2)}\n\n`;
} else {
response += `❌ Nicht angemeldet\n`;
response += `Nutze \`!login email passwort\` zum Anmelden.\n\n`;
response += `⚠️ Nicht verbunden - bitte in Element neu einloggen via "Mit Mana Core anmelden"\n\n`;
}
// Timer-Status
@ -445,11 +394,8 @@ export class MatrixService extends BaseMatrixService {
private async handleTimersCommand(roomId: string, event: MatrixRoomEvent, userId: string) {
try {
const token = await this.getToken(userId);
if (!token) {
await this.sendReply(roomId, event, 'Keine Authentifizierung.');
return;
}
const token = await this.requireLogin(roomId, event, userId);
if (!token) return;
const timers = await this.clockService.getTimers(token);
if (timers.length === 0) {
@ -501,11 +447,8 @@ export class MatrixService extends BaseMatrixService {
const label = args.replace(/[\d:]+\s*(uhr\s*\d*)?/gi, '').trim() || null;
try {
const token = await this.getToken(userId);
if (!token) {
await this.sendReply(roomId, event, 'Keine Authentifizierung.');
return;
}
const token = await this.requireLogin(roomId, event, userId);
if (!token) return;
await this.clockService.createAlarm(time, label, token);
let response = `**Alarm gestellt!**\n\nZeit: ${time.substring(0, 5)} Uhr`;
@ -520,11 +463,8 @@ export class MatrixService extends BaseMatrixService {
private async handleAlarmsCommand(roomId: string, event: MatrixRoomEvent, userId: string) {
try {
const token = await this.getToken(userId);
if (!token) {
await this.sendReply(roomId, event, 'Keine Authentifizierung.');
return;
}
const token = await this.requireLogin(roomId, event, userId);
if (!token) return;
const alarms = await this.clockService.getAlarms(token);
if (alarms.length === 0) {
@ -603,11 +543,8 @@ export class MatrixService extends BaseMatrixService {
return;
}
const token = await this.getToken(userId);
if (!token) {
await this.sendReply(roomId, event, 'Keine Authentifizierung.');
return;
}
const token = await this.requireLogin(roomId, event, userId);
if (!token) return;
const best = results[0];
await this.clockService.addWorldClock(best.timezone, best.city, token);
@ -620,11 +557,8 @@ export class MatrixService extends BaseMatrixService {
private async handleWorldClocksCommand(roomId: string, event: MatrixRoomEvent, userId: string) {
try {
const token = await this.getToken(userId);
if (!token) {
await this.sendReply(roomId, event, 'Keine Authentifizierung.');
return;
}
const token = await this.requireLogin(roomId, event, userId);
if (!token) return;
const clocks = await this.clockService.getWorldClocks(token);
if (clocks.length === 0) {
@ -711,6 +645,22 @@ export class MatrixService extends BaseMatrixService {
return this.demoToken || null;
}
/**
* Require login - returns token or sends login prompt and returns null
*/
private async requireLogin(
roomId: string,
event: MatrixRoomEvent,
userId: string
): Promise<string | null> {
const token = await this.getToken(userId);
if (!token) {
await this.sendReply(roomId, event, LOGIN_MESSAGES.clock);
return null;
}
return token;
}
private async handleLanguage(
roomId: string,
event: MatrixRoomEvent,

View file

@ -16,9 +16,7 @@ export default () => ({
export const HELP_TEXT = `**Clock Bot - Zeiterfassung per Chat**
**Account:**
- \`!login email passwort\` - Anmelden
- \`!logout\` - Abmelden
**Status:**
- \`!status\` - Account & Timer Status
**Timer (Stoppuhr):**
@ -49,16 +47,14 @@ Sende eine Sprachnotiz wie "Timer 25 Minuten" oder "Wecker um 7 Uhr"
- "stop" - Timer stoppen
- "status" - Status anzeigen`;
export const WELCOME_TEXT = `**Clock Bot - Zeiterfassung**
export const WELCOME_TEXT = `🕐 **Clock Bot - Zeiterfassung**
Starte mit:
- \`!timer 25m\` - 25-Minuten Timer
**Schnellstart:**
- \`!timer 25m\` - 25-Minuten Timer starten
- \`!alarm 07:30\` - Wecker stellen
- \`!zeit\` - Aktuelle Zeit
- \`!status\` - Status anzeigen
Oder sende eine Sprachnotiz!
\`!help\` fur alle Befehle.`;
\`!help\` für alle Befehle.`;
// Natural language patterns for time parsing
export const TIME_PATTERNS = {