Gotcha: Don't try and authenticate to URLs generated by GitHub Actions Artifacts v4

Featured image for sharing metadata for article

I recently spotted GitHub's announcement about the new v4 release for working with GitHub Actions Artifacts and impressed with the performance increase they mentioned, wanted to jump on it.

However, I noticed that my API requests to fetch the uploaded artifacts have been failing since I started uploading using the v4 version of the Actions πŸ€”

For context, I've been using the excellent go-github library, and have been doing something like:

import "github.com/google/go-github/v58/github"

// ...

var client *github.Client

// ...

u, _, err := client.Actions.DownloadArtifact(ctx, "...", "...", build.ArtifactID, 1)
// ...
resp, err := client.Do(ctx, req, zipFile)
// ...

Notice that I'm re-using the github.Client to determine the URL to download a given artifact, as well as then performing the download.

It appears that between v3 and v4 of the Actions upload, this has meant that (for some reason) you can no longer send authenticated requests to the static storage location. This may be "I'm holding it wrong", or may be an unintentional side effect, but thought it was worth writing about it as a form of blogumentation.

I've created a sample repo which has a single Actions run which allows us to verify the behaviour change.

We can see from the following Go code:

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/google/go-github/v58/github"
)

func main() {
	ctx := context.Background()
	client := github.NewClient(http.DefaultClient).WithAuthToken(os.Getenv("GITHUB_TOKEN"))

	v3, _, err := client.Actions.DownloadArtifact(ctx, "jamietanna", "github-v4-issue", 1248209756, 1)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("v3: %v\n", v3)

	resp, err := http.DefaultClient.Get(v3.String())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("http.Client v3: Status %d\n", resp.StatusCode)

	req, _ := http.NewRequestWithContext(ctx, http.MethodGet, v3.String(), nil)
	ghResp, err := client.Do(ctx, req, nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("github.Client v3: Status %d\n", ghResp.StatusCode)

	v4, _, err := client.Actions.DownloadArtifact(ctx, "jamietanna", "github-v4-issue", 1248209411, 1)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("v4: %v\n", v4)

	resp, err = http.DefaultClient.Get(v4.String())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("http.Client v4: Status %d\n", resp.StatusCode)

	req, _ = http.NewRequestWithContext(ctx, http.MethodGet, v4.String(), nil)
	ghResp, err = client.Do(ctx, req, nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("github.Client v4: Status %d\n", ghResp.StatusCode)
}

That we then get the following output (linebreaks added for readability):

v3: https://pipelinesghubeus8.actions.githubusercontent.com/emX3Njq6l1qrJDF1bWo9bNVi4eQC64vCBMTaoenXjgnzcrY5lm/_apis/pipelines/1/runs/1/signedartifactscontent?artifactName=1mb-v3&urlExpires=2024-02-15T14%3A48%3A56.6160877Z&urlSigningMethod=HMACV2&urlSignature=1e5RyuMzU8AztwX5Upqs%2FIjbKQfqVqnOFv7pMHN8q2Y%3D

http.Client v3: Status 200
github.Client v3: Status 200


v4: https://productionresultssa15.blob.core.windows.net/actions-results/c7e89aef-cd37-4c12-9cd7-bb30fd886b3c/workflow-job-run-0442c3fb-37e5-5ecb-50da-0a737a3d5924/artifacts/7427eae80b26c2a022516adae0ef004a452a257e4cf1c45fc906d6c83c1f0379.zip?rscd=attachment%3B+filename%3D%221mb-v4.zip%22&se=2024-02-15T14%3A57%3A57Z&sig=cxZHg8ZAeTMW2NVN5nu2AdMD24h2%2Fss0UDoriROJEho%3D&sp=r&spr=https&sr=b&st=2024-02-15T14%3A47%3A57Z&sv=2021-12-02

http.Client v4: Status 200

2024/02/15 14:47:58 GET https://productionresultssa15.blob.core.windows.net/actions-results/c7e89aef-cd37-4c12-9cd7-bb30fd886b3c/workflow-job-run-0442c3fb-37e5-5ecb-50da-0a737a3d5924/artifacts/7427eae80b26c2a022516adae0ef004a452a257e4cf1c45fc906d6c83c1f0379.zip?rscd=attachment%3B+filename%3D%221mb-v4.zip%22&se=2024-02-15T14%3A57%3A57Z&sig=cxZHg8ZAeTMW2NVN5nu2AdMD24h2%2Fss0UDoriROJEho%3D&sp=r&spr=https&sr=b&st=2024-02-15T14%3A47%3A57Z&sv=2021-12-02: 400  []

Digging through the code, it appears that this is due to a response from the new static storage location, productionresultssa15.blob.core.windows.net, which returns:

<?xml version="1.0" encoding="utf-8"?>
<Error>
	<Code>InvalidAuthenticationInfo</Code>
	<Message>Authentication information is not given in the correct format. Check the value of Authorization header.
RequestId:....
Time:.....</Message>
</Error>

So the TL;DR is purposefully don't authenticate to retrieve the static blobs, as that shouldn't be required, even if they are artifacts from private repos!

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #github-actions #go.

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.