I know this isn't a super specific Godot-related question, but I cannot find any information to fix this problem.

I am porting Unity Gaming Services to Godot 4+ Mono and working on the user-generated content service. One issue I am running into is downloading files from the service using HTTP requests (specifically RestSharp here, tried normal and still an error, shown in code). It works fine with the cloud save service but fails with user-generated content.

When trying to download files from the service I keep running into an authentication error:

<?xml version='1.0' encoding='UTF-8'?><Error><Code>AuthenticationRequired</Code><Message>Authentication required.</Message></Error>

My Jwt auth token is working fine, I already call a request to get the content (method below) to get the URL to download. The getting of content in the first place wouldn’t work if there were a Jwt token-related auth issue.

Get Content Method:

public async Task<Content> GetContentAsync(GetContentArgs getContentArgs)
    {
        Validate();

        var request = new RestRequest(
            $"/v1/projects/{ProjectId}/environments/{EnvironmentId}/content/{getContentArgs.ContentId}"
        )
        {
            RequestFormat = DataFormat.Json
        }.AddQueryParameter("includeStatistics", getContentArgs.IncludeStatistics);

        var response = await ugcClient.ExecuteAsync<InternalContent>(request);
        if (response.IsSuccessful)
        {
            var content = new Content(response.Data);
            await DownloadContentDataAsync(content, getContentArgs.DownloadContent, getContentArgs.DownloadThumbnail);
            return content;
        }
        else
        {
            throw new UgcException(response.Content, response.ErrorMessage, response.ErrorException);
        }
    }

Download Content Method: (error causing method)

public async Task DownloadContentDataAsync(Content content, bool downloadContent, bool downloadThumbnail)
    {
        Validate();

        if (downloadContent) // just keep getting auth errors, both RestSharp and normal don't work
        {
            using var httpClient = new System.Net.Http.HttpClient();
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
                "Bearer",
                AuthenticationService.Instance.AccessToken
            );

            var response = await httpClient.GetAsync(content.DownloadUrl);
            if (response.IsSuccessStatusCode)
            {
                content.DownloadedContent = await response.Content.ReadAsByteArrayAsync();
            }
            else
            {
                throw new UgcException(await response.Content.ReadAsStringAsync(), response.ReasonPhrase, null);
            }
        }

        // if (downloadContent)
        // {
        //     var request = new RestRequest(content.DownloadUrl) { RequestFormat = DataFormat.Json };
        //     GD.Print(content.DownloadUrl);

        //     var response = await ugcClient.ExecuteAsync(request);
        //     if (response.IsSuccessful)
        //         content.DownloadedContent = response?.RawBytes ?? null;
        //     else
        //         throw new UgcException(response.Content, response.ErrorMessage, response.ErrorException);
        // }

        if (downloadThumbnail && !string.IsNullOrEmpty(content.ThumbnailUrl))
        {
            var request = new RestRequest(content.ThumbnailUrl) { RequestFormat = DataFormat.Json };

            var response = await ugcClient.ExecuteAsync(request);
            if (response.IsSuccessful)
                content.DownloadedThumbnail = response?.RawBytes ?? null;
            else
                throw new UgcException(response.Content, response.ErrorMessage, response.ErrorException);
        }
    }

No idea what is causing this. Here’s the repo (line 746) if you want to go further.

  • Jesusemora and JusTiCe8 replied to this.
  • My god. I have the answer after searching and logging many resources (even Unity's source). Remove any JWT auth headers. They break it. It's super weird, I thought they would just be ignored.

    var restClient = new RestClient();
    
            if (downloadContent)
            {
                var request = new RestRequest(content.DownloadUrl) { RequestFormat = DataFormat.Json };
    
                var response = await restClient.ExecuteAsync(request);
                if (response.IsSuccessful)
                    content.DownloadedContent = response?.RawBytes ?? null;
                else
                    throw new UgcException(response.Content, response.ErrorMessage, response.ErrorException);
            }
    
            if (downloadThumbnail && !string.IsNullOrEmpty(content.ThumbnailUrl))
            {
                var request = new RestRequest(content.ThumbnailUrl) { RequestFormat = DataFormat.Json };
    
                var response = await restClient.ExecuteAsync(request);
                if (response.IsSuccessful)
                    content.DownloadedThumbnail = response?.RawBytes ?? null;
                else
                    throw new UgcException(response.Content, response.ErrorMessage, response.ErrorException);
            }

    Tirt the service using HTTP requests

    all modern sites should be HTTPS if I'm not mistaken, I can't even go into an HTTP site without firefox throwing a warning.

      Jesusemora all modern sites should be HTTPS if I'm not mistaken, I can't even go into an HTTP site without firefox throwing a warning.

      It's irrelevant here, code use generic HTTP request object/class and it's up to the dev to set-up security correctly, HTTPS need certificate and handshake first to work.

      Tirt No idea what is causing this

      I advise you to dump request URLs & parameters and compare one which work, maybe better with sniffer like WireShark, with one which fail, with the code alone, it needs someone who most likely do the same thing to get an answer. Auth error usually are results of missing parameter(s)/bad setup, especially with headers.

      • Tirt replied to this.

        JusTiCe8 That's what is so weird about the error. I have already implemented Unity's cloud save service and successfully downloaded files in the same way, send a request (line 216) with informational queries and headers, get a response in JSON, serialize it, the information includes a download URL (relating to google), required headers and an HTTP method.

        Put all that in a new request and receive a byte array. In the cloud save implementation, required headers aren't required to download, only upload. I have printed information retaining to downloading in user-generated content and ONLY a download URL is provided, nothing else.

        I have looked at Unity's implementation for their engine and nothing points to me missing something with the actual request itself.

        My god. I have the answer after searching and logging many resources (even Unity's source). Remove any JWT auth headers. They break it. It's super weird, I thought they would just be ignored.

        var restClient = new RestClient();
        
                if (downloadContent)
                {
                    var request = new RestRequest(content.DownloadUrl) { RequestFormat = DataFormat.Json };
        
                    var response = await restClient.ExecuteAsync(request);
                    if (response.IsSuccessful)
                        content.DownloadedContent = response?.RawBytes ?? null;
                    else
                        throw new UgcException(response.Content, response.ErrorMessage, response.ErrorException);
                }
        
                if (downloadThumbnail && !string.IsNullOrEmpty(content.ThumbnailUrl))
                {
                    var request = new RestRequest(content.ThumbnailUrl) { RequestFormat = DataFormat.Json };
        
                    var response = await restClient.ExecuteAsync(request);
                    if (response.IsSuccessful)
                        content.DownloadedThumbnail = response?.RawBytes ?? null;
                    else
                        throw new UgcException(response.Content, response.ErrorMessage, response.ErrorException);
                }