Add api key to every request in ActiveResource
Asked Answered
F

7

20

I have 2 RESTful Rails apps I'm trying to make talk to each other. Both are written in Rails 3 (beta3 at the moment). The requests to the service will require the use an api key which is just a param that needs to be on every request. I can't seem to find any information on how to do this.

You define the url the resource connects to via the site= method. There should be an equivalent query_params= method or similar.

There is one good blog post I found related to this and it's from October 2008, so not exactly useful for Rails 3.

Update: I had a thought. Would a small Rack middleware or Metal be the answer to this? It could just pass through the request, tacking it's api_key on.

Fraunhofer answered 27/5, 2010 at 4:16 Comment(3)
Sorry Nicolas, no I did not find a solution. I think the answer is to manually assign the api key in every single request.Fraunhofer
@Nicolas I realize this question is relatively old, but see if my solution does what you want.Luxuriance
Thanks Kelvin. Your solution was closest to what I was looking for.Fraunhofer
L
23

Use model#prefix_options which is a hash for passing params into query string (or even as substitions for parts of the Model.prefix, e.g. "/myresource/:param/" will be replaced by the value of prefix_options[:param] . Any hash keys not found in the prefix will be added to the query string, which is what we want in your case).

class Model < ActiveResource::Base
  class << self
    attr_accessor :api_key
  end

  def save
    prefix_options[:api_key] = self.class.api_key
    super
  end
end

Model.site = 'http://yoursite/'
Model.api_key = 'xyz123'
m = Model.new(:field_1 => 'value 1')
# hits http://yoursite:80/models.xml?api_key=xyz123
m.save
Luxuriance answered 14/4, 2011 at 7:9 Comment(3)
Now this is what I'm talking about. I suppose you could open up ActiveResource::Base and add these methods too and then it would occur on all requests automatically.Fraunhofer
This would only fix save. Only solution that handles it globally is the one suggested below by @benny-degezelleMiguelinamiguelita
m = Model.new m.prefix_options[:api_key] = 'xyz123'Metathesis
F
10

I recently was faced with a similar issue, if you are on Rails3, it supports using custom header which makes life much easier for these situations.

On the side you are making the request from, add

headers['app_key'] = 'Your_App_Key'

to the class you are inheriting from ActiveResource::Base

On you are server, for Authentication, simply receive it as

request.headers['HTTP_APP_KEY']

For Example:

class Magic < ActiveResource::Base
    headers['app_key'] = 'Your_App_Key'
end

now Magic.get, Magic.find, Magic.post will all send the app_key

Fatimahfatimid answered 29/9, 2011 at 10:50 Comment(1)
I used a word with underscore _ and it did not work. When I changed to - hyphen it did. I hope this saves some hours to someone. The weird thing is that I remember it worked with _ in development, error happened in production.Outset
G
4

I have much nicer solution ! I try with Rack in middleware but i no find any solution in this way....

I propose you this module for override methods of ActiveReouse::Base

Add this lib in /lib/active_resource/extend/ directory don't forget uncomment
"config.autoload_paths += %W(#{config.root}/lib)" in config/application.rb

module ActiveResource #:nodoc:
  module Extend
    module AuthWithApi
      module ClassMethods
        def element_path_with_auth(*args)
          element_path_without_auth(*args).concat("?auth_token=#{self.api_key}")
        end
        def new_element_path_with_auth(*args)
          new_element_path_without_auth(*args).concat("?auth_token=#{self.api_key}")
        end
        def collection_path_with_auth(*args)
          collection_path_without_auth(*args).concat("?auth_token=#{self.api_key}")
        end
      end

      def self.included(base)
        base.class_eval do
          extend ClassMethods
          class << self
            alias_method_chain :element_path, :auth
            alias_method_chain :new_element_path, :auth
            alias_method_chain :collection_path, :auth
            attr_accessor :api_key
          end
        end
      end  
    end
  end  
end

in model

class Post < ActiveResource::Base
  include ActiveResource::Extend::AuthWithApi

  self.site = "http://application.localhost.com:3000/"
  self.format = :json

  self.api_key = 'jCxKPj8wJJdOnQJB8ERy'

  schema do
    string  :title
    string  :content
  end

end
Garaway answered 25/5, 2011 at 11:58 Comment(1)
I've created a class that I want to use as "base" for all my resources, but if I put the api_key inside it, the child class don't see it. Any suggestions?Trombley
I
3

Based on Joel Azemar's answer, but I had to make some changes.. First of all, in the active resource gem I used (2.3.8), there is no 'new_element_path', so aliasing that obviously failed.. Second, I updated the way the token is added to the query, because as was, it would break as soon as you add more params yourself. E.g. request for http://example.com/api/v1/clients.xml?vat=0123456789?token=xEIx6fBsxy6sKLJMPVM4 (notice ?token= i.o. &token=)

Here's my updated snippet auth_with_api.rb;

module ActiveResource #:nodoc:
  module Extend
    module AuthWithApi
      module ClassMethods
        def element_path_with_auth(id, prefix_options = {}, query_options = nil)
          query_options.merge!({:token => self.api_key})
          element_path_without_auth(id, prefix_options, query_options)
        end
        def collection_path_with_auth(prefix_options = {}, query_options = nil)
          query_options.merge!({:token => self.api_key})
          collection_path_without_auth(prefix_options, query_options)
        end
      end

      def self.included(base)
        base.class_eval do
          extend ClassMethods
          class << self
            alias_method_chain :element_path, :auth
            # alias_method_chain :new_element_path, :auth
            alias_method_chain :collection_path, :auth
            attr_accessor :api_key
          end
        end
      end  
    end
  end
end
Inchworm answered 6/3, 2012 at 11:30 Comment(0)
M
2

An Active Resource currently has no good way of passing an api key to the remote service. Passing api_key as a parameter will add it to the objects attributes on the remote service, I assume that this is not the behaviour you'd except. It certainly wasn't the behaviour I needed

Maribelmaribelle answered 14/1, 2011 at 9:55 Comment(0)
U
2

I'd recommend that you have a base class inheriting from ActiveResource::Base and override the self.collection_path and self.element_path class methods to always inject the API KEY something like:

class Base < ActiveResource::Base
  def self.collection_path(prefix_options = {}, query_options = {})
   super(prefix_options, query_options.merge(api_key: THE_API_KEY))
  end

  def self.element_path(id, prefix_options = {}, query_options = {})
    super(id, prefix_options, query_options.merge(api_key: THE_API_KEY))
  end
end

class User < Base; end

User.all # GET /users/?api_key=THE_API_KEY

This will always inject your API_KEY in your ActiveResource method calls.

Uncouth answered 29/4, 2016 at 11:8 Comment(0)
P
1

An Active Resource Object behaves much like a (simplified) Active Record object. If you wish to pass through a new param, then you can set it on the AR object by adding it as an attribute. eg:

jane = Person.create(:first => 'Jane', :last => 'Doe', :api_key => THE_API_KEY)

it should pass the api_key as a parameter, along with all the others.

Panhellenism answered 10/12, 2010 at 18:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.