electron-builder: Electron Updater error: Only one auth mechanism allowed
-
Electron Builder Version: 22.9.1
-
Electron Version: 9.1.0
-
Electron Type (current, beta, nightly): current
-
Electron Updater Version: 4.3.5
-
Target: mac - dmg and zip
Since yesterday, I’ve not been able to use the updater on my app. Anytime the app checks for an update, I get the below error message.
I’m aware something like this has happened before, per #1370, however this suddenly popped up on old and new versions of Electron Builder/Updater.
I was using Electron Updater v4.2.2 and Electron Builder v22.7.0, before updating to the latest versions listed above, but regardless, the issue is the same.
I haven’t changed anything about my config for weeks, so struggling to figure out what the issue might be. Any help would be appreciated!
ERROR MESSAGE
Error in auto-updater. HttpError: 400
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<Error>
<Code>InvalidArgument</Code>
<Message>Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified</Message>
<ArgumentName>Authorization</ArgumentName>
<ArgumentValue>token __redacted__</ArgumentValue>
<RequestId>__redacted__</RequestId>
<HostId>__redacted__</HostId>
</Error>"
Headers: {
"status": "400",
"x-amz-request-id": "51FCEE6BE4B9B7ED",
"x-amz-id-2": "TwopT1BDNyVCWzhApPt7ZNju/FLIsmlTkkI7XaYpOkBc90vYGFpLjIJ3oLV45kDet1TIfZ4UGHs=",
"content-type": "application/xml",
"server": "AmazonS3",
"accept-ranges": "bytes",
"via": "1.1 varnish, 1.1 varnish",
"date": "Fri, 29 Jan 2021 08:16:35 GMT",
"x-served-by": "cache-dca17729-DCA, cache-lon4226-LON",
"x-cache": "MISS, MISS",
"x-cache-hits": "0, 0",
"x-fastly-request-id": "43468af043a1d5472820e0dbf9403e6912b6de68"
}
package.json config:
"build": {
"afterSign": "deploy/notarize.js",
"appId": "com.electron._redacted_",
"buildDependenciesFromSource": true,
"generateUpdatesFilesForAllChannels": true,
"icon": "build/assets/icon.png",
"productName": "_redacted_",
"directories": {
"buildResources": "build",
"output": "dist"
},
"mac": {
"hardenedRuntime": true,
"gatekeeperAssess": false,
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.plist",
"category": "public.app-category._redacted_",
"target": [
"dmg",
"zip"
]
},
"dmg": {
"sign": false
},
"asarUnpack": [
"build/pydist"
],
"publish": [
{
"provider": "github",
"owner": "_redacted_",
"repo": "_redacted_",
"token": "_redacted_",
"private": true,
"releaseType": "release"
}
]
}
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 44
- Comments: 67 (3 by maintainers)
Commits related to this issue
- Bump app-builder-lib from 22.9.1 to 22.10.5 in /desktop (#2072) Summary: allow-large-files Bumps [app-builder-lib](https://github.com/electron-userland/electron-builder) from 22.9.1 to 22.10.5. <deta... — committed to facebook/flipper by dependabot[bot] 3 years ago
- Bump electron-builder from 22.9.1 to 22.10.5 in /desktop (#2068) Summary: Bumps [electron-builder](https://github.com/electron-userland/electron-builder) from 22.9.1 to 22.10.5. <details> <summary>Re... — committed to facebook/flipper by dependabot[bot] 3 years ago
Great news! I talked to GitHub support, and they made a server-side change so that applications using the old version of electron-updater can successfully check for updates again!
I’ve only tested this for an application updating from a private GitHub repository, so someone else will have to see if it’s fixed for public repositories too.
I figured out the solution for this and this will worked for other users also who already downloaded the app(added Screenshot) these changes you can do in your pipeline
//////////////////////////////////////////////////////////////////// “use strict”;
Object.defineProperty(exports, “__esModule”, { value: true }); exports.createHttpError = createHttpError; exports.parseJson = parseJson; exports.configureRequestOptionsFromUrl = configureRequestOptionsFromUrl; exports.configureRequestUrl = configureRequestUrl; exports.safeGetHeader = safeGetHeader; exports.configureRequestOptions = configureRequestOptions; exports.safeStringifyJson = safeStringifyJson; exports.DigestTransform = exports.HttpExecutor = exports.HttpError = void 0;
function _crypto() { const data = require(“crypto”);
_crypto = function () { return data; };
return data; }
var _debug2 = _interopRequireDefault(require(“debug”));
var _fs = require(“fs”);
function _stream() { const data = require(“stream”);
_stream = function () { return data; };
return data; }
function _url() { const data = require(“url”);
_url = function () { return data; };
return data; }
function _CancellationToken() { const data = require(“./CancellationToken”);
_CancellationToken = function () { return data; };
return data; }
function _index() { const data = require(“./index”);
_index = function () { return data; };
return data; }
function _ProgressCallbackTransform() { const data = require(“./ProgressCallbackTransform”);
_ProgressCallbackTransform = function () { return data; };
return data; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const debug = (0, _debug2.default)(“electron-builder”);
function createHttpError(response, description = null) { return new HttpError(response.statusCode || -1,
${response.statusCode} ${response.statusMessage}+ (description == null ? “” : “\n” + JSON.stringify(description, null, " ")) + "\nHeaders: " + safeStringifyJson(response.headers), description); }const HTTP_STATUS_CODES = new Map([[429, “Too many requests”], [400, “Bad request”], [403, “Forbidden”], [404, “Not found”], [405, “Method not allowed”], [406, “Not acceptable”], [408, “Request timeout”], [413, “Request entity too large”], [500, “Internal server error”], [502, “Bad gateway”], [503, “Service unavailable”], [504, “Gateway timeout”], [505, “HTTP version not supported”]]);
class HttpError extends Error { constructor(statusCode, message =
HTTP error: ${HTTP_STATUS_CODES.get(statusCode) || statusCode}, description = null) { super(message); this.statusCode = statusCode; this.description = description; this.name = “HttpError”; this.code =HTTP_ERROR_${statusCode}; }}
exports.HttpError = HttpError;
function parseJson(result) { return result.then(it => it == null || it.length === 0 ? null : JSON.parse(it)); }
class HttpExecutor { constructor() { this.maxRedirects = 10; }
request(options, cancellationToken = new (_CancellationToken().CancellationToken)(), data) { configureRequestOptions(options); const encodedData = data == null ? undefined : Buffer.from(JSON.stringify(data));
}
doApiRequest(options, cancellationToken, requestProcessor, redirectCount = 0) { if (debug.enabled) { debug(
Request: ${safeStringifyJson(options)}); }} // noinspection JSUnusedLocalSymbols // eslint-disable-next-line
addRedirectHandlers(request, options, reject, redirectCount, handler) {// not required for NodeJS }
addErrorAndTimeoutHandlers(request, reject) { this.addTimeOutHandler(request, reject); request.on(“error”, reject); request.on(“aborted”, () => { reject(new Error(“Request has been aborted by the server”)); }); }
handleResponse(response, options, cancellationToken, resolve, reject, redirectCount, requestProcessor) { if (debug.enabled) { debug(
Response: ${response.statusCode} ${response.statusMessage}, request options: ${safeStringifyJson(options)}); } // we handle any other >= 400 error on request end (read detailed message in the response body)Please double check that your authentication token is correct. Due to security reasons actual status maybe not reported, but 404. `)); return; } else if (response.statusCode === 204) { // on DELETE request resolve(); return; }
}
async downloadToBuffer(url, options) { return await options.cancellationToken.createPromise((resolve, reject, onCancel) => { let result = null; const requestOptions = { headers: options.headers || undefined, // because PrivateGitHubProvider requires HttpExecutor.prepareRedirectUrlOptions logic, so, we need to redirect manually redirect: “manual” }; configureRequestUrl(url, requestOptions); configureRequestOptions(requestOptions); this.doDownload(requestOptions, { destination: null, options, onCancel, callback: error => { if (error == null) { resolve(result); } else { reject(error); } }, responseHandler: (response, callback) => { const contentLength = safeGetHeader(response, “content-length”); let position = -1;
}
doDownload(requestOptions, options, redirectCount) { const request = this.createRequest(requestOptions, response => { if (response.statusCode >= 400) { options.callback(new Error(
Cannot download "${requestOptions.protocol || "https:"}//${requestOptions.hostname}${requestOptions.path}", status ${response.statusCode}: ${response.statusMessage})); return; } // It is possible for the response stream to fail, e.g. when a network is lost while // response stream is in progress. Stop waiting and reject so consumer can catch the error.}
createMaxRedirectError() { return new Error(
Too many redirects (> ${this.maxRedirects})); }addTimeOutHandler(request, callback) { request.on(“socket”, socket => { socket.setTimeout(60 * 1000, () => { request.abort(); callback(new Error(“Request timed out”)); }); }); }
static prepareRedirectUrlOptions(redirectUrl, options) { const newOptions = configureRequestOptionsFromUrl(redirectUrl, { …options }); const headers = newOptions.headers;
}
}
exports.HttpExecutor = HttpExecutor;
function configureRequestOptionsFromUrl(url, options) { const result = configureRequestOptions(options); configureRequestUrl(new (_url().URL)(url), result); return result; }
function configureRequestUrl(url, options) { options.protocol = url.protocol; options.hostname = url.hostname;
if (url.port) { options.port = url.port; } else if (options.port) { delete options.port; }
options.path = url.pathname + url.search; }
class DigestTransform extends _stream().Transform { constructor(expected, algorithm = “sha512”, encoding = “base64”) { super(); this.expected = expected; this.algorithm = algorithm; this.encoding = encoding; this._actual = null; this.isValidateOnEnd = true; this.digester = (0, _crypto().createHash)(algorithm); } // noinspection JSUnusedGlobalSymbols
get actual() { return this._actual; } // noinspection JSUnusedGlobalSymbols
_transform(chunk, encoding, callback) { this.digester.update(chunk); callback(null, chunk); } // noinspection JSUnusedGlobalSymbols
_flush(callback) { this._actual = this.digester.digest(this.encoding);
}
validate() { if (this._actual == null) { throw (0, _index().newError)(“Not finished yet”, “ERR_STREAM_NOT_FINISHED”); }
}
}
exports.DigestTransform = DigestTransform;
function checkSha2(sha2Header, sha2, callback) { if (sha2Header != null && sha2 != null && sha2Header !== sha2) { callback(new Error(
checksum mismatch: expected ${sha2} but got ${sha2Header} (X-Checksum-Sha2 header))); return false; }return true; }
function safeGetHeader(response, headerKey) { const value = response.headers[headerKey];
if (value == null) { return null; } else if (Array.isArray(value)) { // electron API return value.length === 0 ? null : value[value.length - 1]; } else { return value; } }
function configurePipes(options, response) { if (!checkSha2(safeGetHeader(response, “X-Checksum-Sha2”), options.options.sha2, options.callback)) { return; }
const streams = [];
if (options.options.onProgress != null) { const contentLength = safeGetHeader(response, “content-length”);
}
const sha512 = options.options.sha512;
if (sha512 != null) { streams.push(new DigestTransform(sha512, “sha512”, sha512.length === 128 && !sha512.includes(“+”) && !sha512.includes(“Z”) && !sha512.includes(“=”) ? “hex” : “base64”)); } else if (options.options.sha2 != null) { streams.push(new DigestTransform(options.options.sha2, “sha256”, “hex”)); }
const fileOut = (0, _fs.createWriteStream)(options.destination); streams.push(fileOut); let lastStream = response;
for (const stream of streams) { stream.on(“error”, error => { if (!options.options.cancellationToken.cancelled) { options.callback(error); } }); lastStream = lastStream.pipe(stream); }
fileOut.on(“finish”, () => { fileOut.close(options.callback); }); }
function configureRequestOptions(options, token, method) { if (method != null) { options.method = method; }
options.headers = { …options.headers }; const headers = options.headers;
if (token != null) { headers.authorization = token.startsWith(“Basic”) ? token :
token ${token}; }if (headers[“User-Agent”] == null) { headers[“User-Agent”] = “electron-builder”; }
if (method == null || method === “GET” || headers[“Cache-Control”] == null) { headers[“Cache-Control”] = “no-cache”; } // do not specify for node (in any case we use https module)
if (options.protocol == null && process.versions.electron != null) { options.protocol = “https:”; }
return options; }
function safeStringifyJson(data, skippedNames) { return JSON.stringify(data, (name, value) => { if (name.endsWith(“authorization”) || name.endsWith(“Password”) || name.endsWith(“PASSWORD”) || name.endsWith(“Token”) || name.includes(“password”) || name.includes(“token”) || skippedNames != null && skippedNames.has(name)) { return “<stripped sensitive data>”; }
}, 2); } // __ts-babel@6.0.4 //# sourceMappingURL=httpExecutor.js.map ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Doesn’t this have to be fixed somehow else? What if people have the older version installed and can’t update to the new one?
This definitely fixed the error running on my local machine, but what about 500+ installs of my app on users machines? They can’t get this update because their local versions will be failing checking for the update. There has to be another fix applied to a server somewhere right?
node_modules/builder-util-runtime/out/httpExecutor.js.endsWith(".amazonaws.com")and replace with.endsWith(".amazonaws.com") || parsedNewUrl.searchParams.has("X-Amz-Credential")Thanks to @GaryCaldwell https://github.com/electron-userland/electron-builder/pull/5594
I am amazed at how little interest this bug seems to have, nobody uses Electron? I think I will migrate to a generic server to avoid this kind of problem in the future …
I changed to public repo but still have this error
@Daniel-Sudhindaran I contacted Github support and they provided a fix on their server side for this. Issue seems to be fixed now. Checked it on windows and updates work again.
This is not the fault of the Electron Builder software. Something has changed on the AWS side and until they revert whatever change they made, existing installed apps will never be able to update. The suggested fix will only make future updates work, but unfortunately, existing users will have to install that next version manually that has the code fix.
We have taken out the release through Github Releases/AWS completely and changed to a generic server so we never have this problem again and had to send out a manual download link with an apology. Many users are ok with clicking a manual download link and letting it install and then you can carry on as you were. It is embarrassing and a massive pain I know but it gets you back up and running.
Doesn’t this have to be fixed somehow else? What if people have the older version installed and can’t update to the new one?
^ This is what I was trying to ask. Users have the old one installed, so how do they update to get the new one?
omg def need a fix… can’t wait!!
Does anyone have any pointers for this exact issue? I’m unsure how to distribute the fix to the current users 🤔 Appreciate any help given 🙏🏽
I also experienced this today - using a github private repo for releases.
@abraaoz @paulkoeckdev - replace with .endsWith(“.amazonaws.com”) || parsedNewUrl.searchParams.has(“X-Amz-Credential”) is not working!!
Same issue. Using GitHub private repo. Need server side fix otherwise other users won’t get latest update so the workaround mentioned above isn’t a viable option. Please help! Thanks.
Doesn’t this have to be fixed somehow else? What if people have the older version installed and can’t update to the new one?
Just noticed a pull request which seems to cover this issue at #5594. Made the adjustment in my local project, and fixed the error, but seems to be failing some of their tests so may lead to some other issues? Definitely the right direction though!
As @Daniel-Sudhindaran also mentioned it’s true this issue re-surfaced. It also happens again at windows and with mac.
By doing the change mentioned here https://github.com/electron-userland/electron-builder/issues/5595#issuecomment-770019745 it works again. But this is not a viable solution as all of our customeres have the older version installed and can’t update to the new one which would have this change.
I guess it’s something related to github releases and this does not work again, probably they changed something. As @NoahAndrews mentioned above https://github.com/electron-userland/electron-builder/issues/5595#issuecomment-781444995 he had spoke to Github support and problem was solved back in February.
I guess we should contact them again.
Has anybody made a donation yet requesting this issue? I am sure if we all chip in a couple bucks they would fix this faster…
Also having this issue, no code and repo configuration changed.
EDIT:
This fixed it for me, but a permanent solution is needed without changing the node_modules.
@NoahAndrews Damn I had a few tabs open of issues, and I commented on a wrong one, my bad!
https://github.com/electron-userland/electron-builder/releases/tag/v22.10.5
So the fix has been successfully merged, I was wondering how I can obtain the fix now like is it by just running an npm update or do we have to wait for the next release?
welcome to the club buddy, we’re all waiting for the developers to merge the PR with the fix for this issue. it’s also not their fault it’s more on AWS side of things because of some aauth credentials or tokens.
But the people who built this Updater system (infinite thanks for it) don’t thought on this scenario? xD
I hear what you’re saying, but the problem with this is that we can’t simply ask users to manually download a new update. There are more than 1000 users using our application now and not all of them will know how to do this. It might be easy to some people but not to others. Some users wont have a clue what to do. That’s why auto updates are so good to use in the first place.
What worries me is that this could happen again at some point and then what? Do some sort of manual update again? We just can’t risk that. I agree with @stephanebouget, we are going to migrate to a generic server and take back some control.
Same issue occurs since 5 days with a public repo. Any guidance and help for this would be greatly appreciated.