UNPKG

@casual-simulation/aux-common

Version:
175 lines 6.31 kB
/* CasualOS is a set of web-based tools designed to facilitate the creation of real-time, multi-user, context-aware interactive experiences. * * Copyright (c) 2019-2025 Casual Simulation, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ import { BehaviorSubject, NEVER, Observable, distinctUntilChanged, filter, firstValueFrom, map, of, startWith, switchMap, } from 'rxjs'; /** * Defines a connection client that attempts to authenticate before bubbling a connection event. */ export class AuthenticatedConnectionClient { constructor(inner, authSource) { this._inner = inner; this._authSource = authSource; this._connectionStateChanged = new BehaviorSubject({ connected: false, info: null, }); this._inner.connectionState .pipe(switchMap((state) => this._login(state.connected)), // Only deduplicate disconnected events distinctUntilChanged((previous, current) => !current.connected && current.connected === previous.connected)) .subscribe(this._connectionStateChanged); this._authSource.onAuthPermissionRequest.subscribe((request) => { if (request.origin === this.origin) { this._inner.send({ type: 'permission/request/missing', reason: request.reason, }); } }); this._authSource.onAuthPermissionResult.subscribe((result) => { if (result.origin === this.origin) { this._inner.send({ ...result, type: 'permission/request/missing/response', }); } }); this._inner.event('permission/request/missing').subscribe((request) => { this._authSource.sendAuthExternalPermissionRequest({ type: 'external_permission_request', origin: this.origin, reason: request.reason, user: request.user, }); }); this._inner .event('permission/request/missing/response') .subscribe((response) => { this._authSource.sendAuthExternalPermissionResult({ ...response, type: 'external_permission_result', origin: this.origin, }); }); } get origin() { return this._inner.origin; } get info() { return this._connectionStateChanged.value.info; } get indicator() { return this._authSource.getConnectionIndicatorForOrigin(this.origin); } get connectionState() { return this._connectionStateChanged; } get onError() { return this._inner.onError; } get isConnected() { return this._inner.isConnected; } /** * Gets an observable for events with the given name. * @param name The name of the events. */ event(name) { return this._inner.event(name); } send(message) { return this._inner.send(message); } disconnect() { this._inner.disconnect(); } connect() { this._inner.connect(); } _login(connected) { if (connected) { console.log('[AuthencatedConnectionClient] Logging in...'); const indicator = this.indicator; let responses = this._authSource.onAuthResponseForOrigin(this.origin); if (indicator) { responses = responses.pipe(startWith({ type: 'response', success: true, indicator, origin: this.origin, })); } else { this._authSource.sendAuthRequest({ type: 'request', origin: this.origin, kind: 'need_indicator', }); } const loginStates = responses.pipe(filter((r) => r.success === true), map((r) => r.indicator), switchMap((indicator) => this._loginWithIndicator(indicator)), switchMap((v) => v)); return loginStates; } else { return of({ connected: false, info: null, }); } } async _loginWithIndicator(indicator) { const onLoginResult = this._inner.event('login_result'); this._inner.send({ type: 'login', ...indicator, }); const result = await firstValueFrom(onLoginResult); if (result.success === true) { console.log('[AuthencatedConnectionClient] Logged in.'); return of({ connected: true, info: result.info, }); } else { // handle login failure console.error('[AuthenticatedConnectionClient] Login failed:', result); // Resend request for indicator this._authSource.sendAuthRequest({ type: 'request', origin: this.origin, kind: 'invalid_indicator', indicator: indicator, errorCode: result.errorCode, errorMessage: result.errorMessage, reason: result.reason, }); return NEVER; } } } function onSubscribe(onSubscribe) { return function inner(source) { return new Observable((observer) => { const sub = source.subscribe(observer); onSubscribe(); return sub; }); }; } //# sourceMappingURL=AuthenticatedConnectionClient.js.map