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 { 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 { 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; } } }