Skip to main content
Version: 0.2.0

Authentication

This guide walks through enabling authentication on the Skardi server and using it to protect production pipelines. It uses BetterAuth with a SQLite backend (better-auth-diesel-sqlite), which persists users and sessions across server restarts with no external database required.

How Auth Works

When auth is enabled:

  • POST /api/auth/sign-up/email and POST /api/auth/sign-in/email are exposed for registration and login.
  • Every /:pipeline/execute call must carry a valid session token, either as an Authorization: Bearer <token> header or a session cookie.
  • Auth users and sessions are queryable as virtual SQL tables (auth.users, auth.sessions), so you can JOIN them with your own data in pipelines.

Environment Variables

VariableRequiredDescription
AUTH_MODEYesSet to BETTER_AUTH_DIESEL_SQLITE to enable auth
AUTH_SECRETYesSecret key used to sign sessions (32+ characters)
AUTH_DB_PATHNoPath to the SQLite database file (defaults to skardi_auth.db)
AUTH_BASE_URLNoPublic base URL of the server (defaults to http://localhost:<PORT>)

Prerequisites

Build the server binary:

cargo build --bin skardi-server

Step 1 — Start the Server with Auth Enabled

From the project root, start the server with the active-users pipeline and auth turned on:

AUTH_MODE=BETTER_AUTH_DIESEL_SQLITE \
AUTH_SECRET="super-secret-key-at-least-32-characters-long" \
AUTH_DB_PATH="skardi_auth.db" \
cargo run --bin skardi-server -- \
--pipeline docs/auth/pipelines/active-users.yaml \
--port 8080

You should see the server start and list the auth routes alongside the pipeline endpoint:

Starting Skardi Online Serving Pipeline Server
...
Server listening on 0.0.0.0:8080

Step 2 — Sign Up

Register a new user account:

curl -s -X POST http://localhost:8080/api/auth/sign-up/email \
-H "Content-Type: application/json" \
-d '{
"name": "Alice",
"email": "alice@example.com",
"password": "mysecretpassword"
}' | jq .

Response:

{
"token": "sess_abc123...",
"user": {
"id": "usr_xyz...",
"name": "Alice",
"email": "alice@example.com",
"createdAt": "2025-01-15T12:00:00.000Z"
}
}

Save the token value — you will use it to authenticate pipeline requests.

Step 3 — Sign In (for Returning Users)

If you already have an account, sign in to get a new session token:

curl -s -X POST http://localhost:8080/api/auth/sign-in/email \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com",
"password": "mysecretpassword"
}' | jq .

Response:

{
"token": "sess_abc123...",
"user": {
"id": "usr_xyz...",
"email": "alice@example.com"
}
}

Step 4 — Execute a Pipeline with the Session Token

With auth enabled, unauthenticated requests are rejected:

# Without a token — returns 401
curl -s -X POST http://localhost:8080/active-users/execute \
-H "Content-Type: application/json" \
-d '{"limit": 3}' | jq .
{"error": "Authentication required"}

Add the Authorization: Bearer header to authenticate:

TOKEN="sess_abc123..."   # replace with your actual token

curl -s -X POST http://localhost:8080/active-users/execute \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"limit": 5}' | jq .

Response:

{
"success": true,
"data": [
{
"id": "usr_xyz...",
"name": "Alice",
"email": "alice@example.com",
"expires_at": "2025-01-15T13:00:00.000Z"
}
],
"row_count": 1,
"execution_time_ms": 4
}

BetterAuth also sets a session cookie on sign-in. You can pass it along in requests as an alternative to the Authorization header:

# Sign in and capture the Set-Cookie header
curl -s -c cookies.txt -X POST http://localhost:8080/api/auth/sign-in/email \
-H "Content-Type: application/json" \
-d '{"email": "alice@example.com", "password": "mysecretpassword"}' | jq .

# Use the saved cookie for pipeline execution
curl -s -b cookies.txt -X POST http://localhost:8080/active-users/execute \
-H "Content-Type: application/json" \
-d '{"limit": 3}' | jq .

Step 6 — Query Auth Tables from SQL (Advanced)

When auth is enabled, two virtual tables are available inside any pipeline query:

  • auth.users — all registered users (id, name, email, role, created_at, …)
  • auth.sessions — all active sessions (id, token, user_id, expires_at, …)

You can JOIN these with your own data sources. The included docs/auth/pipelines/active-users.yaml pipeline does exactly this — it returns all users who have a currently active session.

Execute it (requires a valid session token):

curl -s -X POST http://localhost:8080/active-users/execute \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"limit": 10}' | jq .

Full End-to-End Shell Script

Save this as a convenience script to try everything in one go:

#!/usr/bin/env bash
set -euo pipefail

BASE="http://localhost:8080"

echo "==> Sign up"
RESPONSE=$(curl -s -X POST "$BASE/api/auth/sign-up/email" \
-H "Content-Type: application/json" \
-d '{"name":"Alice","email":"alice@example.com","password":"mysecretpassword"}')
echo "$RESPONSE" | jq .

TOKEN=$(echo "$RESPONSE" | jq -r '.token')
echo ""
echo "==> Session token: $TOKEN"
echo ""

echo "==> Execute pipeline (authenticated)"
curl -s -X POST "$BASE/active-users/execute" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"limit": 3}' | jq .

# Users and sessions survive server restarts — sign in again with the same credentials:
echo ""
echo "==> Sign in (after restart)"
curl -s -X POST "$BASE/api/auth/sign-in/email" \
-H "Content-Type: application/json" \
-d '{"email":"alice@example.com","password":"mysecretpassword"}' | jq .

Auth API Reference

EndpointMethodDescription
/api/auth/sign-up/emailPOSTRegister a new user
/api/auth/sign-in/emailPOSTSign in and obtain a session token
/api/auth/sign-outPOSTInvalidate the current session
/api/auth/sessionGETRetrieve the current session (requires token)

Sign-Up / Sign-In Request Body

{
"name": "Alice",
"email": "alice@example.com",
"password": "mysecretpassword"
}

(name is only required on sign-up)

Authentication Header

Authorization: Bearer <session-token>

Error Responses

StatusMeaning
401 UnauthorizedNo token provided
401 UnauthorizedToken is invalid or expired
{"error": "Authentication required"}
{"error": "Invalid or expired session"}

Production Notes

  • BETTER_AUTH_DIESEL_SQLITE stores users and sessions in a SQLite file (AUTH_DB_PATH). Data persists across server restarts.
  • The SQLite database is created automatically on first startup; schema migrations run via the better-auth-diesel-sqlite crate.
  • Set AUTH_BASE_URL to your server's public URL so that cookies and redirects work correctly behind a reverse proxy.
  • Use a strong, randomly generated AUTH_SECRET (at minimum 32 characters). You can generate one with: openssl rand -base64 32
  • Session tokens are short-lived. Clients should re-authenticate when they receive a 401 response.