Fix MongoServerError bad auth Authentication failed (code 18) instantly with a 30-second diagnostic table
"I typed the password correctly — why am I still getting bad auth?"
You landed here after hitting this message in mongosh or a backend app.
MongoServerError: bad auth : Authentication failed.Or in driver logs:
MongoServerError: Authentication failed. (code 18)99% of developers retype the password two or three times at this point. But the core of MongoDB authentication is not the password — it is "which database this user is registered in (authSource)". People coming from MySQL or PostgreSQL get stuck here especially often. The user lives in admin, but you try to log in against mydb, so MongoDB replies "no such person" and authentication bounces.
This is not an article you read from start to finish. It is a diagnostic tool that lets you identify one of five causes in 30 seconds and restore connectivity with copy-paste commands.
30-second diagnostic table: which of the 5 causes is my error?
The first thing to distinguish is authentication failure vs insufficient privileges. These are completely different problems.
| Symptom (error message) | code | Check command | Cause | Jump to |
|---|---|---|---|---|
bad auth : Authentication failed | 18 | Check whether the connection URI has ?authSource=admin | Mismatch of the DB the user belongs to (authSource) | Case 1 |
bad auth (password contains @ # : /) | 18 | Inspect the raw connection string | Missing URL encoding of special characters in the password | Case 2 |
Authentication failed (new client) | 18 | db.getUser(...,{showCredentials:true}) | SCRAM-SHA-1 vs 256 mismatch | Case 3 |
not authorized on X to execute command | 13 | Check db.getUser("user") roles | Auth succeeded, insufficient privileges | Case 4 |
bad auth (fresh server that just enabled --auth) | 18 | Check whether system.users is empty | Admin user never created | Case 5 |
Remember just two key distinctions.
code 18(bad auth) = authentication failure. One of user, password, or authSource is wrong.code 13(not authorized) = authentication succeeded, but you lack permission to run the command. The password was correct, so do not retype it.
One-liner to see where a user is registered:
// Run while connected as an admin
db.getSiblingDB("admin").system.users.find({}, {user:1, db:1, "credentials":1})The db field in the result is that user's authSource. Most of the time it shows admin. In that case you must also attach authSource=admin when connecting.
Per-case diagnosis + copy-paste recovery commands
Case 1. authSource not specified (most common)
You created the user on a DB other than admin (or, like a Docker root user, the user is on admin) and omitted authSource at connect time.
Diagnose:
db.getSiblingDB("admin").system.users.find({user:"myuser"}, {user:1, db:1})
// If db: "admin" → you need authSource=admin when connectingRecover — mongosh:
mongosh "mongodb://myuser:mypass@localhost:27017/mydb?authSource=admin"Node.js (official mongodb driver):
const { MongoClient } = require("mongodb");
const uri = "mongodb://myuser:mypass@localhost:27017/mydb?authSource=admin";
const client = new MongoClient(uri);
await client.connect();Python (pymongo):
from pymongo import MongoClient
client = MongoClient(
"localhost", 27017,
username="myuser", password="mypass",
authSource="admin"
)
# or URI form
client = MongoClient("mongodb://myuser:mypass@localhost:27017/mydb?authSource=admin")Case 2. Password special characters not URL-encoded
If the password contains characters like @ : / ? # %, the connection-string parser misreads them as URI structure and breaks. For example, if the password is p@ss:w0rd, it treats @ as the host separator.
URL encoding cheat sheet:
| Character | Encoding | Character | Encoding |
|---|---|---|---|
@ | %40 | # | %23 |
: | %3A | % | %25 |
/ | %2F | ? | %3F |
Node.js auto-encoding:
const user = encodeURIComponent("myuser");
const pass = encodeURIComponent("p@ss:w0rd");
const uri = `mongodb://${user}:${pass}@localhost:27017/mydb?authSource=admin`;Python auto-encoding:
from urllib.parse import quote_plus
from pymongo import MongoClient
uri = "mongodb://%s:%s@localhost:27017/mydb?authSource=admin" % (
quote_plus("myuser"), quote_plus("p@ss:w0rd"))
client = MongoClient(uri)Practical tip: I made it a team convention that "passwords never go directly into the URI; always pass them as the driver's username/password parameters." Both pymongo and the Node driver encode correctly when you pass parameters, which eliminates the encoding trap at the source. After we switched, Case 2 tickets disappeared.
Case 3. SCRAM-SHA-1 vs SCRAM-SHA-256 mismatch
The default mechanism on MongoDB 6.x/7.x is SCRAM-SHA-256. Users created by old scripts may have only SHA-1, or the client may fail negotiation, and authentication bounces.
Diagnose — check stored mechanisms:
db.getUser("myuser", { showCredentials: true })
// The credentials object shows SCRAM-SHA-1 / SCRAM-SHA-256 keysRecover — reset mechanisms (grant both):
db.updateUser("myuser", {
mechanisms: ["SCRAM-SHA-256", "SCRAM-SHA-1"],
pwd: "mypass" // password must be reset when updating mechanisms
})Specify explicitly on the client:
// Node.js
const uri = "mongodb://myuser:mypass@localhost:27017/mydb"
+ "?authSource=admin&authMechanism=SCRAM-SHA-256";# pymongo
client = MongoClient(
"localhost", 27017,
username="myuser", password="mypass",
authSource="admin", authMechanism="SCRAM-SHA-256"
)Case 4. User exists but roles are insufficient (this is not bad auth!)
If you got not authorized on mydb to execute command (code 13), login succeeded. You only need to attach roles.
Diagnose:
db.getUser("myuser") // check whether the roles array is empty or insufficientRecover:
db.getSiblingDB("admin").grantRolesToUser("myuser", [
{ role: "readWrite", db: "mydb" }
])Case 5. --auth enabled but the first admin user was never created
If you turned on --auth without creating an admin user, nobody can log in — the instance is locked. Bootstrap with the localhost exception.
Safe sequence (self-hosted):
# 1) restart without auth (or use the localhost exception)
mongod --dbpath /data/db --bind_ip localhost
# 2) connect from localhost and create the admin user
mongoshuse admin
db.createUser({
user: "root",
pwd: "strongPass123!",
roles: [{ role: "root", db: "admin" }]
})# 3) restart with --auth
mongod --dbpath /data/db --authOn Docker, one shot with env vars:
docker run -d --name mongo \
-e MONGO_INITDB_ROOT_USERNAME=root \
-e MONGO_INITDB_ROOT_PASSWORD=strongPass123! \
-p 27017:27017 mongo:7The root user created this way is created on the admin DB. So you must use authSource=admin when connecting (this is the same trap as Case 1).
Connection string reference & Docker checklist
Complete, working connection strings in one place.
# mongosh
mongosh "mongodb://root:strongPass123!@localhost:27017/?authSource=admin&authMechanism=SCRAM-SHA-256"// Node.js
const uri = "mongodb://root:strongPass123!@localhost:27017/mydb"
+ "?authSource=admin&authMechanism=SCRAM-SHA-256";# pymongo (URI)
client = MongoClient(
"mongodb://root:strongPass123!@localhost:27017/mydb?authSource=admin&authMechanism=SCRAM-SHA-256")Docker checklist
-
MONGO_INITDB_ROOT_*users are always created on admin →authSource=adminis required - Init scripts run only on first start (ignored if the volume already exists)
- Create app-specific users on
mydb, but put that user's exact authSource in the connection URI
Conclusion: how not to get stuck next time
One-line summary: If code 18, suspect authSource, password, or SCRAM. If code 13, grant roles.
Follow just two prevention rules.
- Always specify authSource (especially Docker root users, which are always on admin).
- Always run passwords through an encoding function, or pass them as driver parameters.
If you arrived here from a MySQL ERROR 1698 (auth_socket/mysql_native_password) or PostgreSQL password authentication failed (pg_hba.conf) runbook, the mapping can be confusing. MongoDB has two concepts those systems do not: the authentication DB the user belongs to (authSource) and SCRAM mechanism negotiation. MySQL authenticates on a user@host basis and PostgreSQL on pg_hba.conf connection rules, but in MongoDB the prerequisite for authentication is "which database the user is registered in." That difference is decisive.
FAQ
Q. Can I connect without authSource?
A. Yes, if the user was created on the same DB you are connecting to. But Docker root users and shared admin users live on admin, so in practice authSource=admin is required. Always specify it so you never have to guess.
Q. The password is correct but I still get bad auth.
A. Almost always an authSource mismatch (Case 1) or password special-character encoding (Case 2). First check the user's actual db field with db.getSiblingDB("admin").system.users.find().
Q. Is not authorized also a password problem?
A. No. code 13 means authentication succeeded and you lack privileges. Grant roles with grantRolesToUser (Case 4).
Q. Scripts that used to connect with the mongo shell no longer work.
A. The legacy mongo shell is deprecated; the current standard is mongosh. Command syntax and default SCRAM-SHA-256 negotiation changed, so switch to the mongosh examples.
Nodelog는 모든 콘텐츠의 내용과 출처를 공개 전에 검토합니다. 환경(OS·버전)에 따라 결과가 달라질 수 있는 기술 정보는 공식 문서와 함께 확인하며, 검토 기준과 정정 원칙은 편집 정책에서 안내합니다. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
Comments
Be the first to comment.