Requests icon indicating copy to clipboard operation
Requests copied to clipboard

How to perform Digest Authentication With Requests

Open devahsancreative opened this issue 9 years ago • 3 comments

$user = 'myusername';
$pass = 'mypass';
$BaseApiUrl     = 'myurl';

$headers = array('Accept' => 'application/json');
$options = array('auth' => new Requests_Auth_Basic(array($user, $pass)));
$request = Requests::get($BaseApiUrl, $headers, $options);
var_dump($request->status_code);
var_dump($request->body);

This is my code it gives error int(401) string(28) "HTTP Digest: Access denied. "

You have Basic/Digest Authentication

please provide step for Digest Authentication

devahsancreative avatar Oct 14 '16 05:10 devahsancreative

It doesn't look like Requests has Digest support at all at present (the readme is the only reference to it). Depending on your api's Digest implementation (there's a few different implementations of it) you should be able to build the Authorization header directly yourself and pass it to Requests though (but it may require you to first do a HEAD against the site to get all the digest details to compute the hash).

(Note: If it's your own server, keep in mind that Digest Authentication isn't considered more secure than Basic + SSL, as most clients can be MITM'd to fall back to Basic Auth)

dd32 avatar Oct 17 '16 04:10 dd32

Related #32

soulseekah avatar Feb 17 '18 06:02 soulseekah

Here is an example for the Digest Authentication. You need to create the auth header by yourself and that only works, if you fetch the auth request headers with an additional http request (the first wp_remote_get() call). The second call is the actual request you want to sent. The solution is based on this blog post with another regexp I found here

$url='?';
$username ='?';
$password ='?';

$response = wp_remote_get( $url, $request );
$header   = wp_remote_retrieve_header( $response, 'www-authenticate' );
if ( empty( $header ) ) {
	// handle error;
}

$matches     = [];
$server_bits = [];

/*
* Parses the 'www-authenticate' header for nonce, realm and other values.
*
* $header looks like
* Digest realm="your authentication",qop="auth",nonce="611...552",opaque="a707ae2edc...2f8d80a9af0fd335"
*/
preg_match_all(
	'#(realm|nonce|qop|opaque)=(?:([\'"])([^\2]+?)\2|([^\s,]+))#',
	$header,
	$matches,
	PREG_SET_ORDER
);

foreach ( $matches as $match ) {
	$server_bits[ $match[1] ] = $match[3] ?? $match[4];
}

$nc           = '00000001';
$path         = parse_url( $url, PHP_URL_PATH );
$client_nonce = uniqid();
$ha1          = md5( $username . ':' . $server_bits['realm'] . ':' . $password );
$ha2          = md5( 'GET:' . $path );

// The order of this array matters, because it affects resulting hashed val
$response_bits = [
	$ha1,
	$server_bits['nonce'],
	$nc,
	$client_nonce,
	$server_bits['qop'],
	$ha2
];

$digest_header_values = [
	'username' => sprintf( 'username="%s"', $username ),
	'realm'    => sprintf( 'realm="%s"', $server_bits['realm'] ),
	'nonce'    => sprintf( 'nonce="%s"', $server_bits['nonce'] ),
	'uri'      => sprintf( 'uri="%s"', $path ),
	'response' => sprintf( 'response="%s"', md5( implode( ':', $response_bits ) ) ),
	'opaque'   => sprintf( 'opaque="%s"', $server_bits['opaque'] ),
	'qop'      => sprintf( 'qop=%s', $server_bits['qop'] ),
	'nc'       => sprintf( 'nc=%s', $nc ),
	'cnonce'   => sprintf( 'cnonce="%s"', $client_nonce )
];

$digestHeader = implode( ', ', $digest_header_values );

$request  = [
	'headers' => [
		'Authorization' => 'Digest ' . $digestHeader,
	],
];
$response = wp_remote_get( $url, $request );

Probably there are other headers missing for your webserver configuration. You can add them to the regexp.

mmethner avatar Aug 19 '21 09:08 mmethner