Revert "fix: tighten cors logic for socket io (#2688)" (#2694)

This reverts commit a94620de49.
This commit is contained in:
Daniel Salazar
2026-03-19 12:24:14 -07:00
committed by GitHub
parent a94620de49
commit 5e2c7e0495
11 changed files with 58 additions and 116 deletions
+1 -1
View File
@@ -24422,7 +24422,7 @@
},
"src/puter-js": {
"name": "@heyputer/puter.js",
"version": "2.2.14",
"version": "2.2.12",
"license": "Apache-2.0",
"dependencies": {
"@heyputer/kv.js": "^0.2.1",
+3 -6
View File
@@ -1546,7 +1546,7 @@ export function subdomain (req) {
return req.hostname.slice(0, -1 * (config.domain.length + 1));
}
export async function jwt_auth (req, authService) {
export async function jwt_auth (req) {
let token;
// HTTML Auth header
if ( req.header && req.header('Authorization') )
@@ -1583,11 +1583,8 @@ export async function jwt_auth (req, authService) {
}
try {
if ( ! authService ) {
throw new Error('jwt_auth requires authService');
}
const actor = await authService.authenticate_from_token(token);
const svc_auth = Context.get('services').get('auth');
const actor = await svc_auth.authenticate_from_token(token);
if ( !actor.type?.constructor?.name === 'UserActorType' ) {
throw ({
+8 -46
View File
@@ -22,22 +22,6 @@ const socketio = require('socket.io');
const { createAdapter } = require('@socket.io/redis-streams-adapter');
const { redisClient } = require('../../clients/redis/redisSingleton');
const normalizeHost = (value) => {
if ( typeof value !== 'string' ) return null;
const trimmedValue = value.trim().toLowerCase().replace(/^\./, '');
if ( ! trimmedValue ) return null;
return trimmedValue.split(':')[0];
};
const extractOriginHost = (origin) => {
if ( typeof origin !== 'string' || origin.length === 0 ) return null;
try {
return normalizeHost(new URL(origin).host);
} catch {
return null;
}
};
/**
* SocketioService provides a service for sending messages to clients.
* socket.io is used behind the scenes. This service provides a simpler
@@ -50,39 +34,15 @@ class SocketioService extends BaseService {
* @evtparam server The server to attach socket.io to.
*/
'__on_install.socketio' (_, { server }) {
const uiHost = normalizeHost(this.global_config.domain);
const apiHost = uiHost ? `api.${uiHost}` : null;
const isApiRequest = (req) => normalizeHost(req?.headers?.host) === apiHost;
const isUiOriginAllowed = (req) => {
const origin = req?.headers?.origin;
if ( ! origin ) return false;
return extractOriginHost(origin) === uiHost;
};
/**
* @type {import('socket.io').Server}
*/
const socketioOptions = {
cors: (req, callback) => {
if ( isApiRequest(req) ) {
callback(null, {
origin: true,
credentials: true,
});
return;
}
callback(null, {
origin: isUiOriginAllowed(req),
credentials: true,
});
},
allowRequest: (req, callback) => {
if ( isApiRequest(req) ) {
callback(null, true);
return;
}
callback(null, isUiOriginAllowed(req));
cors: {
origin: (origin, callback) => {
callback(null, origin);
},
credentials: true,
},
adapter: createAdapter(redisClient),
};
@@ -106,7 +66,9 @@ class SocketioService extends BaseService {
if ( socket_specifier.room ) {
this.io.to(socket_specifier.room).emit(key, data);
} else if ( socket_specifier.socket ) {
this.io.to(socket_specifier.socket).emit(key, data);
const io = this.io.sockets.sockets.get(socket_specifier.socket);
if ( ! io ) continue;
io.emit(key, data);
}
}
}
+12 -18
View File
@@ -674,16 +674,6 @@ class WebServerService extends BaseService {
app.use(function (req, res, next) {
const origin = req.headers.origin;
const subdomain = req.subdomains[req.subdomains.length - 1];
const isApiOrDavRequest =
config.experimental_no_subdomain ||
subdomain === 'api' ||
subdomain === 'dav';
const isCrossOriginAuthRoute =
req.path === '/signup' ||
req.path === '/login' ||
req.path.startsWith('/extensions/') ||
req.path.startsWith('/auth/oidc');
const is_site =
hostMatchesDomain(req.hostname, config.static_hosting_domain) ||
@@ -702,16 +692,16 @@ class WebServerService extends BaseService {
req.co_isolation_enabled
;
if ( isCrossOriginAuthRoute || isApiOrDavRequest ) {
if ( req.path === '/signup' || req.path === '/login' || req.path.startsWith('/extensions/') || req.path.startsWith('/auth/oidc') ) {
res.setHeader('Access-Control-Allow-Origin', origin ?? '*');
if ( origin ) {
res.vary('Origin');
}
}
// Allow browser credentials on API/DAV cross-origin requests.
if ( isApiOrDavRequest && origin ) {
res.setHeader('Access-Control-Allow-Credentials', 'true');
// Website(s) to allow to connect
if (
config.experimental_no_subdomain ||
req.subdomains[req.subdomains.length - 1] === 'api' ||
req.subdomains[req.subdomains.length - 1] === 'dav'
) {
res.setHeader('Access-Control-Allow-Origin', origin ?? '*');
}
// Request methods to allow
@@ -725,6 +715,10 @@ class WebServerService extends BaseService {
// Request headers to allow
res.header('Access-Control-Allow-Headers', allowed_headers.join(', '));
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
// res.setHeader('Access-Control-Allow-Credentials', true);
// Needed for SharedArrayBuffer
// NOTE: This is put behind a configuration flag because we
// need some experimentation to ensure the interface
+1 -2
View File
@@ -113,7 +113,6 @@ router.all('*', async function (req, res, next) {
}
const db = Context.get('services').get('database').get(DB_READ, 'default');
const authService = Context.get('services').get('auth');
// --------------------------------------
// POST to login/signup/logout
@@ -138,7 +137,7 @@ router.all('*', async function (req, res, next) {
let authed = false;
try {
try {
auth_user = await jwt_auth(req, authService);
auth_user = await jwt_auth(req);
auth_user = auth_user.user;
authed = true;
} catch (e) {
+18 -19
View File
@@ -34,22 +34,14 @@ class WSPushService extends BaseService {
this.svc_event.on('fs.write.*', this._on_fs_update.bind(this));
this.svc_event.on('fs.move.*', this._on_fs_move.bind(this));
this.svc_event.on('fs.pending.*', this._on_fs_pending.bind(this));
this.svc_event.on(
'fs.storage.upload-progress',
this._on_upload_progress.bind(this),
);
this.svc_event.on(
'fs.storage.progress.*',
this._on_upload_progress.bind(this),
);
this.svc_event.on(
'puter-exec.submission.done',
this._on_submission_done.bind(this),
);
this.svc_event.on(
'outer.gui.*',
this._on_outer_gui.bind(this),
);
this.svc_event.on('fs.storage.upload-progress',
this._on_upload_progress.bind(this));
this.svc_event.on('fs.storage.progress.*',
this._on_upload_progress.bind(this));
this.svc_event.on('puter-exec.submission.done',
this._on_submission_done.bind(this));
this.svc_event.on('outer.gui.*',
this._on_outer_gui.bind(this));
}
async _on_fs_create (key, data) {
@@ -259,11 +251,15 @@ class WSPushService extends BaseService {
const { socket_id } = metadata;
if ( ! socket_id ) {
console.warn('missing socket id', { metadata });
return;
this.log.warn('missing socket id', { metadata });
}
this.log.info(`socket id: ${ socket_id}`);
const svc_socketio = context.get('services').get('socketio');
if ( ! svc_socketio.has({ socket: socket_id }) ) {
return;
}
const ws_event_name = metadata.call_it_download
? 'download.progress' : 'upload.progress';
@@ -315,6 +311,9 @@ class WSPushService extends BaseService {
const svc_socketio = this.services.get('socketio');
for ( const user_id of user_id_list ) {
if ( ! svc_socketio.has({ room: user_id }) ) {
continue;
}
svc_socketio.send({ room: user_id }, key, response);
this.svc_event.emit(`sent-to-user.${key}`, {
user_id,
@@ -341,7 +340,7 @@ class WSPushService extends BaseService {
const kvStore = Context.get('services').get('puter-kvstore');
await kvStore.set({ key: key, value: ts });
} catch ( error ) {
console.error('Failed to update user timestamp in kvstore', { user_id, error: error.message });
this.log.error('Failed to update user timestamp in kvstore', { user_id, error: error.message });
}
}
-1
View File
@@ -49,7 +49,6 @@ const download = function (options) {
let xhr = new XMLHttpRequest();
xhr.open('post', (`${window.api_origin }/download`), true);
xhr.withCredentials = true;
xhr.setRequestHeader('Authorization', `Bearer ${ window.auth_token}`);
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "puter",
"version": "2.2.14",
"version": "2.2.12",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "puter",
"version": "2.2.14",
"version": "2.2.12",
"license": "Apache-2.0",
"dependencies": {
"@heyputer/kv.js": "^0.1.92",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@heyputer/puter.js",
"version": "2.2.14",
"version": "2.2.12",
"description": "Puter.js - A JavaScript library for interacting with Puter services.",
"homepage": "https://developer.puter.com",
"main": "src/index.js",
+12 -19
View File
@@ -92,7 +92,6 @@ const createDeferred = () => {
function initXhr (endpoint, APIOrigin, authToken, method = 'post', contentType = 'text/plain;actually=json', responseType = undefined) {
const xhr = new XMLHttpRequest();
xhr.open(method, APIOrigin + endpoint, true);
xhr.withCredentials = true;
if ( authToken )
{
xhr.setRequestHeader('Authorization', `Bearer ${ authToken}`);
@@ -281,18 +280,16 @@ function make_driver_method (arg_defs, driverInterface, driverName, driverMethod
async function driverCall (options, driverInterface, driverName, driverMethod, driverArgs, settings) {
const deferred = createDeferred();
driverCall_(
options,
deferred.resolve,
deferred.reject,
driverInterface,
driverName,
driverMethod,
driverArgs,
undefined,
undefined,
settings,
);
driverCall_(options,
deferred.resolve,
deferred.reject,
driverInterface,
driverName,
driverMethod,
driverArgs,
undefined,
undefined,
settings);
return await deferred.promise;
}
@@ -300,12 +297,8 @@ async function driverCall (options, driverInterface, driverName, driverMethod, d
// This function encapsulates the logic for sending a driver call request
async function driverCall_ (
options = {},
resolve_func,
reject_func,
driverInterface,
driverName,
driverMethod,
driverArgs,
resolve_func, reject_func,
driverInterface, driverName, driverMethod, driverArgs,
method,
contentType = 'text/plain;actually=json',
settings = {},
@@ -457,7 +457,6 @@ const upload = async function (items, dirPath, options = {}) {
// open request to server
xhr.open('post', (`${this.APIOrigin }/batch`), true);
xhr.withCredentials = true;
// set auth header
xhr.setRequestHeader('Authorization', `Bearer ${ this.authToken}`);