Skip to content
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
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions src/backend/src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -1546,7 +1546,7 @@ export function subdomain (req) {
return req.hostname.slice(0, -1 * (config.domain.length + 1));
}

export async function jwt_auth (req) {
export async function jwt_auth (req, authService) {
let token;
// HTTML Auth header
if ( req.header && req.header('Authorization') )
Expand Down Expand Up @@ -1583,8 +1583,11 @@ export async function jwt_auth (req) {
}

try {
const svc_auth = Context.get('services').get('auth');
const actor = await svc_auth.authenticate_from_token(token);
if ( ! authService ) {
throw new Error('jwt_auth requires authService');
}

const actor = await authService.authenticate_from_token(token);

if ( !actor.type?.constructor?.name === 'UserActorType' ) {
throw ({
Expand Down
4 changes: 1 addition & 3 deletions src/backend/src/modules/web/SocketioService.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@ class SocketioService extends BaseService {
if ( socket_specifier.room ) {
this.io.to(socket_specifier.room).emit(key, data);
} else if ( socket_specifier.socket ) {
const io = this.io.sockets.sockets.get(socket_specifier.socket);
if ( ! io ) continue;
io.emit(key, data);
this.io.to(socket_specifier.socket).emit(key, data);
}
}
}
Expand Down
75 changes: 45 additions & 30 deletions src/backend/src/modules/web/WebServerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,27 +260,36 @@ class WebServerService extends BaseService {
// TODO: ^ Replace above line with the following code:
await this.services.emit('install.socketio', { server });
const socketio = this.services.get('socketio').io;
const authService = this.services.get('auth');

// Socket.io middleware for authentication
socketio.use(async (socket, next) => {
if ( socket.handshake.auth.auth_token ) {
try {
let auth_res = await jwt_auth(socket);
// successful auth
socket.actor = auth_res.actor;
socket.user = auth_res.user;
socket.token = auth_res.token;
// join user room
socket.join(socket.user.id);

// setTimeout 0 is needed because we need to send
// the notifications after this handler is done
// setTimeout(() => {
// }, 1000);
next();
} catch (e) {
console.warn('socket auth err', e);
}
const authToken = socket.handshake?.auth?.auth_token;
if ( ! authToken ) {
next(new Error('socket auth token missing'));
return;
}

try {
const authRes = await jwt_auth(socket, authService);
// successful auth
socket.actor = authRes.actor;
socket.user = authRes.user;
socket.token = authRes.token;
// join user room
socket.join(socket.user.id);

// setTimeout 0 is needed because we need to send
// the notifications after this handler is done
// setTimeout(() => {
// }, 1000);
next();
} catch ( error ) {
console.warn('socket auth err', error);
const authError = error instanceof Error
? error
: new Error('socket auth failed');
next(authError);
}
});

Expand Down Expand Up @@ -674,6 +683,16 @@ 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) ||
Expand All @@ -692,16 +711,16 @@ class WebServerService extends BaseService {
req.co_isolation_enabled
;

if ( req.path === '/signup' || req.path === '/login' || req.path.startsWith('/extensions/') || req.path.startsWith('/auth/oidc') ) {
if ( isCrossOriginAuthRoute || isApiOrDavRequest ) {
res.setHeader('Access-Control-Allow-Origin', origin ?? '*');
if ( origin ) {
res.vary('Origin');
}
}
// 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 ?? '*');

// Allow browser credentials on API/DAV cross-origin requests.
if ( isApiOrDavRequest && origin ) {
res.setHeader('Access-Control-Allow-Credentials', 'true');
}

// Request methods to allow
Expand All @@ -715,10 +734,6 @@ 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
Expand Down
3 changes: 2 additions & 1 deletion src/backend/src/routers/_default.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ 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
Expand All @@ -137,7 +138,7 @@ router.all('*', async function (req, res, next) {
let authed = false;
try {
try {
auth_user = await jwt_auth(req);
auth_user = await jwt_auth(req, authService);
auth_user = auth_user.user;
authed = true;
} catch (e) {
Expand Down
37 changes: 19 additions & 18 deletions src/backend/src/services/WSPushService.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,22 @@ 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) {
Expand Down Expand Up @@ -251,15 +259,11 @@ class WSPushService extends BaseService {
const { socket_id } = metadata;

if ( ! socket_id ) {
this.log.warn('missing socket id', { metadata });
console.warn('missing socket id', { metadata });
return;
}

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';
Expand Down Expand Up @@ -311,9 +315,6 @@ 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,
Expand All @@ -340,7 +341,7 @@ class WSPushService extends BaseService {
const kvStore = Context.get('services').get('puter-kvstore');
await kvStore.set({ key: key, value: ts });
} catch ( error ) {
this.log.error('Failed to update user timestamp in kvstore', { user_id, error: error.message });
console.error('Failed to update user timestamp in kvstore', { user_id, error: error.message });
}
}

Expand Down
1 change: 1 addition & 0 deletions src/gui/src/helpers/download.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ 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');

Expand Down
4 changes: 2 additions & 2 deletions src/puter-js/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/puter-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@heyputer/puter.js",
"version": "2.2.12",
"version": "2.2.14",
"description": "Puter.js - A JavaScript library for interacting with Puter services.",
"homepage": "https://developer.puter.com",
"main": "src/index.js",
Expand Down
31 changes: 19 additions & 12 deletions src/puter-js/src/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ 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}`);
Expand Down Expand Up @@ -280,25 +281,31 @@ 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;
}

// 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 = {},
Expand Down
1 change: 1 addition & 0 deletions src/puter-js/src/modules/FileSystem/operations/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ 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}`);

Expand Down
Loading