Stop treating transient auth_required as home-session expiry, keep auth scope consistent across login redirect and server-rail joins, and leave /login when the in-memory user is still authenticated. Co-authored-by: Cursor <cursoragent@cursor.com>
100 lines
3.5 KiB
TypeScript
100 lines
3.5 KiB
TypeScript
import { Injectable, inject } from '@angular/core';
|
|
import { Router } from '@angular/router';
|
|
import { Store } from '@ngrx/store';
|
|
import { firstValueFrom } from 'rxjs';
|
|
import { selectCurrentUser } from '../../../../store/users/users.selectors';
|
|
import { ServerDirectoryFacade } from '../../../server-directory';
|
|
import { AUTH_MODE_AUTHORIZE, buildLoginReturnQueryParams } from '../../domain/logic/auth-navigation.rules';
|
|
import { isEndpointOnlineForConnection } from '../../../server-directory/domain/logic/server-endpoint-connectivity.rules';
|
|
import { shouldNavigateToAuthorizeSignalServer } from '../../domain/logic/signal-server-authorize.rules';
|
|
import { isSameSignalServerUrl } from '../../domain/logic/signal-server-auth-failure.rules';
|
|
import { SignalServerAuthService } from './signal-server-auth.service';
|
|
|
|
@Injectable({ providedIn: 'root' })
|
|
export class SignalServerAuthorizeService {
|
|
private readonly router = inject(Router);
|
|
private readonly store = inject(Store);
|
|
private readonly serverDirectory = inject(ServerDirectoryFacade);
|
|
private readonly signalServerAuth = inject(SignalServerAuthService);
|
|
|
|
async ensureCredentialForServerUrl(serverUrl: string): Promise<boolean> {
|
|
if (this.signalServerAuth.hasValidCredential(serverUrl)) {
|
|
return true;
|
|
}
|
|
|
|
const currentUser = await firstValueFrom(this.store.select(selectCurrentUser));
|
|
|
|
if (!currentUser) {
|
|
return false;
|
|
}
|
|
|
|
if (isSameSignalServerUrl(serverUrl, currentUser.homeSignalServerUrl)) {
|
|
// The home account is never auto-provisioned or re-authorized as a
|
|
// foreign account. Resurrect the credential from the legacy token store
|
|
// (session-restore case); a genuinely missing home session is handled by
|
|
// the session-expiry flow, not the authorize login page.
|
|
this.signalServerAuth.migrateHomeCredential(currentUser);
|
|
|
|
return this.signalServerAuth.hasValidCredential(serverUrl);
|
|
}
|
|
|
|
let result;
|
|
|
|
try {
|
|
result = await this.signalServerAuth.ensureProvisioned(serverUrl, currentUser);
|
|
} catch {
|
|
return false;
|
|
}
|
|
|
|
if (result.kind === 'existing' || result.kind === 'provisioned') {
|
|
return true;
|
|
}
|
|
|
|
const endpointStatus = await this.resolveEndpointStatusForAuthorize(serverUrl);
|
|
|
|
if (shouldNavigateToAuthorizeSignalServer(endpointStatus, result)) {
|
|
await this.navigateToAuthorize(serverUrl, this.router.url);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private async resolveEndpointStatusForAuthorize(serverUrl: string) {
|
|
const endpoint = this.serverDirectory.findServerByUrl(serverUrl);
|
|
|
|
if (!endpoint) {
|
|
return null;
|
|
}
|
|
|
|
if (isEndpointOnlineForConnection(endpoint.status) || endpoint.status === 'offline' || endpoint.status === 'incompatible') {
|
|
return endpoint.status;
|
|
}
|
|
|
|
await this.serverDirectory.testServer(endpoint.id);
|
|
|
|
return this.serverDirectory.servers().find((candidate) => candidate.id === endpoint.id)?.status ?? endpoint.status;
|
|
}
|
|
|
|
async navigateToAuthorize(serverUrl: string, returnUrl: string): Promise<void> {
|
|
const endpoint = this.serverDirectory.ensureServerEndpoint({
|
|
name: this.buildEndpointName(serverUrl),
|
|
url: serverUrl
|
|
});
|
|
|
|
await this.router.navigate(['/login'], {
|
|
queryParams: buildLoginReturnQueryParams(returnUrl, undefined, {
|
|
mode: AUTH_MODE_AUTHORIZE,
|
|
serverId: endpoint.id
|
|
})
|
|
});
|
|
}
|
|
|
|
private buildEndpointName(serverUrl: string): string {
|
|
try {
|
|
return new URL(serverUrl).hostname;
|
|
} catch {
|
|
return serverUrl;
|
|
}
|
|
}
|
|
}
|