Desktop, Cli: Bind OneDrive OAuth and resource servers to loopback and validate OAuth state

This commit is contained in:
Laurent Cozic committed 2026-10-18 21:55:10 +01:00
1 parent eba62ede92
commit c4c49b6d28
3 files changed
+9 -5

No files matched your search

+1 -1
View File
@@ -84,7 +84,7 @@ class ResourceServer {
this.logger().error('Resource server:', error); this.logger().error('Resource server:', error);
}); });
this.server_.listen(this.port_); this.server_.listen(this.port_, '127.0.0.1');
enableServerDestroy(this.server_); enableServerDestroy(this.server_);
+5 -2
View File
@@ -5,6 +5,7 @@ import OneDriveApi from './onedrive-api';
import * as http from 'http'; import * as http from 'http';
import * as urlParser from 'url'; import * as urlParser from 'url';
import { randomBytes } from 'crypto';
const enableServerDestroy = require('server-destroy'); const enableServerDestroy = require('server-destroy');
interface DestroyableServer extends http.Server { interface DestroyableServer extends http.Server {
@@ -57,7 +58,8 @@ export class OneDriveApiNodeUtils {
const port = await findAvailablePort(require('tcp-port-used'), this.possibleOAuthDancePorts(), 0); const port = await findAvailablePort(require('tcp-port-used'), this.possibleOAuthDancePorts(), 0);
if (!port) throw new Error(_('All potential ports are in use - please report the issue at %s', 'https://github.com/laurent22/joplin')); if (!port) throw new Error(_('All potential ports are in use - please report the issue at %s', 'https://github.com/laurent22/joplin'));
const authCodeUrl = this.api().authCodeUrl(`http://localhost:${port}`); const state = randomBytes(32).toString('base64url');
const authCodeUrl = this.api().authCodeUrl(`http://localhost:${port}`, state);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.oauthServer_ = http.createServer() as DestroyableServer; this.oauthServer_ = http.createServer() as DestroyableServer;
@@ -91,6 +93,7 @@ export class OneDriveApiNodeUtils {
}; };
if (!query.code) return writeResponse(400, '"code" query parameter is missing'); if (!query.code) return writeResponse(400, '"code" query parameter is missing');
if (query.state !== state) return writeResponse(400, '"state" query parameter is invalid');
this.api() this.api()
.execTokenRequest(query.code as string, `http://localhost:${port.toString()}`) .execTokenRequest(query.code as string, `http://localhost:${port.toString()}`)
@@ -118,7 +121,7 @@ export class OneDriveApiNodeUtils {
} }
}); });
this.oauthServer_.listen(port); this.oauthServer_.listen(port, '127.0.0.1');
enableServerDestroy(this.oauthServer_); enableServerDestroy(this.oauthServer_);
+3 -2
View File
@@ -92,14 +92,15 @@ export default class OneDriveApi {
return `${r.parentReference.path}/${r.name}`; return `${r.parentReference.path}/${r.name}`;
} }
public authCodeUrl(redirectUri: string) { public authCodeUrl(redirectUri: string, state: string | null = null) {
const query = { const query: Record<string, string> = {
client_id: this.clientId_, client_id: this.clientId_,
scope: 'files.readwrite offline_access sites.readwrite.all', scope: 'files.readwrite offline_access sites.readwrite.all',
response_type: 'code', response_type: 'code',
redirect_uri: redirectUri, redirect_uri: redirectUri,
prompt: 'login', prompt: 'login',
}; };
if (state) query.state = state;
return `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${stringify(query)}`; return `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${stringify(query)}`;
} }