// constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="MongoCredential" /> class.
        /// </summary>
        /// <param name="mechanism">Mechanism to authenticate with.</param>
        /// <param name="identity">The identity.</param>
        /// <param name="evidence">The evidence.</param>
        public MongoCredential(string mechanism, MongoIdentity identity, MongoIdentityEvidence evidence)
        {
            if (identity == null)
            {
                throw new ArgumentNullException("identity");
            }
            if (evidence == null)
            {
                throw new ArgumentNullException("evidence");
            }

            _mechanism = mechanism;
            _identity  = identity;
            _evidence  = evidence;
        }
        // constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="MongoCredential" /> class.
        /// </summary>
        /// <param name="mechanism">Mechanism to authenticate with.
        /// In .NET Standard, authenticating via SCRAM-SHA-256 may not work with non-ASCII passwords because SaslPrep is
        /// not fully implemented due to the lack of a string normalization function in .NET Standard 1.6.
        /// Normalizing the password into Unicode Normalization Form KC beforehand MAY help.
        /// SCRAM-SHA-1 is the recommended alternative for now.</param>
        /// <param name="identity">The identity.</param>
        /// <param name="evidence">The evidence.</param>
        public MongoCredential(string mechanism, MongoIdentity identity, MongoIdentityEvidence evidence)
        {
            if (identity == null)
            {
                throw new ArgumentNullException("identity");
            }
            if (evidence == null)
            {
                throw new ArgumentNullException("evidence");
            }

            _mechanism           = mechanism;
            _identity            = identity;
            _evidence            = evidence;
            _mechanismProperties = new Dictionary <string, object>();
        }
        // private static methods
        private static MongoCredential FromComponents(string mechanism, string source, string username, MongoIdentityEvidence evidence)
        {
            var defaultedMechanism = (mechanism ?? "DEFAULT").Trim().ToUpperInvariant();

            switch (defaultedMechanism)
            {
            case "DEFAULT":
            case "MONGODB-CR":
            case "SCRAM-SHA-1":
            case "SCRAM-SHA-256":
                // it is allowed for a password to be an empty string, but not a username
                source = source ?? "admin";
                if (evidence == null || !(evidence is PasswordEvidence))
                {
                    var message = string.Format("A {0} credential must have a password.", defaultedMechanism);
                    throw new ArgumentException(message);
                }

                return(new MongoCredential(
                           mechanism,
                           new MongoInternalIdentity(source, username),
                           evidence));

            case "MONGODB-X509":
                // always $external for X509.
                source = "$external";
                if (evidence == null || !(evidence is ExternalEvidence))
                {
                    throw new ArgumentException("A MONGODB-X509 does not support a password.");
                }

                return(new MongoCredential(
                           mechanism,
                           new MongoX509Identity(username),
                           evidence));

            case "GSSAPI":
                // always $external for GSSAPI.
                source = "$external";

                return(new MongoCredential(
                           "GSSAPI",
                           new MongoExternalIdentity(source, username),
                           evidence));

            case "PLAIN":
                source = source ?? "admin";
                if (evidence == null || !(evidence is PasswordEvidence))
                {
                    throw new ArgumentException("A PLAIN credential must have a password.");
                }

                MongoIdentity identity;
                if (source == "$external")
                {
                    identity = new MongoExternalIdentity(source, username);
                }
                else
                {
                    identity = new MongoInternalIdentity(source, username);
                }

                return(new MongoCredential(
                           mechanism,
                           identity,
                           evidence));

            default:
                throw new NotSupportedException(string.Format("Unsupported MongoAuthenticationMechanism {0}.", mechanism));
            }
        }
        // private static methods
        private static MongoCredential FromComponents(string mechanism, string source, string username, MongoIdentityEvidence evidence)
        {
            if (string.IsNullOrEmpty(mechanism))
            {
                throw new ArgumentException("Cannot be null or empty.", "mechanism");
            }
            if (string.IsNullOrEmpty(username))
            {
                return(null);
            }

            switch (mechanism.ToUpperInvariant())
            {
            case "MONGODB-CR":
                // it is allowed for a password to be an empty string, but not a username
                source = source ?? "admin";
                if (evidence == null || !(evidence is PasswordEvidence))
                {
                    throw new ArgumentException("A MONGODB-CR credential must have a password.");
                }

                return(new MongoCredential(
                           mechanism,
                           new MongoInternalIdentity(source, username),
                           evidence));

            case "MONGODB-X509":
                // always $external for X509.
                source = "$external";
                if (evidence == null || !(evidence is ExternalEvidence))
                {
                    throw new ArgumentException("A MONGODB-X509 does not support a password.");
                }

                return(new MongoCredential(
                           mechanism,
                           new MongoExternalIdentity(username),
                           evidence));

            case "GSSAPI":
                // always $external for GSSAPI.
                source = "$external";

                return(new MongoCredential(
                           "GSSAPI",
                           new MongoExternalIdentity(source, username),
                           evidence));

            case "PLAIN":
                source = source ?? "admin";
                if (evidence == null || !(evidence is PasswordEvidence))
                {
                    throw new ArgumentException("A PLAIN credential must have a password.");
                }

                MongoIdentity identity;
                if (source == "$external")
                {
                    identity = new MongoExternalIdentity(source, username);
                }
                else
                {
                    identity = new MongoInternalIdentity(source, username);
                }

                return(new MongoCredential(
                           mechanism,
                           identity,
                           evidence));

            default:
                throw new NotSupportedException(string.Format("Unsupported MongoAuthenticationMechanism {0}.", mechanism));
            }
        }