Skip to content

gitcoder89431/vibe_client

Repository files navigation

vibe_client

CI License: MIT Next.js Convex

A Next.js + Convex starter pre-wired to authenticate with VibeAuth. Clone it, set two env vars, have working auth in 5 minutes.

Live demo: vibe.ruixen.app · auth via accounts.ruixen.app

vibe_client screenshot


Requires

A running VibeAuth instance — deploy one to Vercel in one click before continuing.


How it works

User → VibeAuth (sign in) → server-side relay fetches JWT
     → redirect to vibe_client?_vibe_token=<jwt>
     → JWT stored in localStorage → passed to Convex
     → Convex verifies JWT via JWKS → identity confirmed
  1. User clicks "Sign in" — redirected to your VibeAuth instance
  2. After sign-in, VibeAuth's server-side relay fetches a signed JWT and appends it to the redirect URL
  3. vibe_client reads _vibe_token from the URL on landing, stores it in localStorage
  4. ConvexProviderWithAuth passes the JWT to Convex on every request
  5. Convex verifies the JWT against VibeAuth's JWKS endpoint
  6. ctx.auth.getUserIdentity() returns the user's identity in any Convex function

The JWT flow works with any backend that can verify a JWT against a JWKS URL — Convex is what this template uses, but Express, FastAPI, or anything else works the same way.

Quick start

1. Clone and install

git clone https://github.com/gitcoder89431/vibe_client
cd vibe_client
pnpm install

2. Set up Convex

pnpm convex:dev

This creates your Convex project and generates convex/_generated/. Copy the NEXT_PUBLIC_CONVEX_URL it outputs.

3. Configure environment

cp .env.local.example .env.local

Fill in .env.local:

NEXT_PUBLIC_CONVEX_URL=https://your-project.convex.cloud
NEXT_PUBLIC_VIBE_AUTH_URL=https://accounts.yourdomain.com

4. Set env var in Convex dashboard

In convex.dev → your project → Settings → Environment Variables:

VIBE_AUTH_URL = https://accounts.yourdomain.com

This must match BETTER_AUTH_URL on your VibeAuth instance exactly.

5. Add your app's URL to VibeAuth trusted origins

In your VibeAuth admin → Settings → Trusted Origins, add your app's URL (e.g. http://localhost:3001).

6. Run

pnpm dev

Open http://localhost:3000.

Environment variables

Variable Where Description
NEXT_PUBLIC_CONVEX_URL .env.local Your Convex deployment URL
NEXT_PUBLIC_VIBE_AUTH_URL .env.local Your VibeAuth instance URL (e.g. https://accounts.yourdomain.com)
VIBE_AUTH_URL Convex Dashboard Same as above — used by Convex to verify JWTs

Project structure

src/
├── app/
│   ├── page.tsx          # Public home — sign in / dashboard link
│   ├── dashboard/
│   │   └── page.tsx      # Protected — redirects if not signed in
│   ├── providers.tsx     # ConvexProviderWithAuth wired to VibeAuth
│   └── layout.tsx
├── lib/
│   └── auth.ts           # JWT utilities — token storage, session parsing, sign out
convex/
├── auth.config.ts        # Points Convex to VibeAuth JWKS
├── schema.ts             # Users table
└── users.ts              # me query + getOrCreate mutation

Building a new app from this template

Clone this repo as your starting point. For each new project, only three things change:

Thing Action
Convex project Run pnpm convex:dev — new project, new NEXT_PUBLIC_CONVEX_URL
NEXT_PUBLIC_VIBE_AUTH_URL Point to your VibeAuth instance (e.g. https://accounts.yourdomain.com)
VIBE_AUTH_URL in Convex dashboard Same as above

These files copy as-is to every project — never touch them:

  • convex/auth.config.ts — points Convex to VibeAuth JWKS, identical everywhere
  • src/lib/auth.ts — JWT utilities, identical everywhere
  • src/app/providers.tsx — ConvexProviderWithAuth setup, identical everywhere

Schema pattern — anchor everything to vibeAuthId:

The sub claim in the JWT is the VibeAuth user ID and is consistent across all your apps — the same user gets the same ID everywhere.

// convex/schema.ts
defineTable({
    vibeAuthId: v.string(),  // = ctx.auth.getUserIdentity().subject
    email: v.string(),
    // ...your app-specific fields
}).index("by_vibeAuthId", ["vibeAuthId"])
// any Convex query/mutation — look up the current user
const identity = await ctx.auth.getUserIdentity()
const user = await ctx.db.query("users")
    .withIndex("by_vibeAuthId", q => q.eq("vibeAuthId", identity.subject))
    .first()

Connecting users to Convex

On first sign-in, call users.getOrCreate to create a Convex record linked to the user's VibeAuth ID:

const getOrCreate = useMutation(api.users.getOrCreate)

useEffect(() => {
  if (session) {
    getOrCreate({
      email: session.user.email,
      name: session.user.name ?? undefined,
    })
  }
}, [session])

After that, ctx.auth.getUserIdentity().subject in any Convex function is the user's VibeAuth user ID — use it to scope all data.

Stack

  • Next.js 15 — App Router, server components
  • Convex — real-time backend + database
  • VibeAuth — self-hosted auth hub (JWT issuer)

About

A Next.js + Convex starter pre-wired to authenticate with VibeAuth. Clone it, set two env vars, have working auth in 5 minutes.

Topics

Resources

License

Stars

Watchers

Forks

Contributors