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