The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
====================================
Implement Authentication in a Driver
====================================

.. default-domain:: mongodb

.. include:: /includes/note-legacy.rst

Overview
--------

The current version of MongoDB supports only the very basic
authentication of a username and password for a particular database.
Once authenticated, the user has full read and write access to that
database.

The exception is the authentication for the :term:`admin database`.
Authentication on the :term:`admin database` gives the user read and
write access to *all* databases on the server. Effectively, admin
access means root access to the database.

.. note::

   On a single socket, it is possible to authenticate for any number of
   databases and as different users. This authentication persists for
   the life of the database connection, barring a :ref:`logout
   <implement-authentication-logout>` command.

Authentication Process
----------------------

Authentication is a two step process:

#. The driver runs a :dbcommand:`getnonce` command to get a nonce for
   use in the subsequent authentication, as in the following example in
   the :program:`mongo` shell:

   .. Q: Should I use the shell wrapper db.runCommand()?

   .. code-block:: javascript

      db.$cmd.findOne( { getnonce: 1} )

   The result of the command displays the ``nonce`` as a hexadecimal
   String:

   .. code-block:: javascript

      { "nonce" : "7ca422a24f326f2a", "ok" : 1 }

#. The driver then runs the ``authenticate`` command for the
   database on which to authenticate.

   .. Q: Should I just refer to the db.authenticate() command?

   The authenticate command has the following syntax:

   .. code-block:: javascript

      db.runCommand( { authenticate : 1, user : <username>, nonce : <nonce>, key : <digest> }

   - ``<username>`` is a username in the database's ``system.users``
     collection.

   - ``<nonce>`` is the nonce returned from a previous
     :dbcommand:`getnonce` step.

   - ``<digest>`` is the hex encoding of an MD5 message digest.

     - The MD5 message digest is the MD5 hash of the concatenation of
       ``<nonce>``, ``<username>``, ``<password_digest>``.

     - The ``<password_digest>`` is the value in the ``pwd`` field
       associated with the ``<username>`` in the database's
       ``system.users`` collection. The ``pwd`` is the hex encoding of
       MD5( ``<username>`` + ``":mongo:"`` + ``<password_text>`` ).

   The result of the command should display:

   .. code-block:: javascript

      { "ok" : 1 }

   If the authentication fails, you can review the details in the
   MongoDB server log files.

Consider the following implementation of authentication that uses the
MongoDB JavaScript driver:

.. code-block:: javascript

   DB.prototype.addUser = function( username , pass ){
       var c = this.getCollection( "system.users" );

       var u = c.findOne( { user : username } ) || { user : username };
       u.pwd = hex_md5( username + ":mongo:" + pass );
       print( tojson( u ) );

       c.save( u );
   }

   DB.prototype.auth = function( username , pass ){
       var n = this.runCommand( { getnonce : 1 } );

       var a = this.runCommand(
           {
               authenticate : 1 ,
               user : username ,
               nonce : n.nonce ,
               key : hex_md5( n.nonce + username + hex_md5( username + ":mongo:" + pass ) )
           }
       );

       return a.ok;
   }

.. _implement-authentication-logout:

Logout
-------

.. Drivers may optionally implement the logout command which
   deauthorizes usage for the specified database for this connection. Note
   other databases may still be authorized.  Alternatively, you can close the
   socket to deauthorize.

Drivers may implement the :dbcommand:`logout` command which, for the
connection, deauthorizes the usage for the specified database. Other
databases may still be authorized.

.. Q: Should I use the shell wrapper db.runCommand()?

.. code-block:: javascript

   db.$cmd.findOne( { logout: 1 } )

The result of the command should display:

.. code-block:: javascript

   { "ok" : 1 }

Alternatively, you can close the socket to deauthorize.

Replica Sets and Authentication
-------------------------------

.. For drivers that support , extra care with
   replication is required. When switching from one server in a replica
   set to another, for example in a failover situation, you must
   reauthenticate. Clients will likely want to cache authentication from
   the user so that the client can reauthenticate with the new server when
   appropriate. Be careful also with operations such as Logout. If you log
   out from only some members of a replica set, that could be an issue.
   Authenticating with a server in slave mode is allowed.

For drivers that support :term:`replica sets <replica set>`,
authentication requires additional considerations:

- When switching from one server in a replica set to another, such as
  in a failover situation, you must re-authenticate. Clients might
  consider caching authentication from the user so that the clients can
  re-authenticate with the new server when appropriate.

- Logging out from some but not all members of a replica set could be
  problematic.

- Authenticating with a server in slave mode is allowed.

.. seealso::

   :wiki:`Security and Authentication`