Skip to content

Commit cf20851

Browse files
author
Josh Lockhart
committed
Add HTTP digest authentication middleware
1 parent 74b8b7f commit cf20851

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

Middleware/HttpDigestAuth.php

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?php
2+
/**
3+
* HTTP Digest Authentication
4+
*
5+
* Use this middleware with your Slim Framework application
6+
* to require HTTP digest auth for all routes.
7+
*
8+
* Much of this code was created using <http://php.net/manual/en/features.http-auth.php>
9+
* as a reference. I do not claim ownership or copyright on this code. This
10+
* derivative class is provided under the MIT public license.
11+
*
12+
* @author Josh Lockhart <[email protected]>
13+
* @version 1.0
14+
*
15+
* USAGE
16+
*
17+
* $app = new Slim();
18+
* $app->add(new HttpDigestAuth('theUsername', 'thePassword'));
19+
*
20+
* MIT LICENSE
21+
*
22+
* Permission is hereby granted, free of charge, to any person obtaining
23+
* a copy of this software and associated documentation files (the
24+
* "Software"), to deal in the Software without restriction, including
25+
* without limitation the rights to use, copy, modify, merge, publish,
26+
* distribute, sublicense, and/or sell copies of the Software, and to
27+
* permit persons to whom the Software is furnished to do so, subject to
28+
* the following conditions:
29+
*
30+
* The above copyright notice and this permission notice shall be
31+
* included in all copies or substantial portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
34+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
35+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
36+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
37+
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
38+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
39+
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40+
*/
41+
class HttpDigestAuth extends Slim_Middleware {
42+
/**
43+
* @var string
44+
*/
45+
protected $username;
46+
47+
/**
48+
* @var string
49+
*/
50+
protected $password;
51+
52+
/**
53+
* @var string
54+
*/
55+
protected $realm;
56+
57+
/**
58+
* Constructor
59+
*
60+
* @param string $username The HTTP Authentication username
61+
* @param string $password The HTTP Authentication password
62+
* @param string $realm The HTTP Authentication realm
63+
* @return void
64+
*/
65+
public function __construct( $username, $password, $realm = 'Protected Area' ) {
66+
$this->username = $username;
67+
$this->password = $password;
68+
$this->realm = $realm;
69+
}
70+
71+
/**
72+
* Call
73+
*
74+
* This method will check the HTTP request headers for previous authentication. If
75+
* the request has already authenticated, the next middleware is called. Otherwise,
76+
* a 401 Authentication Required response is returned to the client.
77+
*
78+
* @return void
79+
*/
80+
public function call() {
81+
//Check header and header username
82+
if ( empty($_SERVER['PHP_AUTH_DIGEST']) ) {
83+
$this->fail();
84+
return;
85+
} else {
86+
$data = $this->parseHttpDigest($_SERVER['PHP_AUTH_DIGEST']);
87+
if ( !$data || $data['username'] !== $this->username ) {
88+
$this->fail();
89+
return;
90+
}
91+
}
92+
93+
//Check header response
94+
$A1 = md5($data['username'] . ':' . $this->realm . ':' . $this->password);
95+
$A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']);
96+
$validResponse = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
97+
if ( $data['response'] !== $validResponse ) {
98+
$this->fail();
99+
return;
100+
}
101+
102+
//By this point the request is authenticated
103+
$this->next->call();
104+
}
105+
106+
/**
107+
* Require Authentication from HTTP Client
108+
*
109+
* @return void
110+
*/
111+
protected function fail() {
112+
$this->app->response()->status(401);
113+
$this->app->response()->header('WWW-Authenticate', sprintf('Digest realm="%s",qop="auth",nonce="%s",opaque="%s"', $this->realm, uniqid(), md5($this->realm)));
114+
}
115+
116+
/**
117+
* Parse HTTP Digest Authentication header
118+
*
119+
* @return array|false
120+
*/
121+
protected function parseHttpDigest( $headerValue ) {
122+
$needed_parts = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
123+
$data = array();
124+
$keys = implode('|', array_keys($needed_parts));
125+
preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $headerValue, $matches, PREG_SET_ORDER);
126+
foreach ( $matches as $m ) {
127+
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
128+
unset($needed_parts[$m[1]]);
129+
}
130+
return $needed_parts ? false : $data;
131+
}
132+
}

0 commit comments

Comments
 (0)