Sinatra - API - Authentication
Asked Answered
T

3

59

We going to develop a little API application in Sinatra. What are the authentication options available to secure the API calls?

Thirtytwo answered 13/8, 2010 at 18:37 Comment(0)
I
92

Sinatra has no built-in authentication support. There are some gems available, but most are designed for user authentication (i.e. for a website). For an API, they seem like overkill. It’s easy enough to make your own. Simply check the request params in each of your routes to see if they contain a valid API key, and if not, return a 401 error.

helpers do
  def valid_key? (key)
    false
  end
end

get "/" do
  error 401 unless valid_key?(params[:key])

  "Hello, world."
end

#  $ irb -r open-uri
#  >> open("http://yourapp.com/api/?key=123")
#  OpenURI::HTTPError: 401 Unauthorized

Nothing after the call to error will happen if your valid_key? method returns false — error calls halt internally, which stops the request from continuing.

Of course, it’s not ideal to repeat the check at the beginning of each route. Instead, you can create a small extension that adds conditions to your routes:

class App < Sinatra::Base
  register do
    def check (name)
      condition do
        error 401 unless send(name) == true
      end
    end
  end

  helpers do
    def valid_key?
      params[:key].to_i % 2 > 0
    end
  end

  get "/", :check => :valid_key? do
    [1, 2, 3].to_json
  end
end

If you just want authentication on all your routes, use a before handler:

before do
  error 401 unless params[:key] =~ /^xyz/
end

get "/" do
  {"e" => mc**2}.to_json
end
Inheritrix answered 14/8, 2010 at 8:31 Comment(2)
Todd Yandell, thanks a lot for the detailed answer and for your time you spent on it. I really appreciate. It is really helps. ImranThirtytwo
Creating a small extension seems overkilling, a before filter is enough, since the latter has option as to which routes to apply. You can also tell that from within the body of the filter by request.path_info.Biased
R
2

http://www.secondforge.com/blog/2014/11/05/simple-api-authentication-in-sinatra/ has a slightly more detailed answer that uses user tokens.

This is one step more complicated than an API key, but is necessary if your API needs authentication to log in a user to do things such as editing a name/email/password, or accessing per-user information. (i.e. "private" API actions). You can also revoke/expire user tokens to let people log out, etc.

class App < Sinatra::Base

  before do
    begin
      if request.body.read(1)
        request.body.rewind
        @request_payload = JSON.parse request.body.read, { symbolize_names: true }
      end
    rescue JSON::ParserError => e
      request.body.rewind
      puts "The body #{request.body.read} was not JSON"
    end
  end

  post '/login' do
    params = @request_payload[:user]

    user = User.find(email: params[:email])
    if user.password == params[:password] #compare the hash to the string; magic
      #log the user in
    else
      #tell the user they aren't logged in
    end
  end
end

(It's worth to note that it's more common to read credentials from an HTTP header instead of the JSON body, but the author mentions that.)

Rendon answered 15/12, 2014 at 22:55 Comment(0)
D
2

Update

Nowadays Token Based Authentication are getting popular. I'd recommend to use the ruby implementation of the JWT Standard by ruby-jwt for simple authentication and authorization.

gem 'jwt'
Downhaul answered 1/2, 2019 at 12:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.