GoShipped
Product

πŸ” Authentication

Auth is already wired. You mostly need to decide how it should look and which signup flow you want.

GoShipped ships email signup, confirmation, login, logout, and password reset. Users persist in Postgres. Chat and settings stay behind a session.

You do not add an auth library. You decide who can register, where redirects go, and how the screens look.

Register / login UI
        ↓
Supabase Auth
        ↓
public.users  +  API JWT
        ↓
/chat

What you already have

FlowWhat happens
SignupEmail + password on /register. Confirmation email from Supabase Auth.
Email confirmationLink hits /auth/callback or /auth/confirm, then the app.
Login/login β†’ session cookie β†’ /chat
LogoutSign out from the user menu / account settings β†’ /login
Password reset/forgot-password β†’ email β†’ /update-password
Protected routes/chat, /settings, and related prefixes require a session
User rowTrigger handle_new_user inserts public.users from auth.users (the API can also create the row if it is missing)

Optional invite-only: set ALLOWED_EMAILS in apps/api/.env. Non-empty means only those addresses may call the API.

Auth email is not Resend

Two different inboxes

Supabase Auth sends confirmation and password-reset mail.

Resend sends product mail (welcome, trial, payment failed) when you turn EMAIL_ENABLED on.

EMAIL_ENABLED=false does not stop signup confirmation. See Emails.

Local Auth mail shows up in Mailpit (pnpm supabase:status prints the URL). Hosted Auth uses the Supabase inbox / your project's Auth SMTP.

Signup flag vs the real gate

features.signup only hides /register (the proxy sends people to /login).

To actually stop new accounts, disable β€œAllow new users to sign up” in the Supabase dashboard (local: enable_signup in supabase/config.toml). Use ALLOWED_EMAILS if the API should reject everyone else.

Onboarding

After the first confirmed login, /chat can show a first-run overlay.

  • Flag: features.onboarding
  • Copy and steps: apps/api/app/onboarding/config.py
  • Chrome: apps/web/src/components/onboarding/

When the flag is false, the API treats onboarding as completed. Welcome email is independent β€” it still uses Resend if product email is on.

Redirect URLs

pnpm dev serves HTTPS at https://localhost:3000. Confirmation links that open http://localhost:3000 will fail.

Set these in Supabase Auth URL configuration:

EnvironmentSet these
Local (hosted Supabase)Site URL https://localhost:3000. Allow /auth/callback and /auth/confirm on HTTPS (and HTTP if you still need it).
Local SupabaseAlready in supabase/config.toml (site_url + additional_redirect_urls)
ProductionSite URL = your web origin. Redirects: https://<web>/auth/callback and https://<web>/auth/confirm

After you change URLs, request a new email. Old links keep the old host.

The deploy wizard reminds you to set Production Auth URLs. They are not applied automatically.

What you probably want to customize

WhatWhere
Auth screens and copyapps/web/src/components/auth/
Brand on those screensstarter.config.json (name + logo)
Signup UI on/offfeatures.signup
Redirects / Site URLSupabase dashboard or supabase/config.toml
Confirmation / reset HTML (local)supabase/templates/confirmation.html, recovery.html
Confirmation / reset HTML (hosted)Dashboard β†’ Auth β†’ Email templates
Password minimumapps/web/src/config/auth.ts β€” keep in sync with minimum_password_length in config.toml
Invite-onlyDisable Supabase signup + optional ALLOWED_EMAILS

Session refresh lives in apps/web/src/proxy.ts (Next.js 16). You rarely need to touch it.

Try it locally

pnpm supabase:start    # if you use local Auth + Mailpit
pnpm dev
  1. Open /register
  2. Confirm in Mailpit (or the hosted inbox)
  3. Land on /chat
  4. Sign out, then /forgot-password

On this page