Source: https://www.hype-stack.dev/docs/packs-templates/packs/starter-saas-workos

# SaaS Starter (WorkOS)

Authentication, organizations, roles, sessions, feature flags, and an admin app, powered by
[WorkOS](https://workos.com). This is the `starter-saas-workos` pack. Pick it over Better Auth when you want WorkOS to
own identity: hosted user management, enterprise SSO, and directory sync down the line.

```bash
npx @hype-stack/cli compose --packs starter-saas-workos,layout-basic
```

## What you get

The same product as [SaaS Starter (Better Auth)](/docs/packs-templates/packs/starter-saas-betterauth): sign-in,
organizations with invitations and roles, onboarding, per-organization
[feature flags](/docs/backend/organizations#feature-flags), the settings tabs, the full
[admin app](/docs/packs-templates/packs/admin), and the mobile and extension slots. The difference is where identity
lives. Users, organizations, and memberships are WorkOS records read through its API, so the starter's Prisma schema is
thin: invitations, admins, feature flag overrides, and settings. Sessions are sealed with `WORKOS_COOKIE_PASSWORD`.

For session flow, middleware, and route guards, see [Authentication](/docs/backend/authentication) and
[Organizations](/docs/backend/organizations).

## What you need

- A **WorkOS** account and project. Sign up at [dashboard.workos.com](https://dashboard.workos.com).
- **Google** enabled as a social provider inside WorkOS (for the "Login with Google" button). WorkOS handles the Google
  integration for you, so you do not register a Google OAuth app yourself.
- **Resend** for transactional email (password reset, org invites). The starter uses the base template's
  `RESEND_API_KEY`.
- Postgres and Valkey (base services).

## WorkOS credentials

In your WorkOS dashboard, grab:

- **Client ID** (`WORKOS_CLIENT_ID`)
- **API Key** (`WORKOS_API_KEY`)
- A **cookie password** of at least 32 characters (`WORKOS_COOKIE_PASSWORD`). Generate one with
  `openssl rand -base64 32`.

Put these in `apps/backend/.env`.

## Turn off the WorkOS password reset email

WorkOS sends its own password reset email by default. The starter already sends one through Resend, so with the default
left on, every reset lands in the inbox twice: one from your app and one from WorkOS.

In the WorkOS dashboard, open **Emails > Configuration**, click **Manage**, and disable **Password reset**. You need the
Admin role to change it. Do it in each environment (staging and production are configured separately).

## Google OAuth through WorkOS

Enable Google under **Authentication > Google** in your WorkOS dashboard. WorkOS gives you the redirect URL to paste
into Google; you don't manage the Google app directly.

The pack starts Google sign-in from `GET /auth/google/:intent` and passes this redirect URI to WorkOS:

```
http://localhost:3000/auth/google/callback
```

Register that exact URL in **WorkOS > Dashboard > Redirects**. In production, swap the origin for your deployed domain:

```
https://yourdomain.com/auth/google/callback
```

## Environment variables

| Variable                       | Purpose                                                              |
| ------------------------------ | -------------------------------------------------------------------- |
| `WORKOS_CLIENT_ID`             | WorkOS project client ID                                             |
| `WORKOS_API_KEY`               | WorkOS server API key                                                |
| `WORKOS_COOKIE_PASSWORD`       | Session cookie encryption key. The CLI generates one at install time |
| `WORKOS_GOOGLE_OAUTH_CALLBACK` | Google OAuth redirect URI (see above)                                |
| `RESEND_API_KEY`               | Transactional email (password reset, invites)                        |
| `SUPER_ADMIN_EMAIL`            | Bootstrap admin emails for the admin app                             |

## Admin app

This starter also ships a separate [admin app](/docs/packs-templates/packs/admin) for managing users and organizations.
Set `SUPER_ADMIN_EMAIL` to sign in the first time.
