MonoStack
Functionalities

Authentication & Authorization

Session management, 2FA, access control, and multi-tenancy.

Package: @repo/auth Built on: Better Auth

Session Management

Sessions flow from locals.session → data.session via layout files.

In Svelte components, access via page data:

<script lang="ts">
  import { page } from '$app/state'

  const session = page.data.session
  const user = session?.user
</script>

{#if user}
  <p>Welcome, {user.name}</p>
{:else}
  <a href="/login">Sign in</a>
{/if}

Or via props when passed from layout:

<script lang="ts">
  const { data } = $props()
  const user = data.session?.user
</script>

In remote files (server-side):

// *.remote.ts
import { getUserSession } from '@repo/auth/server'

export const getProfile = query(async () => {
  const session = getUserSession()
  if (!session) throw new Error('Unauthorized')

  return db.select().from(users).where(eq(users.id, session.user.id))
})

Two-Factor Authentication (2FA)

TOTP-based 2FA is included in the boilerplate. See the security settings page for the implementation.

Gate-Based Access Control

Gates are functions that take a session and return a boolean. Use them for authorization checks.

Permission Gates

Use createPermissionGate for role/permission-based checks:

// domains/users/utils/gates.ts
import { createPermissionGate } from '$lib/library/authHelpers'

export function userDeleteGates(id: string) {
  return [createPermissionGate('users', ['delete'])]
}

Custom Gates

Create custom gates for complex logic:

// domains/organizations/utils/gates.ts
import type { Gate, Session } from '@repo/auth'
import { createPermissionGate } from '$lib/library/authHelpers'

export function userInOrganizationGate(organizationId: string): Gate {
  return (session: Session | null) =>
    session?.user?.organizations?.some((org) => org.id === organizationId) || false
}

export function organizationDeleteGates(id: string) {
  return [createPermissionGate('organizations', ['delete']), userInOrganizationGate(id)]
}

Check Permissions in Components

<script lang="ts">
  import { page } from '$app/state'
  import { hasAccess } from '@repo/auth'
  import { organizationDeleteGates } from '$lib/domains/organizations/utils/gates'

  const { params } = $props()
  const canDelete = await hasAccess(page.data.session, organizationDeleteGates(params.id))
</script>

{#if canDelete}
  <button class="text-red-500">Delete</button>
{/if}

Server-Side Authorization

Use validateSession with gates in remote files:

// organizations.remote.ts
import { form } from '$app/server'
import { validateSession } from '$lib/library/authHelpers.server'
import { organizationDeleteGates } from '$lib/domains/organizations/utils/gates'

export const deleteOrganization = form(schema, async ({ id }) => {
  await validateSession(organizationDeleteGates(id))

  // User is authorized, proceed with deletion
  return db.delete(organizations).where(eq(organizations.id, id))
})

Multi-Tenant Support

MonoStack supports multi-tenant applications with separate schemas per tenant.

Tenant-Scoped Queries

Use getTenantDb from your app's managed database to get a db connection scoped to the current organization:

// *.remote.ts
import { getTenantDb } from '$lib/db/managedDb.server'

export const getProducts = query(async () => {
  const { db } = await getTenantDb()
  return db.select().from(products)
})

Organization Switching

See the boilerplate's +layout.svelte for organization switching implementation.

Invitations

Invitations are managed via the /invitations route. See the boilerplate for the full implementation.

Permissions

Use PermissionsField for editing role permissions:

// domains/roles/utils/schemas.ts
import { PermissionsField } from '@repo/auth'
import { authDomains } from '$lib/library/domainActions'
import { zaf } from '@repo/form'

export const roleInsertSchema = createInsertSchema(rolesTable)
  .pick({ name: true })
  .extend({
    permissions: zaf(z.array(permissionInsertSchema).default([]), {
      component: PermissionsField,
      props: {
        domains: authDomains,
      },
    }),
  })

Full Documentation

See @repo/auth README for complete API reference.

On this page