slim-basic-auth icon indicating copy to clipboard operation
slim-basic-auth copied to clipboard

Basic case-insensitive and PHP_AUTH_USER

Open Cyclenerd opened this issue 7 years ago • 4 comments

Hi,

I have the following code:

$app->add(new \Slim\Middleware\HttpBasicAuthentication([
	"path" => ["/auth", "/user", "/search"],
	"realm" => "Protected",
	"authenticator" => new PdoAuthenticator([
		"pdo" => $authenticator_pdo,
		"table" => "users",
		"user" => "username",
		"hash" => "password_hash"
	]),
	"callback" => function ($request, $response, $arguments) {
		print_r($arguments);
	},
	"error" => function ($request, $response, $arguments) {
		return $response->withJson(array('error' => 'AUTHENTICATION_FAILED'), 403);
	}
]));

// Check HTTP Basic Authentication
$app->get('/auth', function ($request, $response, $args) {
	$auth_username = $_SERVER['PHP_AUTH_USER'];
	// Return
	return $response->withJson(array(
		'username' => $auth_username,
		'status' => 'OK'
	), 200);
});

If I pass in the header "Authorization: Basic" (upper case B) the authentication is successful and PHP_AUTH_USER is set:

curl 'http://localhost:8080/auth' -H 'Authorization: Basic bmlsczp0ZXN0MTIzNA=='
Array
(
    [user] => nils
    [password] => test1234
)
{"username":"nils","status":"OK"}

If I pass in the header "Authorization: basic" (lowercase letter b) the authentication is successful and PHP_AUTH_USER is not set.

curl 'http://localhost:8080/auth' -H 'Authorization: basic bmlsczp0ZXN0MTIzNA=='
Array
(
    [user] => nils
    [password] => test1234
)
{"username":null,"status":"OK"}

When I remove the case-insensitive (/i) Regular Expression in HttpBasicAuthentication.php then the authentication with basic (lowercase letter b) fails:

curl 'http://localhost:8080/auth' -H 'Authorization: basic bmlsczp0ZXN0MTIzNA=='
{"error":"AUTHENTICATION_FAILED"}

That would be better in my case. I am briefly overflown the RFCs. Basic is always written with (upper case B).

Best regards Nils

Cyclenerd avatar Feb 28 '17 08:02 Cyclenerd

According to RFC2617 the authentication scheme is case-insensitive.

HTTP provides a simple challenge-response authentication mechanism that MAY be used by a server to challenge a client request and by a client to provide authentication information. It uses an extensible, case-insensitive token to identify the authentication scheme, followed by a comma-separated list of attribute-value pairs which carry the parameters necessary for achieving authentication via that scheme.

PHP itself is quite picky about format of of headers though. What is the problem current behaviour causes you?

tuupola avatar Feb 28 '17 09:02 tuupola

Thanks for the super fast response and the clarification of the RFC.

My problem is that I trust the authentication and expect a PHP_AUTH_USER. I double check this now:

$check_auth_user = function ($request, $response, $next) {
	if (isset($_SERVER['PHP_AUTH_USER'])) {
		$response = $next($request, $response);
	} else {
		return $response->withJson(array('error' => 'AUTH_USER'), 403);
	}
	return $response;
};

// Check HTTP Basic Authentication
$app->get('/auth', function ($request, $response, $args) {
	$auth_username = $_SERVER['PHP_AUTH_USER'];
	// Return
	return $response->withJson(array(
		'username' => $auth_username,
		'status' => 'OK'
	), 200);
})->add($check_auth_user);

You're right. PHP is case sensitive.

basic - ❌ PHP_AUTH_USER:

curl 'http://localhost:8080/bla' -H 'Authorization: basic bmlsczp0ZXN0MTIzNA==' | jq
{
  "server": {
    "DOCUMENT_ROOT": "/Users/nils/Projekte/slim/livetracking/public",
    "REMOTE_ADDR": "127.0.0.1",
    "REMOTE_PORT": "52606",
    "SERVER_SOFTWARE": "PHP 5.6.28 Development Server",
    "SERVER_PROTOCOL": "HTTP/1.1",
    "SERVER_NAME": "127.0.0.1",
    "SERVER_PORT": "8080",
    "REQUEST_URI": "/bla",
    "REQUEST_METHOD": "GET",
    "SCRIPT_NAME": "/index.php",
    "SCRIPT_FILENAME": "/Users/nils/Projekte/slim/livetracking/public/index.php",
    "PATH_INFO": "/bla",
    "PHP_SELF": "/index.php/bla",
    "HTTP_HOST": "localhost:8080",
    "HTTP_USER_AGENT": "curl/7.52.1",
    "HTTP_ACCEPT": "*/*",
    "HTTP_AUTHORIZATION": "basic bmlsczp0ZXN0MTIzNA==",
    "REQUEST_TIME_FLOAT": 1488274696.9322,
    "REQUEST_TIME": 1488274696
  }
}

Basic - ✅ PHP_AUTH_USER:

curl 'http://localhost:8080/bla' -H 'Authorization: Basic bmlsczp0ZXN0MTIzNA==' | jq
{
  "server": {
    "DOCUMENT_ROOT": "/Users/nils/Projekte/slim/livetracking/public",
    "REMOTE_ADDR": "127.0.0.1",
    "REMOTE_PORT": "52608",
    "SERVER_SOFTWARE": "PHP 5.6.28 Development Server",
    "SERVER_PROTOCOL": "HTTP/1.1",
    "SERVER_NAME": "127.0.0.1",
    "SERVER_PORT": "8080",
    "REQUEST_URI": "/bla",
    "REQUEST_METHOD": "GET",
    "SCRIPT_NAME": "/index.php",
    "SCRIPT_FILENAME": "/Users/nils/Projekte/slim/livetracking/public/index.php",
    "PATH_INFO": "/bla",
    "PHP_SELF": "/index.php/bla",
    "HTTP_HOST": "localhost:8080",
    "HTTP_USER_AGENT": "curl/7.52.1",
    "HTTP_ACCEPT": "*/*",
    "HTTP_AUTHORIZATION": "Basic bmlsczp0ZXN0MTIzNA==",
   "PHP_AUTH_USER": "nils",
    "PHP_AUTH_PW": "test1234",
    "REQUEST_TIME_FLOAT": 1488274708.5842,
    "REQUEST_TIME": 1488274708
  }
}

If the standard says that basic is OK then the error is in PHP.

Cyclenerd avatar Feb 28 '17 09:02 Cyclenerd

You could try to set PHP_AUTH_USER in the callback.

$app->add(new \Slim\Middleware\HttpBasicAuthentication([
       ...
	"callback" => function ($request, $response, $arguments) {
		$_SERVER['PHP_AUTH_USER'] = $arguments['username'];
	}
]));

That said middleware could actually do that by default if authentication succeeds and PHP_AUTH_USER is still null.

tuupola avatar Feb 28 '17 10:02 tuupola

Good idea. This works for me. Thank you very much.

Cyclenerd avatar Feb 28 '17 10:02 Cyclenerd