Search code examples
githubinno-setuppascalscripthttp-authentication

Sending Private access token (PAT) in Authentication header to download an archive for a private GitHub repository in Inno Setup script


I'm writing an install script that's supposed to download a private repository from GitHub:

if Pat <> '' then
begin
  Pat := Pat + '@'
end;

Url := Format('https://%sgithub.com/<MY_REPO>/%s/archive/main.zip', [Pat, RepoName]);

try
  DownloadTemporaryFile(Url, 'main.zip', '', @OnDownloadProgress);
except
  MsgBox('Failed to download the files from the GitHub repository.', mbError, MB_OK);
  Abort();
end;

For a public repository, I can set Pat to an empty string and this works without issue. However, for private repositories, I get a 404 response code. I've tried using a personal access token, and I've tried doing username:token as well. Furthermore, I have verified that the organization name and repository name match and, when trying the URL from a browser, I can download the file. I also have MFA enabled on my account. How can I make this work?

This script is part of an installer where the user enters their PAT, which allows them to download and install the code in the repository. Only people with a valid PAT can access the code.

For PAT, see Clone A Private Repository (Github).


Solution

  • After searching through other Stack Overflow questions, I found Get www/web content using HTTP request in Inno Setup, which showed me the hint I needed. This code relies on the WinHttpRequest object, to make the GET request with the required authentication, which is my GitHub Personal Access Token (PAT). This code works.

    const
      WinHttpRequestLib = 'WinHttp.WinHttpRequest.5.1';
    
    function ByteArrayToString(const ByteArray: array of Byte): AnsiString;
    var
      I: Integer;
    begin
      SetLength(Result, GetArrayLength(ByteArray));
      for I := 1 to GetArrayLength(ByteArray) do
        Result[I] := Chr(ByteArray[I - 1]);
    end;
    
    procedure GetGitHubArchive(Url, AccessToken: string);
    var
      Request:      Variant;
      ResponseBody: string;
    begin
      // First, load the OLE object associated with the WinHTTP library
      Request := CreateOleObject(WinHttpRequestLib);
    
      // Next, open a new GET request to the URL;
      // if we have a non-empty access token then add the
      // associated authorization header here
      Request.Open('GET', Url, false);
      if AccessToken <> '' then
      begin  
        Request.SetRequestHeader('Authorization', 'Bearer ' + AccessToken);
      end;
    
      // Finally, send the request; if we received a non-200 response code
      // then show the error.
      // Otherwise, extract the response body and write it to a file
      Request.Send;
      if Request.Status <> 200 then 
      begin
        MsgBox('Failed to download file: ' + Request.StatusText, mbError, MB_OK);
        Abort();
      end
      else
      begin
        ResponseBody := ByteArrayToString(Request.ResponseBody);
        SaveStringToFile(ExpandConstant('{tmp}\main.zip'), ResponseBody, False);
      end;
    end;