Skip to content

Commit

Permalink
[Remote] Actually fix auth (#260)
Browse files Browse the repository at this point in the history
* fix favicon, basic auth

* actual fix......
  • Loading branch information
kgarner7 authored Sep 25, 2023
1 parent b375238 commit 9e3e038
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 101 deletions.
47 changes: 24 additions & 23 deletions src/main/features/core/remote/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ interface MimeType {

interface StatefulWebSocket extends WebSocket {
alive: boolean;
auth: boolean;
}

let server: Server | undefined;
Expand All @@ -52,7 +53,7 @@ type SendData = ServerEvent & {

function send({ client, event, data }: SendData): void {
if (client.readyState === WebSocket.OPEN) {
if (client.alive) {
if (client.alive && client.auth) {
client.send(JSON.stringify({ data, event }));
}
}
Expand Down Expand Up @@ -320,12 +321,13 @@ const enableServer = (config: RemoteConfig): Promise<void> => {

wsServer.on('connection', (ws) => {
let authFail: number | undefined;
ws.alive = true;

if (!settings.username && !settings.password) {
ws.alive = true;
ws.auth = true;
} else {
authFail = setTimeout(() => {
if (!ws.alive) {
if (!ws.auth) {
ws.close();
}
}, 10000) as unknown as number;
Expand All @@ -334,30 +336,29 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
ws.on('error', console.error);

ws.on('message', (data) => {
if (!ws.alive) {
try {
const auth = data.toString().split(' ')[1];
const [login, password] = Buffer.from(auth, 'base64')
.toString()
.split(':');

if (login === settings.username && password === settings.password) {
ws.alive = true;
} else {
ws.close();
}

clearTimeout(authFail);
} catch (e) {
console.error(e);
}
return;
}

try {
const json = JSON.parse(data.toString()) as ClientEvent;
const event = json.event;

if (!ws.auth) {
if (event === 'authenticate') {
const auth = json.header.split(' ')[1];
const [login, password] = Buffer.from(auth, 'base64')
.toString()
.split(':');

if (login === settings.username && password === settings.password) {
ws.auth = true;
} else {
ws.close();
}

clearTimeout(authFail);
} else {
return;
}
}

switch (event) {
case 'pause': {
getMainWindow()?.webContents.send('renderer-player-pause');
Expand Down
165 changes: 88 additions & 77 deletions src/remote/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const useRemoteStore = create<SettingsSlice>()(
devtools(
immer((set, get) => ({
actions: {
reconnect: () => {
reconnect: async () => {
const existing = get().socket;

if (existing) {
Expand All @@ -99,91 +99,102 @@ export const useRemoteStore = create<SettingsSlice>()(
existing.close(4001);
}
}
set(async (state) => {
try {
const credentials = await fetch('/credentials');
const authHeader = await credentials.text();

const socket = new WebSocket(
// eslint-disable-next-line no-restricted-globals
location.href.replace('http', 'ws'),
) as StatefulWebSocket;

socket.natural = false;

socket.addEventListener('message', (message) => {
const { event, data } = JSON.parse(message.data) as ServerEvent;

switch (event) {
case 'error': {
toast.error({ message: data, title: 'Socket error' });
break;
}
case 'favorite': {
set((state) => {
if (state.info.song?.id === data.id) {
state.info.song.userFavorite = data.favorite;
}
});
break;
}
case 'proxy': {
set((state) => {
if (state.info.song) {
state.info.song.imageUrl = `data:image/jpeg;base64,${data}`;
}
});
break;
}
case 'rating': {
set((state) => {
if (state.info.song?.id === data.id) {
state.info.song.userRating = data.rating;
}
});
break;
}
case 'song': {
set((nested) => {
nested.info = { ...nested.info, ...data };
});
}
let authHeader: string | undefined;

try {
const credentials = await fetch('/credentials');
authHeader = await credentials.text();
} catch (error) {
console.error('Failed to get credentials');
}

set((state) => {
const socket = new WebSocket(
// eslint-disable-next-line no-restricted-globals
location.href.replace('http', 'ws'),
) as StatefulWebSocket;

socket.natural = false;

socket.addEventListener('message', (message) => {
const { event, data } = JSON.parse(message.data) as ServerEvent;

switch (event) {
case 'error': {
toast.error({ message: data, title: 'Socket error' });
break;
}
});

socket.addEventListener('open', () => {
socket.send(authHeader);
set({ connected: true });
});

socket.addEventListener('close', (reason) => {
if (reason.code === 4002 || reason.code === 4003) {
// eslint-disable-next-line no-restricted-globals
location.reload();
} else if (reason.code === 4000) {
toast.warn({
message: 'Feishin remote server is down',
title: 'Connection closed',
case 'favorite': {
set((state) => {
if (state.info.song?.id === data.id) {
state.info.song.userFavorite = data.favorite;
}
});
} else if (reason.code !== 4001 && !socket.natural) {
toast.error({
message: 'Socket closed for unexpected reason',
title: 'Connection closed',
break;
}
case 'proxy': {
set((state) => {
if (state.info.song) {
state.info.song.imageUrl = `data:image/jpeg;base64,${data}`;
}
});
break;
}

if (!socket.natural) {
set({ connected: false, info: {} });
case 'rating': {
set((state) => {
if (state.info.song?.id === data.id) {
state.info.song.userRating = data.rating;
}
});
break;
}
});

state.socket = socket;
} catch (err) {
console.error(err);
}
case 'song': {
set((nested) => {
nested.info = { ...nested.info, ...data };
});
}
}
});

socket.addEventListener('open', () => {
if (authHeader) {
socket.send(
JSON.stringify({
event: 'authenticate',
header: authHeader,
}),
);
}
set({ connected: true });
});

socket.addEventListener('close', (reason) => {
if (reason.code === 4002 || reason.code === 4003) {
// eslint-disable-next-line no-restricted-globals
location.reload();
} else if (reason.code === 4000) {
toast.warn({
message: 'Feishin remote server is down',
title: 'Connection closed',
});
} else if (reason.code !== 4001 && !socket.natural) {
toast.error({
message: 'Socket closed for unexpected reason',
title: 'Connection closed',
});
}

if (!socket.natural) {
set({ connected: false, info: {} });
}
});

state.socket = socket;
});
},
send: (data: ClientEvent) => {
console.log(data, get().socket);
get().socket?.send(JSON.stringify(data));
},
toggleIsDark: () => {
Expand Down
12 changes: 11 additions & 1 deletion src/remote/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,14 @@ export interface ClientVolume {
volume: number;
}

export type ClientEvent = ClientSimpleEvent | ClientFavorite | ClientRating | ClientVolume;
export interface ClientAuth {
event: 'authenticate';
header: string;
}

export type ClientEvent =
| ClientAuth
| ClientSimpleEvent
| ClientFavorite
| ClientRating
| ClientVolume;

1 comment on commit 9e3e038

@vercel
Copy link

@vercel vercel bot commented on 9e3e038 Sep 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

feishin – ./

feishin-git-development-jeffvli.vercel.app
feishin.vercel.app
feishin-jeffvli.vercel.app

Please sign in to comment.