Better Auth Admin Guide
Better Auth Admin Guide
Operations reference for administrators managing users, sessions, secrets, and database migrations in the bloqr-backend authentication system.
Bootstrapping the First Admin User
There is no hardcoded admin account. Use one of the following methods to create the first admin after the initial database migration.
Method A — Better Auth Admin Plugin API
Sign up normally, then promote the user using the admin plugin’s set-role endpoint.
You need a temporary way to make the first call — use the INITIAL_ADMIN_EMAIL bootstrap
mechanism (see Method B) or call from a trusted network context.
# Sign up the first usercurl -X POST https://your-worker.workers.dev/api/auth/sign-up/email \ -H "Content-Type: application/json" \ -d '{ "name": "Admin User", "email": "admin@example.com", "password": "strong-password" }'
# Save the returned userId, then promote via Prisma CLI (one-time bootstrap):deno task db:studio# In Prisma Studio → User table → set role = "admin" and tier = "admin" for the userMethod B — Prisma Studio (Recommended for First Admin)
# Start Prisma Studio against your local databasedeno task db:studio# OR against production (requires DIRECT_DATABASE_URL):DIRECT_DATABASE_URL="postgresql://..." deno run -A npm:prisma studioIn Prisma Studio:
- Navigate to the User table.
- Find your user by email.
- Set
role→adminandtier→admin. - Save.
Method C — Better Auth CLI (if the user exists)
# Using the Better Auth admin API — requires an existing admin sessioncurl -X POST https://your-worker.workers.dev/api/auth/admin/set-role \ -H "Authorization: Bearer <existing-admin-session-token>" \ -H "Content-Type: application/json" \ -d '{ "userId": "cm9abc123", "role": "admin" }'Managing Users via the Admin Panel
Navigate to /admin/users in the Angular frontend. You must be signed in with an account
whose role is admin.
What the Panel Shows
| Column | Source | Notes |
|---|---|---|
| ID | user.id | Truncated display; click to copy full ID |
| Name | user.name | |
user.email | ||
| Tier | user.tier | anonymous, free, pro, admin |
| Role | user.role | user, admin |
| Status | user.banned | active or banned |
| Created | user.createdAt | ISO timestamp |
| Last seen | session.updatedAt | From most recent session |
Available Actions
- Set tier — promotes or demotes a user’s tier (controls rate limits and feature access)
- Set role — makes a user an admin or revokes admin status
- Ban user — prevents the user from signing in
- Revoke all sessions — signs the user out of all devices immediately
Auth Settings Panel
Navigate to /admin/auth-settings to see the current authentication configuration.
What It Shows
| Section | Fields |
|---|---|
| Provider Status | Active auth method (better-auth), version |
| Social Providers | GitHub (configured / not configured), Google (reserved) |
| MFA | TOTP enabled (true — always on via twoFactor() plugin) |
| Session Config | TTL (7 days), cookie cache duration (5 min), update age (1 day) |
| Database | Adapter (prisma/postgresql), Hyperdrive binding status |
The panel reads from GET /api/auth/providers and the Worker’s live environment — it never
exposes secret values.
Banning and Unbanning Users
sequenceDiagram
participant A as Admin Client
participant W as Worker /api/auth/admin/ban-user
participant DB as PostgreSQL
A->>W: POST { userId: "cm9abc123" }\nAuthorization: Bearer <admin-token>
W->>DB: UPDATE user SET banned = true WHERE id = cm9abc123
W->>DB: DELETE FROM session WHERE userId = cm9abc123
DB-->>W: done
W-->>A: 200 { success: true }
Ban a User (API)
curl -X POST https://your-worker.workers.dev/api/auth/admin/ban-user \ -H "Authorization: Bearer <admin-session-token>" \ -H "Content-Type: application/json" \ -d '{ "userId": "cm9abc123" }'Banning a user also revokes all their active sessions immediately.
Unban a User (API)
curl -X POST https://your-worker.workers.dev/api/auth/admin/unban-user \ -H "Authorization: Bearer <admin-session-token>" \ -H "Content-Type: application/json" \ -d '{ "userId": "cm9abc123" }'Revoking Sessions
Revoke a Specific Session
sequenceDiagram
participant A as Admin
participant W as Worker /api/auth/admin/revoke-user-sessions
participant DB as PostgreSQL
A->>W: POST { userId: "cm9abc123", sessionId: "ses_xyz" }\nAuthorization: Bearer <admin-token>
W->>DB: DELETE FROM session WHERE id = ses_xyz AND userId = cm9abc123
DB-->>W: deleted
W-->>A: 200 { success: true }
curl -X POST https://your-worker.workers.dev/api/auth/admin/revoke-user-sessions \ -H "Authorization: Bearer <admin-session-token>" \ -H "Content-Type: application/json" \ -d '{ "userId": "cm9abc123" }'This endpoint revokes all sessions for the specified user. To revoke a single session, use the self-service endpoint as that user, or delete directly via Prisma Studio.
Revoke All Sessions for a User (Self-Service)
Users can revoke their own other sessions via:
curl -X POST https://your-worker.workers.dev/api/auth/revoke-other-sessions \ -b cookies.txtConfiguring BETTER_AUTH_SECRET
The BETTER_AUTH_SECRET is the HMAC signing key for all session tokens. Changing it
immediately invalidates all existing sessions — all users are signed out.
Generate a New Secret
openssl rand -base64 32# Example output: Tq+7dXk1RovfTH3bI4JlqAeHp8sWmKzVcNxYuBrGdDE=Set for Local Development
BETTER_AUTH_SECRET=Tq+7dXk1RovfTH3bI4JlqAeHp8sWmKzVcNxYuBrGdDE=Set for Production
wrangler secret put BETTER_AUTH_SECRET# Paste the generated value when promptedRotate the Secret (Emergency Procedure)
- Generate a new secret:
openssl rand -base64 32 - Update in production:
wrangler secret put BETTER_AUTH_SECRET - Deploy:
wrangler deploy - All existing sessions are immediately invalidated — users must sign in again.
- Communicate the disruption to users if planned maintenance is not possible.
Never rotate the secret routinely — only do this if the current secret is compromised. Treat
BETTER_AUTH_SECRETlike a database password.
Running Database Migrations
Better Auth uses Prisma for schema management. The canonical schema is prisma/schema.prisma.
Better Auth Tables
| Table | Contents |
|---|---|
user | User accounts — id, name, email, tier, role, banned |
session | Active sessions — id, userId, token, expiresAt, ipAddress |
account | OAuth provider links — userId, providerId, accessToken |
verification | Email verification and password-reset tokens |
twoFactor | TOTP secrets per user (added by twoFactor() plugin) |
Generate Prisma Client After Schema Changes
deno task db:generateThis runs prisma generate, updating node_modules/.prisma/client to match the schema.
Run this whenever you add a new plugin (which may add tables) or modify schema.prisma.
Apply Migrations (Development)
deno task db:migrateThis runs prisma migrate dev against the database pointed to by DIRECT_DATABASE_URL in
.env.local. It creates a new timestamped migration file in migrations/ and applies it.
Apply Migrations (Production / CI)
deno task db:migrate:deployThis runs prisma migrate deploy — applies all pending migrations without prompting.
Use in CI pipelines. Set DIRECT_DATABASE_URL to the production database URL (not Hyperdrive).
Full Migration Workflow
# 1. Edit prisma/schema.prisma (add column, table, etc.)# 2. Generate the migration filedeno task db:migrate # creates migrations/<timestamp>_<name>.sql
# 3. Verify the generated SQL is correctcat migrations/<timestamp>_<name>.sql
# 4. Generate updated Prisma clientdeno task db:generate
# 5. Run tests to ensure nothing brokedeno task test:worker
# 6. Deploy to productionwrangler deploydeno task db:migrate:deploy # apply migration to production DBGitHub OAuth Setup
A short reference — for full setup instructions, see Social Providers.
- Create an OAuth App at GitHub → Settings → Developer Settings → OAuth Apps
- Set the callback URL:
https://your-worker.workers.dev/api/auth/callback/github - Copy the Client ID and generate a Client Secret
- Set the secrets:
Terminal window wrangler secret put GITHUB_CLIENT_IDwrangler secret put GITHUB_CLIENT_SECRET - Deploy:
wrangler deploy
The GitHub sign-in button appears automatically — GET /api/auth/providers returns github: true
when both secrets are present, and the Angular frontend renders the button.
Admin API Reference
All admin endpoints are under /api/auth/admin/* and require an authenticated session with
role: admin.
| Endpoint | Method | Description |
|---|---|---|
/api/auth/admin/list-users | GET | List all users with pagination |
/api/auth/admin/set-role | POST | Change user role (admin / user) |
/api/auth/admin/ban-user | POST | Ban a user (revokes sessions) |
/api/auth/admin/unban-user | POST | Remove a ban |
/api/auth/admin/impersonate-user | POST | Create a session as another user |
/api/auth/admin/revoke-user-sessions | POST | Revoke all sessions for a user |
Related Documentation
- Better Auth User Guide — End-user authentication flows
- Better Auth Developer Guide — Plugin/extensibility guide
- Configuration Guide — Full environment variable reference
- Social Providers — GitHub/Google OAuth setup
- Better Auth Prisma — Prisma adapter technical details