Question

I am working on creating an API for my ruby application that authenticates users based on HTTP Digest Authentication. I decided to use the Grape API library because it makes creating an API cleaner in ruby. The Grape documentation states that you can use Digest Authentication like:

http_digest({ :realm => 'Test Api', :opaque => 'app secret' }) do |username|
  # lookup the user's password here
  { 'user1' => 'password1' }[username]
end

The Grape implementation above is a wrapper for Rack::Auth::Digest::MD5

Now also for security i read that as of RFC 2617 you don't need to store the password as plain text in the database you store an MD5 digest of the username:realm:password and authticate against that so i created a DataMapper model:

class Key
  include DataMapper::Resource

  property :id,              Serial

  property :username,        String
  property :password,        String

  property :active,          Boolean, :default => true
  property :created_at,      DateTime, :default => DateTime.now
  property :updated_at,      DateTime
end

Now with what I provided, I am lost as to how to connect these two and make it work.

Was it helpful?

Solution

Unfortunately, Rack::Auth::Digest::MD5 expects a plaintext password on the server side.

The Grape example code shows a hard-coded lookup of password.

You could replace { 'user1' => 'password1' }[username] with

Key.first( :username => username ).password

provided you stored plaintext passwords in the Key class. You could store these reversibly-encrypted I suppose, although that doesn't add much security unless you construct relatively complex/costly schemes for key management.

Not sure if there is a way around this that would let you store hashed passwords. MD5 isn't the most secure hashing choice (although better than nothing!). If security is an important concern for your API, you will want to look beyond digest auth - using https would help, for example.

Edit: Following a bit of to-and-fro in discussions, the following variation of Grape's example does allow you to store the MD5'd password:

auth :http_digest, { :realm => { :realm => 'Llama', :passwords_hashed => true, :opaque => "7302c32d39bbacb5ed0ace096723fd" } } do |username|
  Digest::MD5.hexdigest( 'fred:Llama:654321' )
end

The example gives a hard-coded username:'fred', password:'654321' response. So I think your target code is something like:

auth :http_digest, { :realm => { :realm => 'Llama', :passwords_hashed => true, :opaque => "7302c32d39bbacb5ed0ace096723fd" } } do |username|
  k = Key.first( :username => username )
  k ? k.password : nil
end

And you store the result of Digest::MD5.hexdigest( "#{username}:#{realm}:#{password}" ) in each user's password property.

Note the double-level hash with :realm twice. This is a bit hacky, but at least you don't have to write your own middleware, Grape is still dealing with it. This is not a documented feature of Grape or covered with tests, so may not work in future versions.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top