fix: resolve connectToRemote identity for federated users
Federated users (e.g. youruser@nova browsing orbit) could not add instances via Settings → Connections because connectToRemote assumed window.location.host was the home instance, producing a double-@ username like youruser@nova.ddns.net@orbit.ddns.net that failed server validation. Now derives trueHomeHost/bareUsername/trueHomeUserId from the user's actual homeInstance fields and branches the auth flow: login-only with bare username when targeting home, register with correct namespacing for third-party remotes. Also skips profile sync to home instance since it's the source of truth.
This commit is contained in:
@@ -163,18 +163,43 @@ export const useInstanceStore = create<InstanceState>((set, get) => ({
|
||||
set({ isLoading: true, error: null });
|
||||
|
||||
try {
|
||||
// Step 1: Verify password against HOME instance
|
||||
// Step 1: Verify password against the instance we're currently browsing
|
||||
const { valid } = await api.users.verifyPassword(password);
|
||||
if (!valid) {
|
||||
throw new Error('Incorrect password');
|
||||
}
|
||||
|
||||
// Step 2: Try register on remote, then fall back to login
|
||||
const homeInstance = window.location.host;
|
||||
// Step 2: Compute the user's true home identity
|
||||
// If we're a federated user (e.g. youruser@nova browsing orbit),
|
||||
// homeInstance points to the real home, not window.location.host.
|
||||
const trueHomeHost = currentUser.homeInstance ?? window.location.host;
|
||||
const bareUsername = currentUser.username.includes('@')
|
||||
? currentUser.username.split('@')[0]!
|
||||
: currentUser.username;
|
||||
const trueHomeUserId = currentUser.homeUserId ?? currentUser.id;
|
||||
const targetHost = new URL(origin).host;
|
||||
const targetIsHome = targetHost === trueHomeHost;
|
||||
|
||||
const tempClient = createApiClient(origin, () => null);
|
||||
|
||||
let response: AuthResponse | null = null;
|
||||
const finalUsername = `${currentUser.username}@${homeInstance}`;
|
||||
let finalUsername: string;
|
||||
|
||||
if (targetIsHome) {
|
||||
// Target IS the user's home instance — they already have a native account.
|
||||
// Just login with bare username, no registration or homeInstance params.
|
||||
finalUsername = bareUsername;
|
||||
try {
|
||||
response = await tempClient.auth.login({
|
||||
username: bareUsername,
|
||||
password,
|
||||
});
|
||||
} catch {
|
||||
throw new DifferentPasswordError(bareUsername);
|
||||
}
|
||||
} else {
|
||||
// Target is a remote/third-party instance — register as user@homeHost
|
||||
finalUsername = `${bareUsername}@${trueHomeHost}`;
|
||||
|
||||
// 2a: Attempt registration with namespaced username
|
||||
try {
|
||||
@@ -182,8 +207,8 @@ export const useInstanceStore = create<InstanceState>((set, get) => ({
|
||||
username: finalUsername,
|
||||
password,
|
||||
displayName: displayName || currentUser.displayName || undefined,
|
||||
homeInstance,
|
||||
homeUserId: currentUser.id,
|
||||
homeInstance: trueHomeHost,
|
||||
homeUserId: trueHomeUserId,
|
||||
});
|
||||
} catch (err) {
|
||||
const message = (err as Error).message;
|
||||
@@ -206,11 +231,12 @@ export const useInstanceStore = create<InstanceState>((set, get) => ({
|
||||
// Namespaced login failed — try legacy plain username as fallback
|
||||
try {
|
||||
response = await tempClient.auth.login({
|
||||
username: currentUser.username,
|
||||
username: bareUsername,
|
||||
password,
|
||||
});
|
||||
} catch {
|
||||
throw new DifferentPasswordError(currentUser.username);
|
||||
throw new DifferentPasswordError(bareUsername);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,10 +268,13 @@ export const useInstanceStore = create<InstanceState>((set, get) => ({
|
||||
// Open WebSocket connection to the remote instance
|
||||
connectInstance(origin, response.token);
|
||||
|
||||
// Sync full home profile to new remote (fire-and-forget)
|
||||
// Sync home profile to new remote (fire-and-forget).
|
||||
// Skip when target is home — home is the source of truth for profile data.
|
||||
if (!targetIsHome) {
|
||||
syncProfileToRemote(instance).catch((err) => {
|
||||
console.warn(`[ProfileSync] Initial sync to ${origin} failed:`, err);
|
||||
});
|
||||
}
|
||||
|
||||
// Sync instance list to all instances (fire-and-forget)
|
||||
get().syncInstanceList().catch(() => {});
|
||||
|
||||
Reference in New Issue
Block a user