Search code examples
rubyoauth-2.0github-api

How to get authenticated API request limits from the GitHub API using Ruby


I'm trying to update an old Ruby app that was using the recently (2021-08-08) Deprecated Authentication method used by GitHub API to be able to make less limited requests. Before the OATH2 authentication token(s) was supplied in the URL, but now they have to be supplied in the request header.

I have tried several methods, but as I am not a Ruby programmer, I am not able to solve the problem.

Using curl for doing this is trivial and works and have example in the GitHub documentation pages.

# Simple, should give a rate limit of: 60
curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/rate_limit

# Authenticated, should give a rate limit of: 5000
curl -H "Accept: application/vnd.github.v3+json" -H "Authorization: token <MY-40-CHAR-TOKEN>" https://api.github.com/rate_limit

Using Ruby is another story as it seem a lot of Ruby things also have changed since.


require 'net/http'

#-----------------------------------------------------
# Test-1
#-----------------------------------------------------
uri = URI("https://api.github.com/rate_limit")
params = { :Authorization => "token <MY-40-CHAR-TOKEN>", :Accept => "application/vnd.github.v3+json" }
uri.query = URI.encode_www_form(params)

res = Net::HTTP.get_response(uri)
puts res.body if res.is_a?(Net::HTTPSuccess)


#-----------------------------------------------------
# Test-2
#-----------------------------------------------------
uri = URI.parse("https://api.github.com/rate_limit")
http = Net::HTTP.new(uri.host, uri.port)

req = Net::HTTP::Get.new(uri.request_uri)
req["User-Agent"] = "my-ruby-script"
req["Accept"] = "application/vnd.github.v3+json" 
req["Authorization"] = "token <MY-40-CHAR-TOKEN>" 

res = http.request(req)
res["content-type"]

response.each_header do |key, value|
  p "#{key} => #{value}"
end

puts res.body


#-----------------------------------------------------
# Test-3
#-----------------------------------------------------
uri = URI.parse('https://api.github.com/rate_limit')
http = Net::HTTP.new(uri.host, uri.port)
res = http.request_get(uri, 'User-Agent': 'myscript' , 'Accept': 'application/vnd.github.v3+json', 'Authorization': 'token <MY-40-CHAR-TOKEN>')


Those all gave useless errors... Then someone posted that you may need to use:

Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https')

The code I'm trying to get to work is:


#-----------------------------------------------------
# Test-4
#-----------------------------------------------------
require 'net/http'
require 'json'

hLine = "-"*60

    if (1)
        access_token = "<MY-40-CHAR-TOKEN>"

        if (access_token)
            puts "Using access_token: #{access_token}" 
            
            # :headers => { "Authorization" => "token " + access_token },
            uri = URI.parse("https://api.github.com/rate_limit")
            req = Net::HTTP::Get.new(uri)
       
            req["User-Agent"] = "myscript"
            req["Accept"] = "application/vnd.github.v3+json" 
            req["Authorization"] = "token #{access_token}" 
            res = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https')   
        else 
            uri = URI.parse("https://api.github.com/rate_limit")
        end

        res = Net::HTTP.get_response(uri) 
        
        if (res.message != "OK")    # 200
            puts "ERROR: Bad response code: #{res.code}\n"
            puts res.body
        else
            debug = 1
            if (debug)
                puts hLine + "\nResponse Headers:\n" + hLine
                puts "#{res.to_hash.inspect}\n"
                puts hLine + "\nBody:\n" + hLine
                puts "#{res.body}\n" + hLine
            end

            rbr = JSON.parse(res.body)['resources']['core']
            RTc = Time.at(rbr['reset'])
            puts "\nCore"
            puts "  Rate limit   : #{rbr['limit']}"
            puts "  Remaining    : #{rbr['remaining']}"
            puts "  Refresh at   : #{RTc}"

        end
    end


# OUTPUT:
#----------------------------------------------
# Core
#  Rate limit   : 60
#  Remaining    : 59
#  Refresh at   : 2022-01-25 20:40:52 +0200

So the token is never applied.

The entire program is also using oktokit, so a solution using that may also be useful.

How can I get this to work in Ruby with the new GitHub API changes?


Solution

  • The issue seem to have more to do with poor Ruby documentation maintenance, it seem mostly obscured with outdated solutions. The other most common issue is, that in the examples I gave (found) they never handle SSL (HTTPS) properly. There is no successful GitHub interaction without SSL. In addition it is stated that they also don't accept an empty User-Agent in the request header.

    The correct (highly simplified) solution is the following:

    require 'net/http'    
    
    url = URI("https://api.github.com/rate_limit")
    http = Net::HTTP.new(url.host, url.port)
    http.use_ssl = true
    
    req = Net::HTTP::Get.new(url)     
    req["Authorization"] = 'token <MY-40-CHAR-TOKEN>'
    res = http.request(req) 
    
    puts res.read_body
    

    Replacing the code in OP in the appropriate places fixes this.


    For future reference, if you need to convert a curl into Ruby's net/http format, use this site: