Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/grape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module Middleware
module Auth
autoload :OAuth2, 'grape/middleware/auth/oauth2'
autoload :Basic, 'grape/middleware/auth/basic'
autoload :Digest, 'grape/middleware/auth/digest'
end
end
end
Expand Down
10 changes: 9 additions & 1 deletion lib/grape/api.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'rack/mount'
require 'rack/auth/basic'
require 'rack/auth/digest/md5'
require 'logger'

module Grape
Expand Down Expand Up @@ -138,7 +139,7 @@ def helpers(&block)
end

# Add an authentication type to the API. Currently
# only `:http_basic` and `:oauth2` are supported.
# only `:http_basic`, `:http_digest` and `:oauth2` are supported.
def auth(type = nil, options = {}, &block)
if type
set(:auth, {:type => type.to_sym, :proc => block}.merge(options))
Expand All @@ -155,6 +156,12 @@ def http_basic(options = {}, &block)
options[:realm] ||= "API Authorization"
auth :http_basic, options, &block
end

def http_digest(options = {}, &block)
options[:realm] ||= "API Authorization"
options[:opaque] ||= "secret"
auth :http_digest, options, &block
end

# Defines a route that will be recognized
# by the Grape API.
Expand Down Expand Up @@ -257,6 +264,7 @@ def build_endpoint(&block)
:format => settings[:error_format] || :txt,
:rescue_options => settings[:rescue_options]
b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
b.use Rack::Auth::Digest::MD5, settings[:auth][:realm], settings[:auth][:opaque], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_digest
b.use Grape::Middleware::Prefixer, :prefix => prefix if prefix
b.use Grape::Middleware::Versioner, :versions => (version if version.is_a?(Array)) if version
b.use Grape::Middleware::Formatter, :default_format => default_format || :json
Expand Down
30 changes: 30 additions & 0 deletions lib/grape/middleware/auth/digest.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require 'rack/auth/digest/md5'

module Grape
module Middleware
module Auth
class Digest < Grape::Middleware::Base
attr_reader :authenticator

def initialize(app, options = {}, &authenticator)
super(app, options)
@authenticator = authenticator
end

def digest_request
Rack::Auth::Digest::Request.new(env)
end

def credentials
digest_request.provided?? digest_request.credentials : [nil, nil]
end

def before
unless authenticator.call(*credentials)
throw :error, :status => 401, :message => "API Authorization Failed."
end
end
end
end
end
end
49 changes: 49 additions & 0 deletions spec/grape/middleware/auth/digest_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require 'spec_helper'

RSpec::Matchers.define :be_challenge do
match do |actual_response|
actual_response.status == 401 &&
actual_response['WWW-Authenticate'] =~ /^Digest / &&
actual_response.body.empty?
end
end

class Test < Grape::API
version '1'

http_digest({:realm => 'Test Api', :opaque => 'secret'}) do |username|
{'foo' => 'bar'}[username]
end

get '/test' do
[{:hey => 'you'},{:there => 'bar'},{:foo => 'baz'}]
end
end

describe Grape::Middleware::Auth::Digest do
def app
Test
end

it 'should be a digest authentication challenge' do
get '/1/test'
last_response.should be_challenge
end

it 'should throw a 401 if no auth is given' do
get '/1/test'
last_response.status.should == 401
end

it 'should authenticate if given valid creds' do
digest_authorize "foo", "bar"
get '/1/test'
last_response.status.should == 200
end

it 'should throw a 401 if given invalid creds' do
digest_authorize "bar", "foo"
get '/1/test'
last_response.status.should == 401
end
end