Database Store
This page explains how database-backed stores work in rix/auth.
The examples use the public Rix facade:
#include <rix.hpp>and Auth through:
rix.authDatabase stores persist users and sessions through Vix database storage.
Use them when authentication data must survive process restarts.
Basic idea
Memory auth is useful for examples:
auto auth = rix.auth.memory();Database auth is for durable applications:
auto auth = rix.auth.database(db);With production configuration:
auto config = rix.auth.config.production();
auto auth = rix.auth.database(db, config);Database auth creates:
DbUserStore
DbSessionStore
Auth serviceThe user store persists users.
The session store persists sessions.
When to use database auth
Use database auth when:
users must survive restarts
sessions must survive restarts
the application is real or production-oriented
authentication state must be durable
multiple deployments need persistent account dataDatabase auth is the recommended direction for real applications.
When not to use database auth
For the first local examples, you can use:
auto auth = rix.auth.memory();Memory auth is simpler when you only want to learn the API.
Use database auth when you are ready to persist real user and session data.
Install
For the facade API, install:
vix add rix/rix
vix installIn vix.app, declare:
deps = [
"rix/rix",
]If your application also uses Vix database APIs, keep the normal Vix package link section:
packages = [
"vix",
]
links = [
"vix::vix",
]deps is for Vix Registry packages.
packages is for CMake package discovery.
Do not put Rix packages in packages.
Database auth
The high-level facade API is:
auto auth = rix.auth.database(db);This uses production configuration by default.
You can also pass configuration explicitly:
auto config = rix.auth.config.production();
config.set_issuer("my-app");
auto auth = rix.auth.database(db, config);Use this form when your application needs custom password policy, session lifetime, token lifetime, or issuer.
Database stores
The facade can create database-backed stores directly:
auto users = rix.auth.stores.database_users(db);
auto sessions = rix.auth.stores.database_sessions(db);Most applications do not need to create stores directly.
Prefer:
auto auth = rix.auth.database(db);or:
auto auth = rix.auth.database(db, config);Direct store creation is useful for advanced integrations, tests, migrations, and custom wiring.
Tables
The database stores use two tables:
rix_auth_users
rix_auth_sessionsDbUserStore stores users.
DbSessionStore stores sessions.
The constructors can create the required schema automatically when schema creation is enabled.
The high-level database auth helper creates the stores using their default schema behavior.
User store table
The user store persists the user model.
A user contains:
id
email
password_hash
email_verified
active
created_at
updated_atThe password hash is sensitive server-side data.
Do not send it to clients.
Do not log it in production.
Session store table
The session store persists the session model.
A session contains:
id
user_id
created_at
expires_at
last_seen_at
revokedThe session id is sensitive.
Do not log session ids in production.
Complete shape
A real application usually creates the database before creating Auth.
The exact database setup depends on your Vix application.
The Auth part looks like this:
#include <rix.hpp>
int main()
{
auto config = rix.auth.config.production();
config.set_issuer("my-app");
/*
* Create or open your Vix database here.
*
* Example shape:
*
* auto db = ...;
*/
auto auth = rix.auth.database(db, config);
auto registered = auth.register_user({
"ada@example.com",
"correct-password"
});
if (registered.failed())
{
const auto &error = registered.error();
rix.debug.eprint(
"auth error:",
rix.auth.error.to_string(error),
error.message()
);
return 1;
}
auto login = auth.login({
"ada@example.com",
"correct-password"
});
if (login.failed())
{
const auto &error = login.error();
rix.debug.eprint(
"auth error:",
rix.auth.error.to_string(error),
error.message());
return 1;
}
rix.debug.print("registered:", registered.value().email());
rix.debug.print("user id:", registered.value().id());
rix.debug.print("session expires at:", login.value().session.expires_at());
return 0;
}This page focuses on the Auth database store API.
Use the database setup style recommended by the Vix database module in your application.
Create stores manually
For advanced usage, create the stores manually:
auto users = rix.auth.stores.database_users(db);
auto sessions = rix.auth.stores.database_sessions(db);Then create managed auth:
auto managed = rix.auth.managed(
std::move(users),
std::move(sessions),
rix.auth.config.production()
);Check the result:
if (managed.failed())
{
const auto &error = managed.error();
rix.debug.eprint(
"auth error:",
rix.auth.error.to_string(error),
error.message()
);
return 1;
}
auto auth = managed.move_value();Use this when you want the returned auth object to own the database-backed stores.
Caller-owned database stores
Advanced code can create an auth service with caller-owned stores:
auto users = rix.auth.stores.database_users(db);
auto sessions = rix.auth.stores.database_sessions(db);
auto auth = rix.auth.create(
*users,
*sessions,
rix.auth.config.production()
);In this case, the caller owns the stores.
The stores must stay alive for as long as the auth service is used.
For normal application code, prefer:
auto auth = rix.auth.database(db, config);Store lifetime rule
This is the simplest durable path:
auto auth = rix.auth.database(db, config);This is also safe:
auto managed = rix.auth.managed(
rix.auth.stores.database_users(db),
rix.auth.stores.database_sessions(db),
config
);because the managed auth object owns the stores.
This requires care:
auto users = rix.auth.stores.database_users(db);
auto sessions = rix.auth.stores.database_sessions(db);
auto auth = rix.auth.create(*users, *sessions, config);because the stores are owned outside the auth object.
Do not destroy the stores while Auth is still using them.
The database object must also stay alive while the stores are using it.
Schema creation
The database-backed stores can create their required tables.
The store constructors accept a schema creation flag at the lower-level API.
Conceptually:
DbUserStore(db, true)
DbSessionStore(db, true)The facade helper uses the default store behavior.
For most applications, that is enough.
For advanced migrations, you can manage schema creation separately and use lower-level store construction when needed.
Ensure schema manually
The lower-level database stores expose:
ensure_schema()This is useful when you want explicit control over schema creation.
Example shape:
auto users = rix.auth.stores.database_users(db);
auto sessions = rix.auth.stores.database_sessions(db);
/*
* If you need direct ensure_schema access, use the lower-level
* DbUserStore and DbSessionStore types.
*/For normal application code, prefer the high-level helper:
auto auth = rix.auth.database(db, config);Register with database auth
Registration is the same as memory auth:
auto registered = auth.register_user({
"ada@example.com",
"correct-password"
});The difference is storage.
With memory auth, the user lives in memory.
With database auth, the user is persisted through the database store.
Always check the result:
if (registered.failed())
{
const auto &error = registered.error();
return 1;
}Login with database auth
Login is also the same:
auto login = auth.login({
"ada@example.com",
"correct-password"
});A successful login creates a database-backed session.
That session can survive process restarts if the database is durable.
Authenticate a persisted session
Authenticate with the session id:
auto session = auth.authenticate_session(session_id);If the session exists in the database and is usable, authentication succeeds.
A usable session must be:
valid
not revoked
not expiredRefresh a persisted session
Refresh a session:
auto refreshed = auth.refresh_session(session_id);The database-backed store updates the stored session.
The refreshed session receives a new expiration time and last-seen time.
Logout with database auth
Logout revokes the session:
auto status = auth.logout(session_id);The database-backed session remains stored but becomes unusable.
After logout:
auto session = auth.authenticate_session(session_id);should fail.
Logout all sessions for a user
Use:
auto status = auth.logout_user(user_id);This revokes all database-backed sessions attached to the user.
Use it when:
a user changes password
a user logs out from all devices
an account is disabled
a security incident requires immediate disconnectInspect database stores
A managed auth object exposes its stores:
auto auth = rix.auth.database(db, config);
auto &users = auth.users();
auto &sessions = auth.sessions();You can use store methods in tests, admin tools, or diagnostics:
auto all_users = auth.users().all();
if (all_users.ok())
{
rix.debug.print("users:", all_users.value().size());
}For large production datasets, prefer dedicated paginated admin APIs instead of loading everything with all().
User store methods
The user store exposes:
create(user)
update(user)
remove_by_id(id)
find_by_id(id)
find_by_email(email)
exists_by_id(id)
exists_by_email(email)
all()Most application code should not call these directly.
Prefer the high-level Auth operations:
auth.register_user(...)
auth.login(...)Session store methods
The session store exposes:
create(session)
update(session)
remove_by_id(id)
revoke_by_id(id)
revoke_by_user_id(user_id)
find_by_id(id)
find_by_user_id(user_id)
exists_by_id(id)
all()Most application code should not call these directly.
Prefer:
auth.authenticate_session(...)
auth.refresh_session(...)
auth.logout(...)
auth.logout_user(...)Error handling
Database operations return explicit results and statuses.
Registration:
auto registered = auth.register_user({
"ada@example.com",
"correct-password"
});
if (registered.failed())
{
const auto &error = registered.error();
}Login:
auto login = auth.login({
"ada@example.com",
"correct-password"
});
if (login.failed())
{
const auto &error = login.error();
}Logout:
auto status = auth.logout(session_id);
if (status.failed())
{
const auto &error = status.error();
}Use the stable error code for programmatic decisions:
rix.auth.error.to_string(error)Use the message for diagnostics:
error.message()Common database store errors
Database-backed auth can fail with errors such as:
StoreError
InvalidInput
InvalidEmail
InvalidPassword
UserNotFound
UserAlreadyExists
InvalidCredentials
InvalidSession
SessionExpired
SessionRevoked
ConfigurationErrorStoreError usually means the database operation failed.
Use the message for diagnostics.
Production configuration
Use production configuration for durable auth:
auto config = rix.auth.config.production();
config.set_issuer("my-production-app");
auto auth = rix.auth.database(db, config);Production configuration is stricter and is the right starting point for real deployments.
Migration strategy
For real applications, treat auth database tables like application data.
A good production strategy is:
use database auth for runtime
keep schema creation explicit in deployment or migrations
validate configuration at startup
use production configuration
monitor StoreError failures
avoid logging secretsThe high-level helper is convenient for getting started.
As the application grows, you can move schema management into your normal migration workflow.
Security notes
Database auth persists sensitive authentication data.
Do not log:
plain-text passwords
password hashes
session ids
raw token valuesUse production configuration.
Use durable database storage.
Use HTTPS in real applications.
Keep token lifetimes short.
Revoke sessions with logout.
Revoke all sessions with logout_user when account security changes.
What you should remember
Use database auth for durable applications:
auto config = rix.auth.config.production();
auto auth = rix.auth.database(db, config);Register and login use the same API as memory auth:
auth.register_user(...)
auth.login(...)Sessions are persisted through the database-backed session store.
Users are persisted through the database-backed user store.
For normal application code, prefer:
rix.auth.database(db, config)over manual store wiring.
Always check results and statuses before using returned values.
Next step
Learn Auth errors.
Next: Errors