Authentication¶
SecantusDB implements SCRAM-SHA-256, MongoDB’s default authentication
mechanism since 4.0, end-to-end on the wire. The same MongoClient(uri, username=, password=) calls you’d point at a real mongod work against
SecantusDB unchanged.
This is wire-protocol authentication only — saslStart / saslContinue
plus the createUser / dropUser / usersInfo admin commands and the
per-connection state machine. Authorization (RBAC) is not implemented
yet: an authenticated principal is treated as fully privileged. See
Compatibility and tasks/backlog.md for the
remaining gaps.
Off by default¶
Auth is opt-in. A vanilla secantusdb daemon and the embedded
SecantusDBServer(...) accept commands from any connection — same as
running mongod without --auth. This keeps the test-harness use case
zero-friction.
secantusdb --port 27117 # no auth required
Turning auth on¶
Two equivalent switches:
CLI flag:
secantusdb --authConstructor:
SecantusDBServer(..., require_auth=True)
With auth on, only the handshake and the SCRAM exchange run on an
unauthenticated connection. Everything else (find, insert,
listDatabases, …) returns Unauthorized (code 13) until the client
completes a saslStart / saslContinue round-trip.
secantusdb --auth --port 27117
Provisioning users¶
Use createUser from any connected client. The plaintext password is
hashed (PBKDF2-HMAC-SHA-256, 15000 iterations) and discarded; only the
SCRAM storedKey and serverKey are persisted.
from pymongo import MongoClient
# Connect once (no auth required to call createUser when --auth is off).
admin = MongoClient("mongodb://127.0.0.1:27117/").admin
admin.command(
{
"createUser": "alice",
"pwd": "hunter2",
"roles": [], # roles accepted but not enforced (RBAC TBD)
}
)
Then connect with credentials. Pymongo handles the SCRAM round-trip:
client = MongoClient(
"mongodb://127.0.0.1:27117/",
username="alice",
password="hunter2",
authSource="admin",
authMechanism="SCRAM-SHA-256",
)
client["mydb"]["users"].insert_one({"_id": 1, "name": "Alice"})
Or as a single URI:
mongodb://alice:hunter2@127.0.0.1:27117/?authSource=admin&authMechanism=SCRAM-SHA-256
mongosh, mongodump, and other tooling work the same way.
Bootstrap pattern¶
The chicken-and-egg problem: with --auth on you can’t createUser
without already being authenticated. There are two ways out:
Provision first, lock down second — start without
--auth, runcreateUser, restart with--auth. The on-disk store keeps the user record across restarts.Pre-seed in code — for embedded use, reach into
server.storage.add_user(...)directly to insert the credential record before clients connect. The shape mirrors mongod’sadmin.system.users[]documents:
from secantus import SecantusDBServer
from secantus.auth import SCRAM_SHA_256, derive_credentials
srv = SecantusDBServer(port=0, storage_path=":memory:", require_auth=True)
srv.start()
creds = derive_credentials("secret")
srv.storage.add_user(
"admin",
"root",
{
"_id": "admin.root",
"user": "root",
"db": "admin",
"credentials": creds.to_doc(),
"roles": [{"role": "root", "db": "admin"}],
"mechanisms": [SCRAM_SHA_256],
},
)
Inspecting state¶
usersInfo returns provisioned users (without credentials by default).
connectionStatus returns who’s authenticated on the current connection.
admin.command({"usersInfo": 1}) # all users in this db
admin.command({"usersInfo": "alice"}) # one
admin.command({"usersInfo": "alice", "showCredentials": True}) # include hashes
admin.command("connectionStatus") # auth state
MONGODB-X509 — cert-as-username auth¶
When the daemon is configured with mTLS ([tls] cert_file + key_file
ca_file— see Configuration), the MONGODB-X509 mechanism lets clients authenticate by presenting an X.509 cert whose subject DN is registered as a username — no SCRAM challenge / response, no password to manage.
The flow:
Client presents its cert during the TLS handshake; SecantusDB verifies the cert chains back to the configured
[tls] ca_file.Server reads the cert’s subject DN (RFC 4514 string form, e.g.
CN=alice,OU=Eng,O=Acme,C=US) and stores it on the connection.Client sends
{authenticate: 1, mechanism: "MONGODB-X509", user: <DN>}(the legacy command path pymongo / Java / Go / Node all use for X509). The server matches the cert DN against the user record and the optionaluserfield; mismatch is an authentication failure.User record must have
MONGODB-X509in itsmechanismsarray.
Provisioning an X509 user¶
# Connect to the bootstrap server (no auth yet, mTLS optional).
admin = MongoClient(server.uri).get_database("$external")
# The username MUST equal the cert's subject DN, exactly as the
# server will see it (RFC 4514 string, most-specific first).
admin.command(
"createUser",
"CN=alice,OU=Eng,O=Acme,C=US",
roles=[{"role": "root", "db": "admin"}],
mechanisms=["MONGODB-X509"],
# No pwd — X509 has no password
)
Convention: X509 users live on the $external virtual auth db (no
real collections), matching mongod. SecantusDB also accepts users
created on admin as a fallback so operators with simpler setups
aren’t forced into $external.
Connecting¶
mongodb://server:27017/?
tls=true
&tlsCAFile=/path/to/ca.pem
&tlsCertificateKeyFile=/path/to/alice-cert+key.pem
&authMechanism=MONGODB-X509
&authSource=$external
The tlsCertificateKeyFile is the concatenation of the cert chain and
the private key in one PEM file (pymongo’s expected format).
Combining X509 and SCRAM on one user¶
A user record can carry both mechanisms simultaneously — useful when
migrating from password auth to cert auth, or when some clients
predate cert support. Just include both in mechanisms:
admin.command(
"createUser",
"CN=alice,OU=Eng,O=Acme,C=US",
pwd="hunter2", # required for SCRAM
roles=[{"role": "root", "db": "admin"}],
mechanisms=["SCRAM-SHA-256", "MONGODB-X509"],
)
The client picks the mechanism per connection — drivers pick the
strongest the server advertises in saslSupportedMechs.
Driver compatibility¶
Anything that speaks SCRAM-SHA-256 or MONGODB-X509 over the
standard MongoDB wire protocol works. Verified end-to-end:
pymongo(sync andmotor)mongo-go-driver(Go) — also coversmongodump/mongorestore/mongosh, which embed the Go drivermongoshdirect connectionmongo-java-driver,mongo-node-driver,mongo-ruby-driver(the same SCRAM / X509 wire flow they use against real mongod)
SCRAM-SHA-1 (the pre-MongoDB-4.0 default) is implemented but not
advertised by default. Pass mechanisms=["SCRAM-SHA-1"] to
createUser if a legacy client pins to it.
What’s not here yet¶
LDAP / Kerberos / GSSAPI / MONGODB-AWS / MONGODB-OIDC — alternative external auth mechanisms. SCRAM and X509 cover the majority case; the others would each be a separate slice.
Internal cluster auth (keyfile / x509) — only meaningful with replica sets / sharding, both out of scope.
These are tracked in tasks/backlog.md and will land in follow-up
slices.