Skip to content

πŸ” Authentication (MVP)

Overview

Simple authentication with Kinde Auth handling the complexity.

Authentication Flow

1. User Login

User β†’ GetCimple Login β†’ Kinde OAuth β†’ Back to GetCimple

2. Session Management

  • Kinde handles sessions
  • 7-day refresh tokens
  • Automatic token refresh
  • Logout clears session

3. Multi-Tenant Access

  • User can belong to multiple orgs
  • Org switcher in app header
  • Remember last selected org
  • Directors see all their orgs

Implementation

Frontend (SvelteKit)

// +page.server.ts
import { redirect } from '@sveltejs/kit'

export async function load({ locals }) {
  if (!locals.user) {
    throw redirect(303, '/login')
  }

  return {
    user: locals.user,
    organization: locals.organization,
  }
}

Kinde Configuration

// Basic setup
const kinde = {
  domain: 'https://getcimple.kinde.com',
  clientId: process.env.KINDE_CLIENT_ID,
  redirectUri: 'https://app.getcimple.com/callback',
  logoutUri: 'https://app.getcimple.com',
}

User Roles

director - Board member, read-only + approvals
manager - Full access within org
staff - Limited access, task completion

Protected Routest

Route Protection

// hooks.server.ts
export async function handle({ event, resolve }) {
  // Check auth for protected routes
  if (event.url.pathname.startsWith('/app')) {
    const session = await getSession(event)
    if (!session) {
      return redirect(303, '/login')
    }
  }

  return resolve(event)
}

What We're Using

  • Kinde Auth: Handles all auth complexity
  • Cookie Sessions: Secure, httpOnly
  • Role-Based Access: Simple 3-role system
  • Multi-Tenant: Built into Kinde

What We're NOT Doing (Yet)

  • Custom auth implementation
  • Social logins (just email/password)
  • 2FA (available in Kinde when needed)
  • API keys (session auth only)
  • Complex permission matrices

Security Checklist

  • HTTPS only (Cloudflare handles)
  • Secure session cookies
  • CSRF protection (SvelteKit built-in)
  • Rate limiting (Cloudflare)
  • Password requirements (Kinde defaults)

Common Patterns

Check User Role

function canApprove(user: User): boolean {
  return user.role === 'director' || user.role === 'manager'
}

Get User's Organizations

async function getUserOrgs(userId: string) {
  return db
    .from('organization_users')
    .select('organizations(*)')
    .eq('user_id', userId)
}

Switch Organization

async function switchOrg(orgId: string) {
  // Set cookie
  cookies.set('current_org', orgId, {
    path: '/',
    httpOnly: true,
    secure: true,
  })

  // Redirect to dashboard
  throw redirect(303, '/app/dashboard')
}

Keep auth simple. Let Kinde handle the hard parts. Focus on the app.