🐛 fix(matrix-web): handle Matrix SSO loginToken callback

Add loginWithLoginToken function to exchange Matrix SSO loginToken for credentials.
The app layout now detects the loginToken URL parameter and completes the SSO flow.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-02-01 15:02:47 +01:00
parent 9e9db590dc
commit dc0d425f61
53 changed files with 1550 additions and 230 deletions

View file

@ -4,9 +4,16 @@ import { CommandRouterService } from './command-router.service';
import { HandlersModule } from '../handlers/handlers.module';
import { OrchestrationModule } from '../orchestration/orchestration.module';
import { VoiceModule } from '../voice/voice.module';
import { SessionModule, CreditModule } from '@manacore/bot-services';
@Module({
imports: [forwardRef(() => HandlersModule), forwardRef(() => OrchestrationModule), VoiceModule],
imports: [
forwardRef(() => HandlersModule),
forwardRef(() => OrchestrationModule),
VoiceModule,
SessionModule.forRoot(),
CreditModule.forRoot(),
],
providers: [MatrixService, CommandRouterService],
exports: [MatrixService, CommandRouterService],
})

View file

@ -67,6 +67,18 @@ export class CommandRouterService {
description: 'Show help',
},
// Auth Commands
{
patterns: ['!login'],
handler: (ctx, args) => this.helpHandler.handleLogin(ctx, args),
description: 'Login with email and password',
},
{
patterns: ['!logout'],
handler: (ctx) => this.helpHandler.handleLogout(ctx),
description: 'Logout',
},
// AI Commands
{
patterns: ['!models', '!modelle'],

View file

@ -4,6 +4,7 @@ import { BaseMatrixService, MatrixBotConfig, MatrixRoomEvent } from '@manacore/m
import { CommandRouterService, CommandContext } from './command-router.service';
import { VoiceService, VoiceServiceError } from '../voice/voice.service';
import { VoiceFormatterService } from '../voice/voice-formatter.service';
import { SessionService, CreditService } from '@manacore/bot-services';
import { HELP_TEXT, WELCOME_TEXT, BOT_INTRODUCTION } from '../config/configuration';
@Injectable()
@ -16,7 +17,9 @@ export class MatrixService extends BaseMatrixService {
private commandRouter: CommandRouterService,
@Inject(forwardRef(() => VoiceService))
private voiceService: VoiceService,
private voiceFormatter: VoiceFormatterService
private voiceFormatter: VoiceFormatterService,
private sessionService: SessionService,
private creditService: CreditService
) {
super(configService);
this.voiceEnabled = configService.get('voice.enabled') !== false;

View file

@ -37,6 +37,11 @@ export default () => ({
// Help text for the unified bot
export const HELP_TEXT = `**🤖 Mana - Dein Assistent**
**👤 Account**
\`!login email passwort\` - Anmelden
\`!logout\` - Abmelden
\`!status\` - Account & Bot Status
**AI & Chat**
Schreib einfach eine Nachricht - ich antworte!
\`!model [name]\` - KI-Modell wechseln

View file

@ -7,9 +7,10 @@ import { HelpHandler } from './help.handler';
import { VoiceHandler } from './voice.handler';
import { BotModule } from '../bot/bot.module';
import { VoiceModule } from '../voice/voice.module';
import { SessionModule, CreditModule } from '@manacore/bot-services';
@Module({
imports: [forwardRef(() => BotModule), VoiceModule],
imports: [forwardRef(() => BotModule), VoiceModule, SessionModule.forRoot(), CreditModule.forRoot()],
providers: [AiHandler, TodoHandler, CalendarHandler, ClockHandler, HelpHandler, VoiceHandler],
exports: [AiHandler, TodoHandler, CalendarHandler, ClockHandler, HelpHandler, VoiceHandler],
})

View file

@ -1,5 +1,5 @@
import { Injectable } from '@nestjs/common';
import { AiService, TodoService } from '@manacore/bot-services';
import { AiService, TodoService, SessionService, CreditService } from '@manacore/bot-services';
import { CommandContext } from '../bot/command-router.service';
import { VoiceService } from '../voice/voice.service';
import { HELP_TEXT } from '../config/configuration';
@ -9,14 +9,61 @@ export class HelpHandler {
constructor(
private aiService: AiService,
private todoService: TodoService,
private voiceService: VoiceService
private voiceService: VoiceService,
private sessionService: SessionService,
private creditService: CreditService
) {}
async showHelp(ctx: CommandContext): Promise<string> {
return HELP_TEXT;
}
async handleLogin(ctx: CommandContext, args: string): Promise<string> {
const parts = args.split(' ');
if (parts.length < 2 || !parts[0] || !parts[1]) {
return 'Verwendung: `!login email passwort`';
}
const [email, password] = parts;
const result = await this.sessionService.login(ctx.userId, email, password);
if (result.success) {
const token = this.sessionService.getToken(ctx.userId);
if (token) {
const balance = await this.creditService.getBalance(token);
return `✅ Erfolgreich angemeldet als **${email}**\n⚡ Credits: ${balance.balance.toFixed(2)}`;
}
return `✅ Erfolgreich angemeldet als **${email}**`;
}
return `❌ Anmeldung fehlgeschlagen: ${result.error}`;
}
async handleLogout(ctx: CommandContext): Promise<string> {
this.sessionService.logout(ctx.userId);
return '👋 Erfolgreich abgemeldet.';
}
async showStatus(ctx: CommandContext): Promise<string> {
// Auth-Status zuerst
const loggedIn = this.sessionService.isLoggedIn(ctx.userId);
const session = this.sessionService.getSession(ctx.userId);
const token = this.sessionService.getToken(ctx.userId);
let authSection = '';
if (loggedIn && session && token) {
const balance = await this.creditService.getBalance(token);
authSection = `**👤 Account**
Angemeldet als: ${session.email}
Credits: ${balance.balance.toFixed(2)}
`;
} else {
authSection = `**👤 Account**
Nicht angemeldet
Nutze \`!login email passwort\`
`;
}
// Check services in parallel
const [aiConnected, todoStats, voiceHealth] = await Promise.all([
this.aiService.checkConnection(),
@ -36,7 +83,7 @@ export class HelpHandler {
return `**📊 Status**
**AI/Ollama**
${authSection}**AI/Ollama**
Verbindung: ${aiStatus}
Modell: \`${currentModel}\`