Closed klickers closed 4 years ago
We'll need at least two things to help make sense of this issue:
A backtrace of the exception. You can use Exception::getTrace()
to capture this and then var_dump()
it to share the raw output as a code snippet in a GitHub comment. That should also include any constructs/methods and their arguments, which should provide context for reproducing the problem.
The original arguments used to construct MongoDB\Driver\Manager
. This is the root connection option of the driver and, as noted in the constructor, it does not perform any IO on its own. The authentication exception will likely point back to the first database operation performed by your application, but we'll also want to see how the Manager was constructed. If you're using the mongodb/mongodb
Composer package atop the low-level driver, note that the MongoDB\Client
object basically wraps the driver's Manager object (most of the Client args are passed down to the driver). This may require some investigation on your end if you're relying on some Yii2 integration to construct the driver or library objects for you.
Lastly, I'll note that you're using version 1.6.0alpha1 of the driver. The most recent stable release is 1.5.3. I don't think that's relevant to your problem, as both versions happen to use the same versions of libmongoc (that will be bumped before the final 1.6.0 release); however, it's worth pointing this out and reminding you that any production application should stick to a stable release.
Cross-referencing this with what I believe is a duplicate thread on the mongodb-user listserv: https://groups.google.com/d/msg/mongodb-user/nVyDeZqFVQM/FOOwkYZxAwAJ
Here's my code in the controller (MVC):
$connection = new \yii\mongodb\Connection([
'dsn' => 'mongodb://@localhost:27017',
]);
$connection->open();
$database = $connection->getDatabase('fsa');
$collection = $database->getCollection('message');
$collection->insert(['sender_id' => $id, 'content' => '<p>randomContent</p>']);
$connection->close();
Code for the open() function - regarding the MongoDB\Driver\Manager - is at https://github.com/yiisoft/yii2-mongodb/blob/7f8d9862ac6c133d63afda171860ca9b61563d27/src/Connection.php#L344.
Stack Trace: (Full trace at https://app.box.com/s/u9rmr594nbrrec9yz9c1j6vh9fqoupbt - it was too long to copy-and-paste.)
An Error occurred while handling another error:
yii\web\HeadersAlreadySentException: Headers already sent in C:\xampp\htdocs\fsayii2\frontend\modules\dashboard\controllers\MessagesController.php on line 35. in C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\web\Response.php:366
Stack trace:
#0 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\web\Response.php(339): yii\web\Response->sendHeaders()
#1 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\web\ErrorHandler.php(135): yii\web\Response->send()
#2 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\base\ErrorHandler.php(111): yii\web\ErrorHandler->renderException(Object(yii\mongodb\Exception))
#3 [internal function]: yii\base\ErrorHandler->handleException(Object(yii\mongodb\Exception))
#4 {main}
Previous exception:
MongoDB\Driver\Exception\AuthenticationException: Authentication failed. in C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2-mongodb\src\Command.php:240
Stack trace:
#0 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2-mongodb\src\Command.php(240): MongoDB\Driver\Manager->executeBulkWrite('fsa.message', Object(MongoDB\Driver\BulkWrite), NULL)
#1 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2-mongodb\src\Command.php(496): yii\mongodb\Command->executeBatch('message', Array)
#2 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2-mongodb\src\Collection.php(266): yii\mongodb\Command->insert('message', Array, Array)
#3 C:\xampp\htdocs\fsayii2\frontend\modules\dashboard\controllers\MessagesController.php(44): yii\mongodb\Collection->insert(Array)
#4 [internal function]: app\modules\dashboard\controllers\MessagesController->actionInbox()
#5 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\base\InlineAction.php(57): call_user_func_array(Array, Array)
#6 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\base\Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#7 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\base\Module.php(528): yii\base\Controller->runAction('inbox', Array)
#8 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\web\Application.php(103): yii\base\Module->runAction('dashboard/messa...', Array)
#9 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\base\Application.php(386): yii\web\Application->handleRequest(Object(yii\web\Request))
#10 C:\xampp\htdocs\fsayii2\frontend\web\index.php(17): yii\base\Application->run()
#11 {main}
Next yii\mongodb\Exception: Authentication failed. in C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2-mongodb\src\Command.php:245
Stack trace:
#0 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2-mongodb\src\Command.php(496): yii\mongodb\Command->executeBatch('message', Array)
#1 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2-mongodb\src\Collection.php(266): yii\mongodb\Command->insert('message', Array, Array)
#2 C:\xampp\htdocs\fsayii2\frontend\modules\dashboard\controllers\MessagesController.php(44): yii\mongodb\Collection->insert(Array)
#3 [internal function]: app\modules\dashboard\controllers\MessagesController->actionInbox()
#4 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\base\InlineAction.php(57): call_user_func_array(Array, Array)
#5 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\base\Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#6 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\base\Module.php(528): yii\base\Controller->runAction('inbox', Array)
#7 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\web\Application.php(103): yii\base\Module->runAction('dashboard/messa...', Array)
#8 C:\xampp\htdocs\fsayii2\vendor\yiisoft\yii2\base\Application.php(386): yii\web\Application->handleRequest(Object(yii\web\Request))
#9 C:\xampp\htdocs\fsayii2\frontend\web\index.php(17): yii\base\Application->run()
#10 {main}
I have switched to the stable version and can't remember why I used the alpha in the beginning. Do I need to download any extra libraries from libmongoc
or libbson
, or are they already downloaded with the driver?
It's something on the Yii end, not the php driver. I discovered that if I executed commands without the Yii mongodb extension (but with the php driver) things work fine. So I'm going to close this issue for now.
To answer your previous question, libmongoc and libbson are bundled with the PECL extension and statically compiled by default. Windows users should only need the DLL file available for download on the PECL website. Linux and macOS users have the option of using libmongoc/libbson as system libraries via extra configure
options, which are discussed in the manual install docs.
I'm not familiar with the yii2-mongodb
package, but I believe the exception originates from yii\mongodb\Command::executeBatch()
. If we look at the PHP driver, we can see that AuthenticationException is only thrown for a very particular libmongoc error domain and code: MONGOC_ERROR_CLIENT_AUTHENTICATE
(see: phongo_exception_from_mongoc_domain()
). There is some more context for this error code in the libmongoc error reporting docs.
This error code appears in various places within libmongoc, but it more often than not comes with a verbose error message generated by libmongoc. The string "Authentication failed." does not appear in either libmongoc or the PHP driver, so I assume it originates from the server. I found one occurrence of it in a constant definition within authorization_manager.cpp
, which is references in two places:
In both of those cases, there should be an accompanying message in the server log. Cross-referencing with that log message should reveal exactly which code point was reached. I'm not very familiar with the server internals, but I believe both of these paths would only be reached if a driver was authenticating with the server.
Coming back to libmongoc, I looked up two places where the driver might actually use the MONGOC_ERROR_CLIENT_AUTHENTICATE
error code with a server-side error string:
mongoc_server_description_handle_ismaster()
: initial isMaster
handshake with the server fails_mongoc_stream_run_ismaster()
: also a point where an isMaster
handshake might failmongoc-cluster.c
where executing an authentication-related command on the server returns an error (e.g. basic challenge/response, x509).I think this is enough to conclude that the driver and server were certainly engaging in some authentication exchange. I believe there is also a clue in your connection string:
$connection = new \yii\mongodb\Connection([
'dsn' => 'mongodb://@localhost:27017',
]);
The @
symbol in the connection string is used to differentiate username/password credentials from a list of hosts. This syntax is also discussed in the connection string spec.
When this string is parsed by mongoc_uri_parse_before_slash()
, libmongoc finds an empty string for the username
and no password
due to the absence of a :
delimiter. Later, in mongoc_cluster_init()
, libmongoc then concludes that authentication is required because username
is not null. An immediate fix may be to remove @
from your connection string unless there was a valid excuse for it being there, which I might have missed.
Beyond that, I will open a libmongoc ticket to see if we can add some validation for this edge case and perhaps raise an error during URI parsing if the driver finds an empty string for the username.
One other tool that might have provided some context here (at the expense of a lot of output) is the mongodb.debug
INI option. This allows the PHP driver to dump libmongoc's debug trace logs to a file or stderr. I'm not sure we need it at this point, as I think the problem has been diagnosed, but it would be helpful to keep this in mind for future use if you encounter another issue where you suspect the driver or libmongoc might be misbehaving.
I'm not sure if there is any valid use case where an empty username would be accepted by the server. If not, perhaps we can consider adding some validation around this to raise a client-side error during URI parsing
If it turns out that there isn't any valid use cases for empty credentials, it would be great if the driver would just skip the username and password fields (rather than throwing an exception, for example). That's what we are doing right now, although in our application code.
if (empty($uriOptions['username'])) {
unset($uriOptions['username']);
unset($uriOptions['password']);
}
The "replicaSet" option has the same problem, by the way, so if you do end up implementing a fix for the credential fields (and unless there is a valid use case for empty replica set names, of course), it would be great if you could implement the same fix for that field, as well.
Edit After I created a user in the admin database and accessed the other DB with the user/pwd credentials, the Yii2 extension worked also. It looks like the extension throws an error if there are no credentials when accessing the database.
After I created a user in the admin database and accessed the other DB with the user/pwd credentials, the Yii2 extension worked also. It looks like the extension throws an error if there are no credentials when accessing the database.
@CathChen003: If you've configured the server to require authentication by creating users and assigning roles, then an exception would be expected when failing to provide credentials in the driver or any other client (e.g. Compass, shell). Your original post indicated that you were not intending to use authentication, which is why I concluded that the @
symbol in the connection string was a mistake.
If it turns out that there isn't any valid use cases for empty credentials, it would be great if the driver would just skip the username and password fields (rather than throwing an exception, for example)
@lindelius: I understand the desire to simply ignore an empty string, but I think raising an error would be more consistent if we conclude that an empty string is an invalid username. Likewise with an empty "replicaSet" option, raising an exception would clearly highlight to the user that their connection string or URI options are malformed.
I'm not sure why your application code would need to work around this. Are you assigning the URI option directly from an environment variable, which may be empty? Alternatively, if this is coming from a library integration (e.g. Symfony bundle parsing some configuration file), perhaps the integration can be improved to detect when an option should be unset vs. an empty string.
In the meantime, I created PHPC-1347 to ensure we don't lose track of your suggestion for the replicaSet
option.
@jmikola I don't recall requiring authentication while setting up MongoDB. I can connect fine through Compass or shell without providing user/pwd details...
@jmikola Yupp, we are using Laravel so the values initially come from env files that are then directly assigned to a config file that consist of a PHP array with a specific, pre-defined structure. We would either have to add more obtrusive code to the beginning of those config files and dynamically change the structure of the array (i.e. adding the URI option fields to the config array based on whether the env values are empty), or keep the current code in the place where we construct the Manager instance.
https://github.com/laravel/laravel/blob/master/.env.example https://github.com/laravel/laravel/blob/master/config/database.php
This is a relatively new issue, by the way. I'm fairly sure it started when we upgraded to version 1.5. Before we upgraded, the driver (or the server, I'm not sure which server version we were running then or whether we upgraded our replica set at the same time) skipped these fields when they contained empty values.
The current functionality isn't so much of an issue (our current "work-around" works perfectly fine, and it's only like 5 rows of code), it's just not ideal.
I don't recall requiring authentication while setting up MongoDB. I can connect fine through Compass or shell without providing user/pwd details...
@CathChen003: I think we're misunderstanding each other. In your previous comment, you stated that you "created a user in the admin database and accessed the other DB with the user/pwd credentials."
Enable Auth walks through the process for configure MongoDB to use authentication. after creating the first administrator user, the mongod
process should be restarted with the --auth
option (or security.authorization
config file option) to ensure that future connections require authentication.
If you only created a user on the server without requiring authentication, then I presume the server will allow clients to authenticate if they attempt to do so; however, the server will not require authentication and clients that choose not to do so will be able to connect and access the database(s) as they wish. That may explain why Compass and the shell continued to work without authentication, even after you created a user.
I'm going to reiterate that the proper solution is to remove the lone @
symbol from your connection string, as that was causing the PHP driver to attempt to authenticate with an empty username and password. If you want to enable authentication on the server, I would suggest following the manual page I linked above and then adjusting your connection string accordingly. IMO, there is no reason to create users in the admin
database (as you seem to have done) unless you plan to actually require authentication on the server for all connections.
@jmikola OK, I understand now. Thanks!
Closing, as both referenced JIRA issues have since been fixed and released.
Description
I'm trying to connect to MongoDB through PHP, but I get the error: "MongoDB\Driver\Exception\AuthenticationException Authentication failed." I don't have a username or password set up, so I'm pretty confused. I can connect to MongoDB through shell and compass, both without entering any username/password. I already tried looking up the issue in several different places (stackoverflow, mongodb-user groups.google.com, etc.) for several days but I can't find any help so I decided to post an issue here. I'm using the Yii2 framework but I don't think this problem is related to that.
Environment
Windows 10 (64-bit) PHP version 7.2.2 on XAMPP MongoDB 4.0.6 Mongo PHP Driver 1.6.0alpha1 for PHP 7.2 Threaded x86
Expected and Actual Behavior
Expected connection to MongoDB, but resulted in error thrown.
I would include more info if I knew what to include. This is my first time working with MongoDB (and NoSQL in general) so I'm pretty unknowledgeable in this area.
Thanks in advance. :)