Rix is the official package layer for Vix.cpp Browse packages
Skip to content

Auth API

This page documents the public rix/auth API.

Use auth through the unified Rix facade:

cpp
#include <rix.hpp>

Then access auth with:

cpp
rix.auth

The auth API provides helpers for:

txt
registration
login
password hashing
sessions
tokens
configuration
memory stores
error handling

Package

The auth package is:

txt
rix/auth

For facade usage, install:

bash
vix add rix/rix
vix install

In vix.app:

txt
deps = [
  "rix/rix",
]

For independent package usage, install:

bash
vix add rix/auth
vix install

In vix.app:

txt
deps = [
  "rix/auth",
]

Facade usage:

cpp
#include <rix.hpp>

Independent usage:

cpp
#include <rix/auth.hpp>

Most application examples should use the facade.

Facade member

The auth facade member is:

cpp
rix.auth

Example:

cpp
#include <rix.hpp>

int main()
{
  auto auth = rix.auth.memory();

  auto registered = auth.register_user({
      "ada@example.com",
      "correct-password"});

  return registered.ok() ? 0 : 1;
}

Main modules

rix.auth exposes several helper areas:

cpp
rix.auth.memory()
rix.auth.password
rix.auth.config
rix.auth.error

Common usage:

cpp
auto auth = rix.auth.memory();

auto config = rix.auth.config.development();

auto hashed = rix.auth.password.hash("correct-password");

auto error_text = rix.auth.error.to_string(error);

Create a memory auth service

Use:

cpp
auto auth = rix.auth.memory();

This creates an auth service backed by in-memory user and session stores.

Memory auth is useful for:

txt
examples
tests
local development
small demos
temporary applications

Memory auth does not persist users or sessions after the process exits.

Create memory auth with config

cpp
auto config = rix.auth.config.development();

config.set_min_password_length(8);
config.set_session_ttl_seconds(60 * 60 * 24 * 7);
config.set_token_ttl_seconds(60 * 15);

auto auth = rix.auth.memory(config);

Use configuration when you want to change password policy, token lifetime, session lifetime, or issuer.

Register a user

Use:

cpp
auto registered = auth.register_user({
    "ada@example.com",
    "correct-password"});

register_user returns an explicit result.

Check it before using the user:

cpp
if (registered.failed())
{
  rix.debug.eprint(
      "auth error:",
      rix.auth.error.to_string(registered.error()),
      registered.error().message());

  return 1;
}

Then use:

cpp
const auto &user = registered.value();

Register request

A register request contains:

txt
email
password

Example:

cpp
auto registered = auth.register_user({
    "ada@example.com",
    "correct-password"});

The plain-text password is hashed before storage.

Do not log the plain-text password.

Login

Use:

cpp
auto login = auth.login({
    "ada@example.com",
    "correct-password"});

login returns an explicit result.

Check it:

cpp
if (login.failed())
{
  rix.debug.eprint(
      "auth error:",
      rix.auth.error.to_string(login.error()),
      login.error().message());

  return 1;
}

Then use:

cpp
login.value().user
login.value().session
login.value().token

Login result

A successful login contains:

txt
user
session
token

Example:

cpp
rix.debug.print("user:", login.value().user.email());
rix.debug.print("session:", login.value().session.id());
rix.debug.print("issuer:", login.value().token.issuer());

Session ids and token values are sensitive.

Do not print them in production logs.

Authenticate a session

Use:

cpp
auto session = auth.authenticate_session(
    login.value().session.id());

Check the result:

cpp
if (session.failed())
{
  rix.debug.eprint(
      "auth error:",
      rix.auth.error.to_string(session.error()),
      session.error().message());

  return 1;
}

Then:

cpp
rix.debug.print("session user id:", session.value().user_id());

A session is valid when it exists, is not expired, and is not revoked.

Refresh a session

Use:

cpp
auto refreshed = auth.refresh_session(session_id);

Check it:

cpp
if (refreshed.failed())
{
  rix.debug.eprint(
      "auth error:",
      rix.auth.error.to_string(refreshed.error()),
      refreshed.error().message());

  return 1;
}

Then:

cpp
rix.debug.print("new expires at:", refreshed.value().expires_at());

Refreshing a session extends its expiration according to the configured session lifetime.

Logout

Use:

cpp
auto status = auth.logout(session_id);

Check it:

cpp
if (status.failed())
{
  rix.debug.eprint(
      "auth error:",
      rix.auth.error.to_string(status.error()),
      status.error().message());

  return 1;
}

Logout revokes the session.

A revoked session can remain stored, but it cannot be used for authentication.

Logout all sessions for a user

Use:

cpp
auto status = auth.logout_user(user_id);

Example:

cpp
auto status = auth.logout_user(registered.value().id());

if (status.failed())
{
  rix.debug.eprint(
      "auth error:",
      rix.auth.error.to_string(status.error()),
      status.error().message());

  return 1;
}

This revokes all sessions belonging to the user.

Issue a token

Use:

cpp
auto token = auth.issue_token(user_id);

Check it:

cpp
if (token.failed())
{
  rix.debug.eprint(
      "auth error:",
      rix.auth.error.to_string(token.error()),
      token.error().message());

  return 1;
}

Then read token metadata:

cpp
rix.debug.print("user id:", token.value().user_id());
rix.debug.print("issuer:", token.value().issuer());
rix.debug.print("issued at:", token.value().issued_at());
rix.debug.print("expires at:", token.value().expires_at());

Do not log raw token values in production.

Password API

Password helpers are available through:

cpp
rix.auth.password

Common helpers:

cpp
rix.auth.password.hash(...)
rix.auth.password.verify(...)
rix.auth.password.accepts(...)
rix.auth.password.hasher()

Hash a password

Use:

cpp
auto hashed = rix.auth.password.hash("correct-password");

Check it:

cpp
if (hashed.failed())
{
  rix.debug.eprint(
      "auth error:",
      rix.auth.error.to_string(hashed.error()),
      hashed.error().message());

  return 1;
}

Then store:

cpp
hashed.value()

Do not store plain-text passwords.

Verify a password

Use:

cpp
const bool ok = rix.auth.password.verify(
    "correct-password",
    hashed.value());

Wrong passwords return false:

cpp
const bool wrong = rix.auth.password.verify(
    "wrong-password",
    hashed.value());

Example:

cpp
if (ok && !wrong)
{
  rix.debug.print("password verification works");
}

Check password policy

Use:

cpp
const bool accepted = rix.auth.password.accepts("correct-password");

Example:

cpp
if (!rix.auth.password.accepts("short"))
{
  rix.debug.eprint("password rejected by policy");
}

hash also checks the password policy.

Create a password hasher

Use:

cpp
auto hasher = rix.auth.password.hasher();

Then:

cpp
auto hashed = hasher.hash("correct-password");

const bool ok = hasher.verify(
    "correct-password",
    hashed.value());

With configuration:

cpp
auto config = rix.auth.config.production();

auto hasher = rix.auth.password.hasher(config);

For most application code, the facade helpers are enough.

Configuration API

Configuration helpers are available through:

cpp
rix.auth.config

Common helpers:

cpp
rix.auth.config.development()
rix.auth.config.production()

Development configuration

Use:

cpp
auto config = rix.auth.config.development();

Development configuration is useful for examples and local development.

It allows simple register and login flows.

Example:

cpp
auto auth = rix.auth.memory(
    rix.auth.config.development());

Production configuration

Use:

cpp
auto config = rix.auth.config.production();

Production configuration is stricter.

It can require email verification before login.

Example:

cpp
auto auth = rix.auth.memory(
    rix.auth.config.production());

Use production configuration when building a real application flow.

Configuration setters

Common configuration setters include:

cpp
config.set_min_password_length(...)
config.set_session_ttl_seconds(...)
config.set_token_ttl_seconds(...)
config.set_issuer(...)
config.set_password_hash_iterations(...)
config.set_password_salt_size(...)
config.set_password_hash_size(...)

Example:

cpp
auto config = rix.auth.config.development();

config.set_min_password_length(12);
config.set_session_ttl_seconds(60 * 60 * 24 * 7);
config.set_token_ttl_seconds(60 * 15);
config.set_issuer("my-app");

auto auth = rix.auth.memory(config);

Error API

Error helpers are available through:

cpp
rix.auth.error

Common helpers:

cpp
rix.auth.error.to_string(error)
rix.auth.error.is(error, code)

Example:

cpp
if (registered.failed())
{
  const auto &error = registered.error();

  rix.debug.eprint(
      "auth error:",
      rix.auth.error.to_string(error),
      error.message());
}

Auth error codes

Auth error codes include:

cpp
rixlib::auth::AuthErrorCode::InvalidInput
rixlib::auth::AuthErrorCode::InvalidEmail
rixlib::auth::AuthErrorCode::InvalidPassword
rixlib::auth::AuthErrorCode::UserNotFound
rixlib::auth::AuthErrorCode::UserAlreadyExists
rixlib::auth::AuthErrorCode::InvalidCredentials
rixlib::auth::AuthErrorCode::InvalidSession
rixlib::auth::AuthErrorCode::SessionExpired
rixlib::auth::AuthErrorCode::InvalidToken
rixlib::auth::AuthErrorCode::TokenExpired
rixlib::auth::AuthErrorCode::InvalidState
rixlib::auth::AuthErrorCode::StoreError
rixlib::auth::AuthErrorCode::Unknown

Use error codes for programmatic decisions.

Use messages for diagnostics.

Check a specific error

cpp
if (rix.auth.error.is(
        login.error(),
        rixlib::auth::AuthErrorCode::InvalidCredentials))
{
  rix.debug.eprint("login failed:", "invalid credentials");
}

This lets your app react differently depending on the failure.

Result pattern

Auth APIs use explicit results.

For operations that return a value:

cpp
auto result = auth.login({
    "ada@example.com",
    "correct-password"});

if (result.failed())
{
  rix.debug.eprint(
      "auth error:",
      rix.auth.error.to_string(result.error()),
      result.error().message());

  return 1;
}

const auto &login = result.value();

Never call value() before checking success.

Status pattern

Some operations return a status.

Example:

cpp
auto status = auth.logout(session_id);

if (status.failed())
{
  rix.debug.eprint(
      "auth error:",
      rix.auth.error.to_string(status.error()),
      status.error().message());

  return 1;
}

Use:

cpp
status.ok()
status.failed()
status.error()

User API

A user value exposes user information.

Common methods include:

cpp
user.id()
user.email()
user.active()
user.email_verified()

Example:

cpp
const auto &user = registered.value();

rix.debug.print("id:", user.id());
rix.debug.print("email:", user.email());

Do not print password hashes in application output.

Session API

A session value exposes session information.

Common methods include:

cpp
session.id()
session.user_id()
session.created_at()
session.expires_at()
session.revoked()
session.expired(...)
session.usable(...)

Example:

cpp
const auto &session = login.value().session;

rix.debug.print("user id:", session.user_id());
rix.debug.print("expires at:", session.expires_at());

Session ids are sensitive.

Avoid logging raw session ids in production.

Token API

A token value exposes token information.

Common methods include:

cpp
token.value()
token.user_id()
token.issuer()
token.issued_at()
token.expires_at()
token.revoked()
token.revoke()
token.expired(...)
token.usable(...)
token.belongs_to(...)
token.issued_by(...)

Example:

cpp
const auto &token = login.value().token;

rix.debug.print("issuer:", token.issuer());
rix.debug.print("expires at:", token.expires_at());

The raw token value is sensitive.

Avoid logging:

cpp
token.value()

in production.

Complete register and login example

cpp
#include <rix.hpp>

int main()
{
  auto auth = rix.auth.memory();

  auto registered = auth.register_user({
      "ada@example.com",
      "correct-password"});

  if (registered.failed())
  {
    rix.debug.eprint(
        "auth error:",
        rix.auth.error.to_string(registered.error()),
        registered.error().message());

    return 1;
  }

  auto login = auth.login({
      "ada@example.com",
      "correct-password"});

  if (login.failed())
  {
    rix.debug.eprint(
        "auth error:",
        rix.auth.error.to_string(login.error()),
        login.error().message());

    return 1;
  }

  auto session = auth.authenticate_session(
      login.value().session.id());

  if (session.failed())
  {
    rix.debug.eprint(
        "auth error:",
        rix.auth.error.to_string(session.error()),
        session.error().message());

    return 1;
  }

  rix.debug.print("authenticated user id:", session.value().user_id());
  return 0;
}

Run:

bash
vix run auth.cpp

Complete password hashing example

cpp
#include <rix.hpp>

int main()
{
  auto hashed = rix.auth.password.hash("correct-password");

  if (hashed.failed())
  {
    rix.debug.eprint(
        "auth error:",
        rix.auth.error.to_string(hashed.error()),
        hashed.error().message());

    return 1;
  }

  const bool valid = rix.auth.password.verify(
      "correct-password",
      hashed.value());

  const bool invalid = rix.auth.password.verify(
      "wrong-password",
      hashed.value());

  if (!valid || invalid)
  {
    return 1;
  }

  rix.debug.print("password verification successful");
  return 0;
}

Complete session example

cpp
#include <rix.hpp>

int main()
{
  auto auth = rix.auth.memory();

  auto registered = auth.register_user({
      "grace@example.com",
      "correct-password"});

  if (registered.failed())
  {
    return 1;
  }

  auto login = auth.login({
      "grace@example.com",
      "correct-password"});

  if (login.failed())
  {
    return 1;
  }

  const auto session_id = login.value().session.id();

  auto refreshed = auth.refresh_session(session_id);

  if (refreshed.failed())
  {
    return 1;
  }

  auto logout = auth.logout(session_id);

  if (logout.failed())
  {
    return 1;
  }

  auto after_logout = auth.authenticate_session(session_id);

  return after_logout.failed() ? 0 : 1;
}

Complete token example

cpp
#include <rix.hpp>

int main()
{
  auto auth = rix.auth.memory();

  auto registered = auth.register_user({
      "linus@example.com",
      "correct-password"});

  if (registered.failed())
  {
    return 1;
  }

  auto token = auth.issue_token(
      registered.value().id());

  if (token.failed())
  {
    return 1;
  }

  rix.debug.print("issuer:", token.value().issuer());
  rix.debug.print("expires at:", token.value().expires_at());

  return 0;
}

Use in a Vix route

cpp
#include <vix.hpp>
#include <rix.hpp>

int main()
{
  vix::App app;

  auto auth = rix.auth.memory();

  app.post("/register", [&](vix::Request &, vix::Response &res) {
    auto registered = auth.register_user({
        "ada@example.com",
        "correct-password"});

    if (registered.failed())
    {
      res.status(400).json({
          "ok", false,
          "error", rix.auth.error.to_string(registered.error()),
          "message", registered.error().message()});

      return;
    }

    res.json({
        "ok", true,
        "email", registered.value().email()});
  });

  app.run();

  return 0;
}

For durable applications, use database-backed stores instead of memory stores.

Use in a Vix project

Create a Vix app:

bash
vix new rix-auth-api --app
cd rix-auth-api

Add Rix:

bash
vix add rix/rix
vix install

Make sure vix.app contains:

txt
deps = [
  "rix/rix",
]

Use in src/main.cpp:

cpp
#include <rix.hpp>

Build and run:

bash
vix build
vix run

Single-file usage

Create a file:

bash
mkdir -p ~/rix-auth-api
cd ~/rix-auth-api
touch auth.cpp

Add:

cpp
#include <rix.hpp>

int main()
{
  auto auth = rix.auth.memory();
  return 0;
}

Run:

bash
vix run auth.cpp

If Rix is not available globally:

bash
vix install -g rix/rix
vix run auth.cpp

Independent usage

Install only auth:

bash
vix add rix/auth
vix install

In vix.app:

txt
deps = [
  "rix/auth",
]

Then include:

cpp
#include <rix/auth.hpp>

Use independent package APIs when you intentionally do not want the unified facade.

For most documentation and application examples, prefer:

cpp
#include <rix.hpp>

and:

cpp
rix.auth

Security notes

Do not log:

txt
plain-text passwords
password hashes
raw token values
session ids

Use HTTPS in production.

Use durable stores for real user accounts.

Use stricter configuration for production.

Keep token lifetimes short.

Keep session lifetimes intentional.

Require email verification when your application needs it.

Common mistakes

Forgetting to install Rix

If your code uses:

cpp
#include <rix.hpp>

install:

bash
vix add rix/rix
vix install

Putting Rix in packages

Wrong:

txt
packages = [
  "rix/rix",
]

Correct:

txt
deps = [
  "rix/rix",
]

deps is for Vix Registry packages.

packages is for CMake package discovery.

Calling value() before checking success

Wrong:

cpp
auto login = auth.login({
    "ada@example.com",
    "correct-password"});

rix.debug.print(login.value().user.email());

Correct:

cpp
auto login = auth.login({
    "ada@example.com",
    "correct-password"});

if (login.failed())
{
  return 1;
}

rix.debug.print(login.value().user.email());

Printing sensitive values

Avoid printing:

txt
passwords
password hashes
tokens
session ids

Expecting memory auth to persist

Memory auth is not durable.

When the process exits, users and sessions are gone.

Use database-backed stores for real applications.

Using production config without email verification flow

Production config can require email verification before login.

For simple examples, use:

cpp
auto auth = rix.auth.memory();

or:

cpp
auto auth = rix.auth.memory(
    rix.auth.config.development());

Treating tokens and sessions as the same thing

A session is server-side authentication state.

A token is a short-lived value issued for a user.

They have separate lifetimes:

cpp
config.set_session_ttl_seconds(...);
config.set_token_ttl_seconds(...);

What you should remember

Create auth:

cpp
auto auth = rix.auth.memory();

Register:

cpp
auto registered = auth.register_user({
    "ada@example.com",
    "correct-password"});

Login:

cpp
auto login = auth.login({
    "ada@example.com",
    "correct-password"});

Authenticate session:

cpp
auto session = auth.authenticate_session(
    login.value().session.id());

Hash password:

cpp
auto hashed = rix.auth.password.hash("correct-password");

Verify password:

cpp
const bool ok = rix.auth.password.verify(
    "correct-password",
    hashed.value());

Issue token:

cpp
auto token = auth.issue_token(user_id);

Always check failed() before using value().

For project usage:

bash
vix add rix/rix
vix install

and keep:

txt
deps = [
  "rix/rix",
]

Next step

Continue with the CSV API.

Next: CSV API

Released under the MIT License.