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

Session Refresh and Logout

This example shows how to refresh a session and log out with rix/auth.

The example uses the public Rix facade:

cpp
#include <rix.hpp>

and accesses auth through:

cpp
rix.auth

It uses an in-memory auth service, so it is useful for examples, tests, and local development.

Create the file

bash
mkdir -p ~/rix-auth-session-example
cd ~/rix-auth-session-example
touch session_refresh_logout.cpp

Add:

cpp
#include <rix.hpp>

int main()
{
  rix.debug.print("== rix/auth session refresh and logout ==");

  auto auth = rix.auth.memory();

  auto registered = auth.register_user({
      "grace@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({
      "grace@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;
  }

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

  rix.debug.print("created session:", session_id);
  rix.debug.print("expires at:", login.value().session.expires_at());

  auto refreshed = auth.refresh_session(session_id);

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

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

    return 1;
  }

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

  auto logout = auth.logout(session_id);

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

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

    return 1;
  }

  rix.debug.print("----------------------------------------");
  rix.debug.print("OK:", "logout successful");

  auto after_logout = auth.authenticate_session(session_id);

  if (after_logout.ok())
  {
    rix.debug.eprint("ERROR:", "session should not be valid after logout");
    return 1;
  }

  rix.debug.print("session rejected after logout");

  return 0;
}

Run it:

bash
vix run session_refresh_logout.cpp

If Rix is not available yet for single-file usage:

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

Expected output shape:

txt
== rix/auth session refresh and logout ==
created session: session_...
expires at: ...
----------------------------------------
OK: session refreshed
new expires at: ...
----------------------------------------
OK: logout successful
session rejected after logout

What this example does

The example creates an in-memory auth service:

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

It registers a user:

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

It logs the user in:

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

It stores the session id:

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

It refreshes the session:

cpp
auto refreshed = auth.refresh_session(session_id);

It logs out:

cpp
auto logout = auth.logout(session_id);

Then it checks that the session is rejected after logout:

cpp
auto after_logout = auth.authenticate_session(session_id);

Create a session

A session is created when login succeeds.

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

The login result contains:

txt
user
session
token

Access the session with:

cpp
login.value().session

Use the session id for later session operations:

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

Refresh a session

Use:

cpp
auto refreshed = auth.refresh_session(session_id);

If the session is valid, not expired, and not revoked, refresh succeeds.

The refreshed session has an updated expiration timestamp.

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

Check refresh errors

refresh_session returns a result.

Check it before using the refreshed session:

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

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

  return 1;
}

Then use:

cpp
refreshed.value()

Logout

Use:

cpp
auto logout = auth.logout(session_id);

Logout revokes the session.

A revoked session remains stored, but it can no longer be used for authentication.

Check logout errors

logout returns a status.

Use:

cpp
logout.ok()
logout.failed()
logout.error()

Example:

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

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

  return 1;
}

Authenticate after logout

After logout, this should fail:

cpp
auto after_logout = auth.authenticate_session(session_id);

Check it:

cpp
if (after_logout.ok())
{
  rix.debug.eprint("ERROR:", "session should not be valid after logout");
  return 1;
}

This confirms that logout revoked the session.

Normal session flow

The normal flow is:

txt
register user
login user
get session id
refresh session when needed
logout when done
reject session after logout

In code:

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

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

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

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

auto refreshed = auth.refresh_session(session_id);

auto logout = auth.logout(session_id);

In real code, check every result before calling value().

Error handling pattern

Use this pattern for session operations that return a value:

cpp
auto result = auth.refresh_session(session_id);

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

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

  return 1;
}

Use this pattern for operations that only return a status:

cpp
auto status = auth.logout(session_id);

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

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

  return 1;
}

Refresh before logout

This is valid:

cpp
auto refreshed = auth.refresh_session(session_id);

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

auto logout = auth.logout(session_id);

Refresh keeps the session alive.

Logout revokes it.

After logout, refresh should fail because the session is no longer usable.

Refresh after logout 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 logout = auth.logout(session_id);

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

  auto refreshed = auth.refresh_session(session_id);

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

    return 0;
  }

  return 1;
}

Run:

bash
vix run session_refresh_logout.cpp

The refresh after logout should fail.

Logout all sessions for a user

Use:

cpp
auto status = auth.logout_user(user_id);

Example:

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

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

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

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

if (login1.failed() || login2.failed())
{
  return 1;
}

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;
}

logout_user revokes all sessions belonging to the given user.

Authenticate session directly

Use:

cpp
auto session = auth.authenticate_session(session_id);

This checks that the session:

txt
exists
is valid
is not expired
is not revoked

Example:

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

Custom session lifetime

You can configure session lifetime with auth config:

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

config.set_session_ttl_seconds(60 * 60);

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

This creates sessions that last one hour.

Use this when testing session expiration behavior or changing application policy.

Token lifetime is separate

Session lifetime and token lifetime are separate settings.

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

The session controls server-side authentication.

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

Safe output

Session ids are sensitive.

This example prints the session id so the flow is easy to see.

For production logs, avoid printing:

txt
session ids
raw token values
passwords
password hashes

Use stable user ids, event ids, or redacted values instead.

Use in a Vix project

Create a project:

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

Add Rix:

bash
vix add rix/rix
vix install

Make sure vix.app contains:

txt
deps = [
  "rix/rix",
]

A minimal vix.app can look like this:

txt
name = "rix-auth-session"
type = "executable"
standard = "c++20"
output_dir = "bin"

sources = [
  "src/main.cpp",
]

include_dirs = [
  "include",
  "src",
]

deps = [
  "rix/rix",
]

packages = [
  "vix",
]

links = [
  "vix::vix",
]

Put the example code in:

txt
src/main.cpp

Build and run:

bash
vix build
vix run

Single-file usage

For examples, tests, and quick experiments:

bash
vix run session_refresh_logout.cpp

If needed:

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

For project usage, prefer:

bash
vix add rix/rix
vix install

and keep the dependency in vix.app:

txt
deps = [
  "rix/rix",
]

Use only auth with the facade

If you want the rix.* facade style but only want auth mounted, define the feature macro before including rix.hpp:

cpp
#define RIX_ENABLE_AUTH
#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;
  }

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

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

When at least one RIX_ENABLE_* macro is defined, only selected modules are mounted.

If you also want debug output:

cpp
#define RIX_ENABLE_AUTH
#define RIX_ENABLE_DEBUG
#include <rix.hpp>

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

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

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

    return 1;
  }

  rix.debug.print("registered:", registered.value().email());
  return 0;
}

Use the independent package

For independent usage, install:

bash
vix add rix/auth
vix install

In vix.app:

txt
deps = [
  "rix/auth",
]

Then include auth package headers directly.

The examples in this documentation prefer the public facade:

cpp
#include <rix.hpp>

and:

cpp
rix.auth

Common mistakes

Forgetting to install Rix

If rix.hpp is not found, install Rix first.

For a project:

bash
vix add rix/rix
vix install

For single-file usage:

bash
vix install -g rix/rix

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 refreshed = auth.refresh_session(session_id);

rix.debug.print(refreshed.value().expires_at());

Correct:

cpp
auto refreshed = auth.refresh_session(session_id);

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

rix.debug.print(refreshed.value().expires_at());

Expecting logout to delete the session

Logout revokes the session.

A revoked session can remain stored, but it is not usable.

This makes logout behavior safer and easier to audit.

Refreshing a revoked session

This should fail:

cpp
auth.logout(session_id);

auto refreshed = auth.refresh_session(session_id);

A revoked session is not refreshable.

Printing session ids in production

Avoid logging raw session ids.

They are sensitive authentication values.

The example prints them only to make the flow visible.

Expecting memory sessions to persist

Memory sessions disappear when the process exits.

Use database-backed stores for durable auth.

What you should remember

Create memory auth:

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

Login creates a session:

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

Refresh the session:

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

Logout revokes the session:

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

After logout, authentication should fail:

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

Check every result before using value().

For project usage:

bash
vix add rix/rix
vix install

and keep:

txt
deps = [
  "rix/rix",
]

Next step

Continue with token issue.

Next: Token issue

Released under the MIT License.