-
Notifications
You must be signed in to change notification settings - Fork 2k
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
@uppy/companion: remove oauthOrigin
#5311
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Diff output filesdiff --git a/packages/@uppy/companion/lib/config/companion.js b/packages/@uppy/companion/lib/config/companion.js
index 5931656..1839910 100644
--- a/packages/@uppy/companion/lib/config/companion.js
+++ b/packages/@uppy/companion/lib/config/companion.js
@@ -107,8 +107,8 @@ const validateConfig = (companionOptions) => {
"startup.uploadUrls",
);
}
- if (!companionOptions.oauthOrigin) {
- throw new TypeError("Option oauthOrigin is required. To disable security, pass \"*\"");
+ if (companionOptions.corsOrigins == null) {
+ throw new TypeError("Option corsOrigins is required. To disable security, pass true");
}
if (
periodicPingUrls != null && (!Array.isArray(periodicPingUrls)
diff --git a/packages/@uppy/companion/lib/server/controllers/connect.d.ts b/packages/@uppy/companion/lib/server/controllers/connect.d.ts
index e2b73d9..668b7e4 100644
--- a/packages/@uppy/companion/lib/server/controllers/connect.d.ts
+++ b/packages/@uppy/companion/lib/server/controllers/connect.d.ts
@@ -1,2 +1,2 @@
-declare function _exports(req: object, res: object): void;
+declare function _exports(req: object, res: object, next: any): void;
export = _exports;
diff --git a/packages/@uppy/companion/lib/server/controllers/connect.js b/packages/@uppy/companion/lib/server/controllers/connect.js
index 374cd76..7d7d616 100644
--- a/packages/@uppy/companion/lib/server/controllers/connect.js
+++ b/packages/@uppy/companion/lib/server/controllers/connect.js
@@ -1,42 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const oAuthState = require("../helpers/oauth-state");
-const queryString = (params, prefix = "?") => {
- const str = new URLSearchParams(params).toString();
- return str ? `${prefix}${str}` : "";
-};
/**
- * initializes the oAuth flow for a provider.
- *
- * @param {object} req
- * @param {object} res
+ * Derived from `cors` npm package.
+ * @see https://github.com/expressjs/cors/blob/791983ebc0407115bc8ae8e64830d440da995938/lib/index.js#L19-L34
+ * @param {string} origin
+ * @param {*} allowedOrigins
+ * @returns {boolean}
*/
-module.exports = function connect(req, res) {
- const { secret, oauthOrigin } = req.companion.options;
- const stateObj = oAuthState.generateState();
- // not sure if we need to store origin in the session state (e.g. we could've just gotten it directly inside send-token)
- // but we're afraid to change the logic there
- if (!Array.isArray(oauthOrigin)) {
- // If the server only allows a single origin, we ignore the client-supplied
- // origin from query because we don't need it.
- stateObj.origin = oauthOrigin;
- } else if (oauthOrigin.length < 2) {
- // eslint-disable-next-line prefer-destructuring
- stateObj.origin = oauthOrigin[0];
- } else {
- // If we have multiple allowed origins, we need to check the client-supplied origin from query.
- // If the client provides an untrusted origin,
- // we want to send `undefined`. `undefined` means `/`, which is the same origin when passed to `postMessage`.
- // https://html.spec.whatwg.org/multipage/web-messaging.html#dom-window-postmessage-options-dev
- const { origin } = JSON.parse(atob(req.query.state));
- stateObj.origin = oauthOrigin.find(o => o === origin);
+function isOriginAllowed(origin, allowedOrigins) {
+ if (Array.isArray(allowedOrigins)) {
+ return allowedOrigins.some(allowedOrigin => isOriginAllowed(origin, allowedOrigin));
}
- if (req.companion.options.server.oauthDomain) {
- stateObj.companionInstance = req.companion.buildURL("", true);
- }
- if (req.query.uppyPreAuthToken) {
- stateObj.preAuthToken = req.query.uppyPreAuthToken;
+ if (typeof allowedOrigins === "string") {
+ return origin === allowedOrigins;
}
+ return allowedOrigins.test?.(origin) ?? !!allowedOrigins;
+}
+const queryString = (params, prefix = "?") => {
+ const str = new URLSearchParams(params).toString();
+ return str ? `${prefix}${str}` : "";
+};
+function encodeStateAndRedirect(req, res, stateObj) {
+ const { secret } = req.companion.options;
const state = oAuthState.encodeState(stateObj, secret);
const { providerClass, providerGrantConfig } = req.companion;
// pass along grant's dynamic config (if specified for the provider in its grant config `dynamic` section)
@@ -62,4 +48,62 @@ module.exports = function connect(req, res) {
});
// Now we redirect to grant's /connect endpoint, see `app.use(Grant(grantConfig))`
res.redirect(req.companion.buildURL(`/connect/${oauthProvider}${qs}`, true));
+}
+function getClientOrigin(base64EncodedState) {
+ try {
+ const { origin } = JSON.parse(atob(base64EncodedState));
+ return origin;
+ } catch {
+ return undefined;
+ }
+}
+/**
+ * Initializes the oAuth flow for a provider.
+ *
+ * The client has open a new tab and is about to be redirected to the auth
+ * provider. When the user will return to companion, we'll have to send the auth
+ * token back to Uppy with `window.postMessage()`.
+ * To prevent other tabs and unauthorized origins from accessing that token, we
+ * reuse origin(s) from `corsOrigins` to limit the scope of `postMessage()`, which
+ * has `targetOrigin` parameter, required for cross-origin messages (i.e. if Uppy
+ * and Companion are served from different origins).
+ * We support multiple origins in `corsOrigins`, we have to figure out which
+ * origin the current connect request is coming from. Because the OAuth window
+ * was opened with `window.open()`, starting a new browsing context, the request
+ * is not cross origin and we don't have a `Origin` header to work with.
+ * That's why we use the client-provided base64-encoded parameter, check if it
+ * matches origin(s) allowed in `corsOrigins` Companion option, and use that as
+ * our `targetOrigin` for the `window.postMessage()` call (see `send-token.js`).
+ *
+ * @param {object} req
+ * @param {object} res
+ */
+module.exports = function connect(req, res, next) {
+ const stateObj = oAuthState.generateState();
+ if (req.companion.options.server.oauthDomain) {
+ stateObj.companionInstance = req.companion.buildURL("", true);
+ }
+ if (req.query.uppyPreAuthToken) {
+ stateObj.preAuthToken = req.query.uppyPreAuthToken;
+ }
+ // Get the computed header generated by `cors` in a previous middleware.
+ stateObj.origin = res.getHeader("Access-Control-Allow-Origin");
+ let clientOrigin;
+ if (!stateObj.origin && (clientOrigin = getClientOrigin(req.query.state))) {
+ const { corsOrigins } = req.companion.options;
+ if (typeof corsOrigins === "function") {
+ corsOrigins(clientOrigin, (err, finalOrigin) => {
+ if (err) {
+ next(err);
+ }
+ stateObj.origin = finalOrigin;
+ encodeStateAndRedirect(req, res, stateObj);
+ });
+ return;
+ }
+ if (isOriginAllowed(clientOrigin, req.companion.options.corsOrigins)) {
+ stateObj.origin = clientOrigin;
+ }
+ }
+ encodeStateAndRedirect(req, res, stateObj);
};
diff --git a/packages/@uppy/companion/lib/server/controllers/index.d.ts b/packages/@uppy/companion/lib/server/controllers/index.d.ts
index a782333..044ac31 100644
--- a/packages/@uppy/companion/lib/server/controllers/index.d.ts
+++ b/packages/@uppy/companion/lib/server/controllers/index.d.ts
@@ -6,7 +6,7 @@ export let thumbnail: typeof import("./thumbnail");
export let list: typeof import("./list");
export let simpleAuth: typeof import("./simple-auth");
export let logout: typeof import("./logout");
-export let connect: (req: any, res: any) => void;
+export let connect: (req: any, res: any, next: any) => void;
export let preauth: typeof import("./preauth");
export let redirect: (req: any, res: any) => void;
export let refreshToken: typeof import("./refresh-token");
diff --git a/packages/@uppy/companion/lib/standalone/helper.js b/packages/@uppy/companion/lib/standalone/helper.js
index fa5d55f..5c3e44d 100644
--- a/packages/@uppy/companion/lib/standalone/helper.js
+++ b/packages/@uppy/companion/lib/standalone/helper.js
@@ -40,9 +40,18 @@ const hasProtocol = (url) => {
const companionProtocol = process.env.COMPANION_PROTOCOL || "http";
function getCorsOrigins() {
if (process.env.COMPANION_CLIENT_ORIGINS) {
- return process.env.COMPANION_CLIENT_ORIGINS
- .split(",")
- .map((url) => (hasProtocol(url) ? url : `${companionProtocol}://${url}`));
+ switch (process.env.COMPANION_CLIENT_ORIGINS) {
+ case "true":
+ return true;
+ case "false":
+ return false;
+ case "*":
+ return "*";
+ default:
+ return process.env.COMPANION_CLIENT_ORIGINS
+ .split(",")
+ .map((url) => (hasProtocol(url) ? url : `${companionProtocol}://${url}`));
+ }
}
if (process.env.COMPANION_CLIENT_ORIGINS_REGEX) {
return new RegExp(process.env.COMPANION_CLIENT_ORIGINS_REGEX);
@@ -179,9 +188,6 @@ const getConfigFromEnv = () => {
corsOrigins: getCorsOrigins(),
testDynamicOauthCredentials: process.env.COMPANION_TEST_DYNAMIC_OAUTH_CREDENTIALS === "true",
testDynamicOauthCredentialsSecret: process.env.COMPANION_TEST_DYNAMIC_OAUTH_CREDENTIALS_SECRET,
- oauthOrigin: process.env.COMPANION_OAUTH_ORIGIN?.includes(",")
- ? process.env.COMPANION_OAUTH_ORIGIN.split(",")
- : process.env.COMPANION_OAUTH_ORIGIN,
};
};
/** |
Murderlon
reviewed
Jul 4, 2024
@@ -6,8 +6,10 @@ These cover all the major Uppy versions and how to migrate to them. | |||
|
|||
- End-of-Life versions of Node.js are no longer supported (use latest 18.x LTS, | |||
20.x LTS, or 22.x current). | |||
- Setting the `oauthOrigin` option is now required. To get back to the unsafe | |||
behavior of the previous version, set it to `'*'`. | |||
- Setting the `corsOrigin` option is now required. You should define the list of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's link to this option in the docs
mifi
reviewed
Jul 8, 2024
mifi
reviewed
Jul 8, 2024
mifi
reviewed
Jul 8, 2024
Murderlon
reviewed
Jul 8, 2024
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code LGTM.
Murderlon
approved these changes
Jul 8, 2024
Co-authored-by: Mikael Finstad <finstaden@gmail.com>
Merged
github-actions bot
added a commit
that referenced
this pull request
Jul 10, 2024
| Package | Version | Package | Version | | ------------------------- | ------- | ------------------------- | ------- | | @uppy/angular | 0.7.0 | @uppy/onedrive | 4.0.0 | | @uppy/audio | 2.0.0 | @uppy/progress-bar | 4.0.0 | | @uppy/aws-s3 | 4.0.0 | @uppy/provider-views | 4.0.0 | | @uppy/aws-s3-multipart | 4.0.0 | @uppy/react | 4.0.0 | | @uppy/box | 3.0.0 | @uppy/react-native | 0.6.0 | | @uppy/companion | 5.0.0 | @uppy/redux-dev-tools | 4.0.0 | | @uppy/companion-client | 4.0.0 | @uppy/remote-sources | 2.0.0 | | @uppy/compressor | 2.0.0 | @uppy/screen-capture | 4.0.0 | | @uppy/core | 4.0.0 | @uppy/status-bar | 4.0.0 | | @uppy/dashboard | 4.0.0 | @uppy/store-default | 4.0.0 | | @uppy/drag-drop | 4.0.0 | @uppy/store-redux | 4.0.0 | | @uppy/drop-target | 3.0.0 | @uppy/svelte | 4.0.0 | | @uppy/dropbox | 4.0.0 | @uppy/thumbnail-generator | 4.0.0 | | @uppy/facebook | 4.0.0 | @uppy/transloadit | 4.0.0 | | @uppy/file-input | 4.0.0 | @uppy/tus | 4.0.0 | | @uppy/form | 4.0.0 | @uppy/unsplash | 4.0.0 | | @uppy/golden-retriever | 4.0.0 | @uppy/url | 4.0.0 | | @uppy/google-drive | 4.0.0 | @uppy/utils | 6.0.0 | | @uppy/google-photos | 0.2.0 | @uppy/vue | 2.0.0 | | @uppy/image-editor | 3.0.0 | @uppy/webcam | 4.0.0 | | @uppy/informer | 4.0.0 | @uppy/xhr-upload | 4.0.0 | | @uppy/instagram | 4.0.0 | @uppy/zoom | 3.0.0 | | @uppy/locales | 4.0.0 | uppy | 4.0.0 | - meta: Bump docker/setup-qemu-action from 3.0.0 to 3.1.0 (dependabot[bot] / #5314) - meta: Bump docker/build-push-action from 6.2.0 to 6.3.0 (dependabot[bot] / #5313) - @uppy/core: bring back resetProgress (Merlijn Vos / #5320) - docs: add note regarding `COMPANION_CLIENT_ORIGINS_REGEX` (Antoine du Hamel / #5322) - @uppy/companion: make streaming upload default to `true` (Mikael Finstad / #5315) - docs: change slug for aws docs (Merlijn Vos / #5321) - @uppy/core: export UppyOptions, UppyFile, Meta, Body (Merlijn Vos / #5319) - meta: fix React linter rules (Antoine du Hamel / #5317) - meta: enforce use of extension in import type declarations (Antoine du Hamel / #5316) - @uppy/companion: remove `oauthOrigin` (Antoine du Hamel / #5311) - docs,@uppy/companion-client: don't close socket when pausing (Mikael Finstad / #4821) - @uppy/aws-s3: fix signing on client for bucket name with dots (Antoine du Hamel / #5312) - @uppy/react: introduce useUppyEvent (Merlijn Vos / #5264) - @uppy/companion: do not list Node.js 20.12 as compatible (Antoine du Hamel / #5309) - @uppy/provider-views: `.openFolder()` - return progress indication (Evgenia Karunus / #5306) - examples,@uppy/companion: Release: uppy@3.27.3 (github-actions[bot] / #5304) - @uppy/companion: fix `TypeError` when parsing request (Antoine du Hamel / #5303)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
No description provided.