Search code examples
rubyhttpsuri23andme-api

How to POST to exchange a 23andme auth code for a token


The 23andme web site has an API and they give the following instructions:

Send a POST /token/ request with these parameters (client_id and client_secret are on your dashboard):

curl https://api.23andme.com/token/
 -d client_id=xxx \
 -d client_secret=yyy \
 -d grant_type=authorization_code \
 -d code=zzz \
 -d "redirect_uri=https://localhost:5000/receive_code/"
 -d "scope=basic%20rs3094315"

If successful, you'll get a 200 JSON response like this:

{"access_token":"89822b93d2",
 "token_type":"bearer",
 "expires_in": 86400,
 "refresh_token":"33c53cd7bb",
 "scope":"rs3094315 basic"}

So, here's what I tried, but it didn't work. I know Ruby, but have never used net/http or uri.

def self.get_token(code)
    uri = URI.parse("https://api.23andme.com/token")
    https = Net::HTTP.new(uri.host, uri.port)
    https.use_ssl = true

    request = Net::HTTP::Post.new(uri.path)

    request["client_id"]     = 'my client id goes here'
    request["client_secret"] = 'my client secret goes here'
    request["grant_type"]    = 'authorization_code'
    request["code"]          = code
    request["redirect_uri"]  = 'http://localhost:3000'
    request["scope"]         = 'names basic haplogroups relatives'

    response = https.request(request)
    return response.to_s
end

Solution

  • You are doing it right using curl with the -d option to set the parameter in the body of the POST request. However, with the Net::HTTP::Post object, the syntax request["key"] = value is used to set the headers of the Http object.

    Use set_form_data to set all the parameters into the body of the POST request.

    For example, use it in this way:

    request.set_form_data({"client_id" => "my client id goes here", "code" => code})
    

    The following will clarify the above:

    $ > request["client_id"] = 'my client id goes here'
    # => "my client id goes here" 
    $ > request.body
    # => nil # body is nil!!!
    $ > request.each_header { |e| p e }
    # "client_id"
    # => {"client_id"=>["my client id goes here"]} 
    
    $ > request.set_form_data("client_secret" => 'my client secret goes here')
    # => "application/x-www-form-urlencoded" 
    $ > request.body
    # => "client_secret=my+client+secret+goes+here" # body contains the added param!!!