Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OIDC sessions #2

Merged
merged 3 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FRONTEND_BASE_URL=http://localhost:8000
BACKEND_BASE_URL=http://localhost:8000
OIDC_DISCOVERY_URL=<discovery_url>
OIDC_CLIENT_ID=<client_id>
OIDC_CLIENT_SECRET=<client_secret>
TURN_SECRET_KEY=<turnserver_secret>
99 changes: 55 additions & 44 deletions frontend/src/ControlSocket.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const SIGNALING_SERVER_HOST = import.meta.env.VITE_SIGNALING_SERVER || location.host;
const SIGNALING_SERVER_PROTO = import.meta.env.DEV || location.protocol === 'http:' ? 'ws:' : 'wss:';
const SIGNALING_SERVER_HOST =
import.meta.env.VITE_SIGNALING_SERVER || location.host;
const SIGNALING_SERVER_PROTO =
import.meta.env.DEV || location.protocol === "http:" ? "ws:" : "wss:";
const SIGNALING_SERVER_WS_URL = `${SIGNALING_SERVER_PROTO}//${SIGNALING_SERVER_HOST}`;

interface RequestHandler {
Expand All @@ -8,12 +10,12 @@ interface RequestHandler {
}

interface Props {
onError(error: string): void,
onTokenData(tokenData: TokenData): void,
onIceServersUpdated(iceServers: RTCIceServer[]): void,
onPeerJoined(clientID: string): void,
onBroadcast(message: any): void,
setSession(session?: Session): void,
onError(error: string): void;
onTokenData(tokenData: TokenData): void;
onIceServersUpdated(iceServers: RTCIceServer[]): void;
onPeerJoined(clientID: string): void;
onBroadcast(message: any): void;
setSession(session?: Session): void;
}

export interface Session {
Expand All @@ -22,13 +24,13 @@ export interface Session {
}

export type TokenData = {
token_clientId: string,
token_user: string | null,
token_logins: number,
token_clientId: string;
token_user: string | null;
token_logins: number;
};

import { getLogger } from './Logger';
const logger = getLogger('ControlSocket');
import { getLogger } from "./Logger";
const logger = getLogger("ControlSocket");

export default class ControlSocket {
private requestId = 0;
Expand All @@ -38,7 +40,7 @@ export default class ControlSocket {
private socketReady?: Promise<WebSocket>;

public constructor(readonly props: Props) {
logger.info('new ControlSocket()');
logger.info("new ControlSocket()");
}

private async getSocket(): Promise<WebSocket> {
Expand All @@ -51,7 +53,7 @@ export default class ControlSocket {
case WebSocket.CLOSING:
case WebSocket.CLOSED:
default:
throw new Error('Control socket not open');
throw new Error("Control socket not open");
}
}

Expand All @@ -70,27 +72,27 @@ export default class ControlSocket {

private connect(): Promise<WebSocket> {
return new Promise((resolve, reject) => {
logger.info('ControlSocket::connect()');
logger.info("ControlSocket::connect()");

const socket = new WebSocket(SIGNALING_SERVER_WS_URL);

socket.addEventListener('open', () => {
socket.addEventListener("open", () => {
resolve(socket);
});

socket.addEventListener('error', () => {
socket.addEventListener("error", () => {
reject();
});

socket.addEventListener('message', (event) => {
socket.addEventListener("message", (event) => {
this.handleMessage(event.data);
});
});
}

public close() {
if (this.socket) {
logger.info('ControlSocket::close()');
logger.info("ControlSocket::close()");
this.socket.close();
delete this.socket;
}
Expand All @@ -101,13 +103,13 @@ export default class ControlSocket {
const message = JSON.parse(data);
if (!message.id) {
switch (message.method) {
case 'peerJoined':
logger.info('ControlSocket::handleMessage(): Peer joined');
case "peerJoined":
logger.info("ControlSocket::handleMessage(): Peer joined");
const { clientId } = message.params;
this.props.onPeerJoined(clientId);
return;
case 'broadcast':
logger.info('ControlSocket::handleMessage(): Broadcast');
case "broadcast":
logger.info("ControlSocket::handleMessage(): Broadcast");
this.props.onBroadcast(message.params);
return;
default:
Expand Down Expand Up @@ -150,12 +152,14 @@ export default class ControlSocket {

public async authenticate() {
const str = document.cookie.match(/session=([^;]+)/) || [];
const response = await this.request('authenticate', { token: str[1] || '' });
const response = await this.request("authenticate", {
token: str[1] || "",
});
return this.handleAuthResponse(response);
}

private async getIceConfig(hostname: string): Promise<RTCIceServer[]> {
const result = await this.request('getIceConfig', { hostname });
const result = await this.request("getIceConfig", { hostname });

if (!Array.isArray(result)) {
throw new Error(`Invalid "getIceConfig" response: ${result}`);
Expand All @@ -165,22 +169,24 @@ export default class ControlSocket {
urls,
username: user || undefined,
credential: pass || undefined,
credentialType: (user && pass) ? 'password' : undefined,
}))
credentialType: user && pass ? "password" : undefined,
}));
}

public async broadcast(type: string, data: object) {
const result = await this.request('broadcast', { params: { type, ...data } });
const result = await this.request("broadcast", {
params: { type, ...data },
});
logger.info(`ControlSocket::broadcast(): response: ${result}`);
}

public async login(username: string) {
const response = await this.request('login', { username });
const response = await this.request("login", { username });
return this.handleAuthResponse(response);
}

public async newSession(): Promise<string> {
const result = await this.request('newSession');
const result = await this.request("newSession");

const session = parseSession(result);
if (!session) {
Expand All @@ -194,21 +200,23 @@ export default class ControlSocket {

public async joinSession(session: Session) {
if (this.session) {
logger.info(`ControlSocket::joinSession(): Already joined session: ${this.session.id}`);
logger.info(
`ControlSocket::joinSession(): Already joined session: ${this.session.id}`,
);
return;
}

const { id: sessionId, pin: sessionPin } = session;
await this.request('joinSession', { sessionId, sessionPin });
await this.request("joinSession", { sessionId, sessionPin });

this.session = session;
this.props.setSession(session);
}

public async leaveSession() {
const result = await this.request('leaveSession');
const result = await this.request("leaveSession");

if (result !== 'OK') {
if (result !== "OK") {
throw new Error(`Invalid "leaveSession" response: ${result}`);
}

Expand All @@ -217,13 +225,15 @@ export default class ControlSocket {
}

private handleAuthResponse(response: unknown): TokenData {
const hasStructure = Array.isArray(response) &&
const hasStructure =
Array.isArray(response) &&
response.length === 2 &&
typeof response[0] === 'object' &&
typeof response[0].token_clientId === 'string' &&
(typeof response[0].token_user === 'string' || response[0].token_user === null) &&
typeof response[0].token_logins === 'number' &&
typeof response[1] === 'string';
typeof response[0] === "object" &&
typeof response[0].token_clientId === "string" &&
(typeof response[0].token_user === "string" ||
response[0].token_user === null) &&
typeof response[0].token_logins === "number" &&
typeof response[1] === "string";

if (!hasStructure) {
throw new Error(`Invalid "authorize" response: ${response}`);
Expand All @@ -244,9 +254,10 @@ export default class ControlSocket {
}

function parseSession(result: any): Session | null {
const hasStructure = typeof result === 'object' &&
typeof result.id === 'string' &&
typeof result.pin === 'string';
const hasStructure =
typeof result === "object" &&
typeof result.id === "string" &&
typeof result.pin === "string";

if (!hasStructure) return null;

Expand Down
35 changes: 17 additions & 18 deletions frontend/src/Logger.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
type LogLevel = 'debug' | 'info' | 'log' | 'warning' | 'error';
type LogLevel = "debug" | "info" | "log" | "warning" | "error";

let levelsToLog = new Set<LogLevel>(['log', 'warning', 'error']);
let levelsToLog = new Set<LogLevel>(["log", "warning", "error"]);

export function setLevel(level: LogLevel) {
levelsToLog.clear();

switch (level) {
case 'debug':
levelsToLog.add('debug');
case 'info':
levelsToLog.add('info');
case 'log':
levelsToLog.add('log');
case 'warning':
levelsToLog.add('warning');
case 'error':
levelsToLog.add('error');
case "debug":
levelsToLog.add("debug");
case "info":
levelsToLog.add("info");
case "log":
levelsToLog.add("log");
case "warning":
levelsToLog.add("warning");
case "error":
levelsToLog.add("error");
}
}


export class Logger {
constructor(readonly name: string) {}

Expand All @@ -28,31 +27,31 @@ export class Logger {
}

public log(...data: any[]) {
if (levelsToLog.has('log')) {
if (levelsToLog.has("log")) {
console.log(this.prefix(), ...data);
}
}

public debug(...data: any[]) {
if (levelsToLog.has('debug')) {
if (levelsToLog.has("debug")) {
console.debug(this.prefix(), ...data);
}
}

public info(...data: any[]) {
if (levelsToLog.has('info')) {
if (levelsToLog.has("info")) {
console.info(this.prefix(), ...data);
}
}

public warning(...data: any[]) {
if (levelsToLog.has('warning')) {
if (levelsToLog.has("warning")) {
console.warn(this.prefix(), ...data);
}
}

public error(...data: any[]) {
if (levelsToLog.has('error')) {
if (levelsToLog.has("error")) {
console.error(this.prefix(), ...data);
}
}
Expand Down
Loading
Loading