Articles

Magic link and Google auth for a small SaaS

I do not like starting a SaaS with password auth.

Passwords create work that has nothing to do with the product: password resets, hashing choices, credential stuffing, breach concerns, and support emails from users who cannot log in.

For most small SaaS products, I prefer magic link plus Google Sign-In.

A magic link is passwordless login by email. The user enters an email address, gets a link, clicks it, and the app creates a session.

That gives you a clean first version:

  • no password field
  • no reset flow
  • no password storage
  • no separate signup form
  • fewer support paths

The login can also be the signup. If the email does not exist yet, create the user after the token is verified.

Stacknaut uses that model.

The email part matters

Magic links depend on email delivery. If the email is slow or lands in spam, auth feels broken.

I treat transactional email as part of the auth system. Postmark describes transactional email as one-to-one email triggered by user action. A login email is exactly that.

In Stacknaut, magic link emails go through Postmark, and the backend stores a short-lived token and code before sending.

The code fallback matters. Sometimes the user opens email on a different device, or the link gets mangled. A six-digit code gives them another way in without asking support.

Why add Google Sign-In

Some users do not want to check email. Google Sign-In gives them a faster path.

Google Identity Services requires an OAuth client ID for the web app. The frontend gets an ID token from Google, then the backend verifies that token server-side before creating a session.

The important detail: the app does not trust the browser just because it says Google logged someone in. The backend verifies the token.

How Stacknaut connects both

Both auth methods feed into the same user table.

Email is the primary identity. A user can start with a magic link and later use Google Sign-In with the same email. Or the other way around.

That keeps the product simple:

  • one users table
  • one session model
  • one backend auth hook
  • one frontend store
  • two login methods

No account-linking UI for the first version.

Session shape

Stacknaut uses backend-signed JWT sessions.

The frontend stores the token and sends it as a bearer token. The backend verifies the token and loads the user by email before protected routes run.

This is not the only valid session design. Cookie-based sessions can be a good choice too. For this starter kit, JWT keeps the moving parts small and avoids a session table.

The important part is not JWT specifically. It is that auth checks happen on the backend, and every protected API route uses the same verification path.

The agent benefit

Auth code spreads if you let it.

I want the coding agent to see a small number of obvious files:

backend/src/controllers/magicLinkController.ts
backend/src/controllers/googleAuthController.ts
backend/src/controllers/verifyJWTToken.ts
frontend/src/stores/appStore.ts
shared/src/schemas/users.ts
shared/src/schemas/magicLinks.ts

When the agent adds a protected route, it should reuse the existing backend auth hook. Login UI changes should call the existing store methods. User fields belong in the shared schema.

That is the point of shipping auth in the starter kit. Not just "there is a login page", but "there is a pattern the agent can keep following".

See the implementation in Stacknaut's authentication docs.

Production SaaS starting point

Use the foundation your checklist assumes.

Stacknaut packages the recurring SaaS work into repos your coding agent can inspect, modify, and deploy.

What you get in Stacknaut

  • Auth, billing, shared types, API structure, jobs, logging, and prerendered pages
  • Agent instructions and skills that keep future changes consistent
  • Kamal + Hetzner deployment so launch work has a clear finish line

2e75a5fa