-
Notifications
You must be signed in to change notification settings - Fork 330
Description
What happened?
I use Octokit to download GitHub releases, this worked fine for months but suddenly stopped working as of today, with the following error:
HttpError: <Error><Code>AccessDenied</Code><Message>Multiple auth mechanisms are not allowed; please use either query parameters or an Authorization header</Message>
This is the function I use to download the release:
async function downloadRelease(destination: string, token: string, owner: string, repo: string, assetName: string) {
const octokit = new Octokit({
auth: token,
});
const release = await octokit.request('GET /repos/{owner}/{repo}/releases/{asset_id}', {
owner: owner,
repo: repo,
asset_id: 'latest',
});
const asset = await octokit.request(release.data.assets_url);
const assetData = (asset.data as { name: string; url: string }[]).find((assetData) => assetData.name === assetName);
if (!assetData) {
throw new Error(`Unable to find ${assetName}`);
}
const res = await octokit.request(assetData.url, {
headers: { accept: 'application/octet-stream' },
});
await fs.promises.writeFile(destination, Buffer.from(res.data));
}The part that fails is the actual download of the file:
const res = await octokit.request(assetData.url, {
headers: { accept: 'application/octet-stream' },
});What did you expect to happen?
Octokit to properly download the release asset as described in the docs: https://docs.github.com/en/rest/reference/repos#get-a-release-asset
What the problem might be
I assume that something changed in the GitHub API making authorization stricter? When downloading the file, GitHub redirects to another url. This redirected URL contains authentication information. I guess that Octokit then uses this URL but still also includes the authorization header containing the personal access token. This combination results in a failure.
Example of requested file:
https://api.github.com/repos/owner/repo/releases/assets/1234
Example of the URL containing credentials it redirects to (simplified/shortened):
https://objects.githubusercontent.com/github-production-release-asset-abc/1234-5678?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...
I can actually get it to work when rewriting the download part of my function using an external http library (I used got):
async function downloadRelease(destination: string, token: string, owner: string, repo: string, assetName: string) {
const octokit = new Octokit({
auth: token,
});
const release = await octokit.request('GET /repos/{owner}/{repo}/releases/{asset_id}', {
owner: owner,
repo: repo,
asset_id: 'latest',
});
const asset = await octokit.request(release.data.assets_url);
const assetData = (asset.data as { name: string; url: string }[]).find((assetData) => assetData.name === assetName);
if (!assetData) {
throw new Error(`Unable to find ${assetName}`);
}
const file = (
await got(assetData.url, {
method: 'GET',
headers: {
authorization: `token ${token}`,
accept: 'application/octet-stream',
},
})
).rawBody;
await fs.promises.writeFile(destination, Buffer.from(file));
}