🐛 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

@ -148,6 +148,58 @@ export async function checkHomeserver(
}
}
/**
* Login with a Matrix SSO login token (for SSO/OAuth callback)
* This exchanges the loginToken from SSO redirect for proper credentials
*/
export async function loginWithLoginToken(
homeserver: string,
loginToken: string
): Promise<LoginResult> {
// Load polyfills first
await import('./polyfills');
const { createClient } = await import('matrix-js-sdk');
// Normalize homeserver URL
let baseUrl = homeserver.trim();
if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) {
baseUrl = `https://${baseUrl}`;
}
// Remove trailing slash
baseUrl = baseUrl.replace(/\/$/, '');
const tempClient = createClient({ baseUrl });
try {
const response = await tempClient.login('m.login.token', {
token: loginToken,
initial_device_display_name: 'Manalink',
});
return {
success: true,
credentials: {
homeserver: baseUrl,
accessToken: response.access_token,
userId: response.user_id,
deviceId: response.device_id,
},
};
} catch (err) {
const message = err instanceof Error ? err.message : 'Login failed';
// Provide more helpful error messages
if (message.includes('M_UNKNOWN_TOKEN') || message.includes('M_FORBIDDEN')) {
return { success: false, error: 'Login token expired or invalid. Please try again.' };
}
if (message.includes('Failed to fetch') || message.includes('NetworkError')) {
return { success: false, error: 'Could not connect to homeserver' };
}
return { success: false, error: message };
}
}
/**
* Register a new account (if registration is open)
*/

View file

@ -3,6 +3,7 @@ export { matrixStore } from './store.svelte';
export {
loginWithPassword,
loginWithToken,
loginWithLoginToken,
discoverHomeserver,
checkHomeserver,
register,

View file

@ -1,5 +1,5 @@
<script lang="ts">
import { matrixStore } from '$lib/matrix';
import { matrixStore, loginWithLoginToken } from '$lib/matrix';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { onMount, onDestroy } from 'svelte';
@ -143,7 +143,45 @@
return;
}
// Try to initialize Matrix
// Check for SSO loginToken in URL (Matrix SSO callback)
const urlParams = new URLSearchParams(window.location.search);
const loginToken = urlParams.get('loginToken');
if (loginToken) {
console.log('Found loginToken in URL, exchanging for credentials...');
// Exchange loginToken for Matrix credentials
const result = await loginWithLoginToken('matrix.mana.how', loginToken);
if (result.success && result.credentials) {
console.log('SSO login successful, initializing Matrix client...');
// Remove loginToken from URL to prevent re-processing on refresh
const cleanUrl = window.location.pathname;
window.history.replaceState({}, '', cleanUrl);
// Initialize with the new credentials
const initialized = await matrixStore.initialize(result.credentials);
if (!initialized) {
initError = matrixStore.error || 'Failed to initialize Matrix client';
}
loading = false;
return;
} else {
// SSO token exchange failed
initError = result.error || 'SSO login failed';
loading = false;
// Clear the URL and redirect to login
window.history.replaceState({}, '', '/login');
goto('/login');
return;
}
}
// Try to initialize Matrix with stored credentials
const success = await matrixStore.initialize();
if (!success) {