Bläddra i källkod

fix(web,server): web socket auth (for web) (#4632)

Jason Rasmussen 1 år sedan
förälder
incheckning
0fb1d33f17

+ 1 - 1
server/src/domain/auth/auth.service.ts

@@ -147,7 +147,7 @@ export class AuthService {
     return mapAdminSignupResponse(admin);
   }
 
-  async validate(headers: IncomingHttpHeaders, params: Record<string, string>): Promise<AuthUserDto | null> {
+  async validate(headers: IncomingHttpHeaders, params: Record<string, string>): Promise<AuthUserDto> {
     const shareKey = (headers['x-immich-share-key'] || params.key) as string;
     const userToken = (headers['x-immich-user-token'] ||
       params.userToken ||

+ 0 - 5
server/src/immich/app.guard.ts

@@ -99,11 +99,6 @@ export class AppGuard implements CanActivate {
     const req = context.switchToHttp().getRequest<AuthRequest>();
 
     const authDto = await this.authService.validate(req.headers, req.query as Record<string, string>);
-    if (!authDto) {
-      this.logger.warn(`Denied access to authenticated route: ${req.path}`);
-      return false;
-    }
-
     if (authDto.isPublicUser && !isSharedRoute) {
       this.logger.warn(`Denied access to non-shared route: ${req.path}`);
       return false;

+ 7 - 11
server/src/infra/repositories/communication.repository.ts

@@ -18,26 +18,22 @@ export class CommunicationRepository implements OnGatewayConnection, OnGatewayDi
 
   async handleConnection(client: Socket) {
     try {
-      this.logger.log(`New websocket connection: ${client.id}`);
+      this.logger.log(`Websocket Connect:    ${client.id}`);
       const user = await this.authService.validate(client.request.headers, {});
-      if (user) {
-        await client.join(user.id);
-        for (const callback of this.onConnectCallbacks) {
-          await callback(user.id);
-        }
-      } else {
-        client.emit('error', 'unauthorized');
-        client.disconnect();
+      await client.join(user.id);
+      for (const callback of this.onConnectCallbacks) {
+        await callback(user.id);
       }
-    } catch (e) {
+    } catch (error: Error | any) {
+      this.logger.error(`Websocket connection error: ${error}`, error?.stack);
       client.emit('error', 'unauthorized');
       client.disconnect();
     }
   }
 
   async handleDisconnect(client: Socket) {
+    this.logger.log(`Websocket Disconnect: ${client.id}`);
     await client.leave(client.nsp.name);
-    this.logger.log(`Client ${client.id} disconnected from Websocket`);
   }
 
   send(event: CommunicationEvent, userId: string, data: any) {

+ 15 - 4
web/src/lib/stores/websocket.ts

@@ -1,5 +1,5 @@
 import type { AssetResponseDto, ServerVersionResponseDto } from '@api';
-import { io } from 'socket.io-client';
+import { Socket, io } from 'socket.io-client';
 import { writable } from 'svelte/store';
 import { loadConfig } from './server-config.store';
 
@@ -20,9 +20,15 @@ export const websocketStore = {
   onRelease: writable<ReleaseEvent>(),
 };
 
+let websocket: Socket | null = null;
+
 export const openWebsocketConnection = () => {
   try {
-    const websocket = io('', {
+    if (websocket) {
+      return;
+    }
+
+    websocket = io('', {
       path: '/api/socket.io',
       reconnection: true,
       forceNew: true,
@@ -40,9 +46,14 @@ export const openWebsocketConnection = () => {
       .on('on_config_update', () => loadConfig())
       .on('on_new_release', (data) => websocketStore.onRelease.set(data))
       .on('error', (e) => console.log('Websocket Error', e));
-
-    return () => websocket?.close();
   } catch (e) {
     console.log('Cannot connect to websocket ', e);
   }
 };
+
+export const closeWebsocketConnection = () => {
+  if (websocket) {
+    websocket.close();
+  }
+  websocket = null;
+};

+ 16 - 3
web/src/routes/+layout.svelte

@@ -18,7 +18,7 @@
   import { handleError } from '$lib/utils/handle-error';
   import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store';
   import { api } from '@api';
-  import { openWebsocketConnection } from '$lib/stores/websocket';
+  import { closeWebsocketConnection, openWebsocketConnection } from '$lib/stores/websocket';
 
   let showNavigationLoadingBar = false;
   export let data: LayoutData;
@@ -28,7 +28,18 @@
     api.setKey($page.params.key);
   }
 
-  beforeNavigate(() => {
+  beforeNavigate(({ from, to }) => {
+    const fromRoute = from?.route?.id || '';
+    const toRoute = to?.route?.id || '';
+
+    if (fromRoute.startsWith('/auth') && !toRoute.startsWith('/auth')) {
+      openWebsocketConnection();
+    }
+
+    if (!fromRoute.startsWith('/auth') && toRoute.startsWith('/auth')) {
+      closeWebsocketConnection();
+    }
+
     showNavigationLoadingBar = true;
   });
 
@@ -37,7 +48,9 @@
   });
 
   onMount(async () => {
-    openWebsocketConnection();
+    if ($page.route.id?.startsWith('/auth') === false) {
+      openWebsocketConnection();
+    }
 
     try {
       await loadConfig();