1 require 'rack/auth/abstract/handler'
2 require 'rack/auth/digest/request'
3 require 'rack/auth/digest/params'
4 require 'rack/auth/digest/nonce'
10 # Rack::Auth::Digest::MD5 implements the MD5 algorithm version of
11 # HTTP Digest Authentication, as per RFC 2617.
13 # Initialize with the [Rack] application that you want protecting,
14 # and a block that looks up a plaintext password for a given username.
16 # +opaque+ needs to be set to a constant base64/hexadecimal string.
18 class MD5
< AbstractHandler
22 attr_writer
:passwords_hashed
26 @passwords_hashed = nil
34 auth
= Request
.new(env)
40 if !auth
.digest
? || !auth
.correct_uri
? || !valid_qop
?(auth
)
46 return unauthorized(challenge(:stale => true))
48 env['REMOTE_USER'] = auth
.username
63 Params
.new
do |params
|
64 params
['realm'] = realm
65 params
['nonce'] = Nonce
.new
.to_s
66 params
['opaque'] = H(opaque
)
69 hash
.each
{ |k
, v
| params
[k
] = v
}
73 def challenge(hash
= {})
74 "Digest #{params(hash)}"
78 valid_opaque
?(auth
) && valid_nonce
?(auth
) && valid_digest
?(auth
)
85 def valid_opaque
?(auth
)
86 H(opaque
) == auth
.opaque
89 def valid_nonce
?(auth
)
93 def valid_digest
?(auth
)
94 digest(auth
, @authenticator.call(auth
.username
)) == auth
.response
98 ::Digest::MD5.hexdigest(data)
104 H([secret
, data] * ':')
107 def A1(auth
, password
)
108 [ auth
.username
, auth
.realm
, password
] * ':'
112 [ auth
.method
, auth
.uri
] * ':'
115 def digest(auth
, password
)
116 password_hash
= passwords_hashed
? ? password
: H(A1(auth
, password
))
118 KD(password_hash
, [ auth
.nonce
, auth
.nc
, auth
.cnonce
, QOP
, H(A2(auth
)) ] * ':')