Skip to content

Repository files navigation

Forge

A minimal Next.js boilerplate with authentication, database and a component library ready to go.

Prerequisites

  • Node.js 18+
  • Docker (recommended) or PostgreSQL running locally

Setup

  1. Clone the repo
git clone https://github.com/mmilanovic4/forge.git
cd forge
  1. Install dependencies
npm install
  1. Create the environment file and fill in your values:
touch .env

See the Environment Variables section below for the required values.

  1. Start the database
docker compose up -d
  1. Push the database schema
npm run db:push
  1. Generate the Prisma client
npm run prisma:generate
  1. Start the dev server
npm run dev

Open http://localhost:3000.

Docker

The included docker-compose.yml spins up PostgreSQL and Mailpit.

docker compose up -d    # start in background
docker compose down     # stop
docker compose down -v  # stop and delete all data

Mailpit web UI is available at http://localhost:8025.

Environment Variables

Variable Required Description
DATABASE_URL PostgreSQL connection string
BETTER_AUTH_SECRET Random secret string (min 32 chars)
BETTER_AUTH_URL Base URL of the app (e.g. http://localhost:3000)
SMTP_HOST SMTP server host
SMTP_PORT SMTP server port
SMTP_USER SMTP username
SMTP_PASS SMTP password
SMTP_FROM From email address
NEXT_PUBLIC_AUTH_METHOD Login method [otp, magic-link]
DISCORD_CLIENT_ID Discord OAuth app client ID
DISCORD_CLIENT_SECRET Discord OAuth app client secret
GITHUB_CLIENT_ID GitHub OAuth app client ID
GITHUB_CLIENT_SECRET GitHub OAuth app client secret
GOOGLE_CLIENT_ID Google OAuth app client ID
GOOGLE_CLIENT_SECRET Google OAuth app client secret
S3_BUCKET Bucket name for file uploads
S3_REGION Bucket region
S3_ACCESS_KEY_ID S3 access key ID
S3_SECRET_ACCESS_KEY S3 secret access key
S3_ENDPOINT Custom endpoint for S3-compatible services

Auth Method

Forge supports three login methods, controlled via the NEXT_PUBLIC_AUTH_METHOD environment variable:

Value Description
otp Passwordless login via a one-time code sent to the user's email
magic-link Passwordless login via a sign-in link sent to the user's email

If NEXT_PUBLIC_AUTH_METHOD is not set, Forge defaults to password-based authentication.

Note: Both otp and magic-link require SMTP to be configured, as they rely on email delivery.

Passkeys

Passkeys (WebAuthn / FIDO2) are supported out of the box and are additive — they sit alongside whatever primary login method is configured (otp, magic-link or password), rather than replacing it. A single account can have a password (or email login) and one or more passkeys at the same time.

There are two ways to sign in with a passkey on the login screen, both of which work without typing an email:

  • Autofill (conditional UI) — when the browser has a saved passkey, it offers it directly from the email field, so the user signs in with a single tap.
  • "Sign in with a passkey" button — an explicit fallback for browsers or situations where autofill doesn't trigger. Users add and remove passkeys from their account settings while signed in. Passkeys work with both platform authenticators (Touch ID, Face ID, Windows Hello, Android biometrics) and roaming security keys (e.g. YubiKey); the browser lets the user choose.

A few things to know:

  • WebAuthn requires a secure context. In production the app must be served over HTTPS; localhost is exempt for local development.
  • Passkey sign-in is a passwordless (non-credential) path, so it is not gated by the two-factor challenge by default — a passkey login completes in one step.

User Roles

Forge uses two roles managed via Better Auth's admin plugin:

Role Description
user Default role assigned on registration
admin Full access to the admin panel and user management (/users)

Admins can manage other users (ban, unban, change roles, delete) from the /users page. An admin cannot ban or delete their own account.

Social Auth

GitHub, Google and Discord OAuth are supported but not configured by default. To enable them, create OAuth apps and add your credentials to .env.

Replace {APP_URL} with your BETTER_AUTH_URL value (e.g. http://localhost:3000).

  • Discorddiscord.com/developers/applications → New Application → OAuth2

    • Redirects: {APP_URL}/api/auth/callback/discord
  • GitHubgithub.com/settings/developers → New OAuth App

    • Homepage URL: {APP_URL}
    • Callback URL: {APP_URL}/api/auth/callback/github
  • Googleconsole.cloud.google.com → APIs & Services → Credentials → Create OAuth 2.0 Client ID

    • Authorized JavaScript origins: {APP_URL}
    • Authorized redirect URI: {APP_URL}/api/auth/callback/google

File Storage

File uploads (such as profile avatars) are stored in any S3-compatible object storage — AWS S3, Cloudflare R2, MinIO and others, all work with the same variables.

For local development, the included docker-compose.yml runs MinIO and creates the bucket automatically. Add the following to .env:

S3_BUCKET=forge-uploads
S3_REGION=eu-central-raccoon-city
S3_ACCESS_KEY_ID=forge
S3_SECRET_ACCESS_KEY=forgeforge
S3_ENDPOINT=http://localhost:9000

CI

.github/workflows/build.yml runs next build on every push and pull request, so a broken build gets caught automatically instead of being found by hand. It needs no secrets or configuration — it fails the run on any build error and on any Turbopack warning.

Since this repo is meant to be cloned as a starting point, the workflow comes with it and will start running on your own fork the moment you push. If you don't want that, just delete the file.

About

Next.js boilerplate with Better Auth, Prisma, PostgreSQL and shadcn/ui.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages