Skip to main content

Overview

PassTru uses Supabase Authentication for secure user management. All API requests require a valid JWT token obtained through the authentication flow.

Authentication Methods

Supabase Auth supports multiple authentication methods:
  • Email/Password (primary method)
  • Magic Links
  • OAuth providers (Google, GitHub, etc.)
  • Phone/SMS

Sign Up

Create a new client account:
import { supabase } from '@/integrations/supabase/client'

const { data, error } = await supabase.auth.signUp({
  email: '[email protected]',
  password: 'secure-password',
  options: {
    data: {
      full_name: 'John Doe'
    }
  }
})

if (error) {
  console.error('Sign up error:', error.message)
} else {
  console.log('User created:', data.user)
}

Response

user
object
The authenticated user object
session
object | null
Session object with access and refresh tokens

Sign In

Authenticate existing users:
const { data, error } = await supabase.auth.signInWithPassword({
  email: '[email protected]',
  password: 'secure-password'
})

if (error) {
  console.error('Sign in error:', error.message)
} else {
  console.log('Signed in:', data.session)
}

Get Current Session

Retrieve the active session:
const { data: { session } } = await supabase.auth.getSession()

if (session) {
  console.log('Access token:', session.access_token)
  console.log('User:', session.user)
}

Get Current User

Fetch user details with organization and role information:
// Get authenticated user
const { data: { user } } = await supabase.auth.getUser()

if (user) {
  // Fetch user role
  const { data: roleData } = await supabase
    .from('user_roles')
    .select('role')
    .eq('user_id', user.id)
    .single()

  // Fetch organization
  const { data: org } = await supabase
    .from('organizations')
    .select('id, slug, name, logo_url')
    .eq('owner_id', user.id)
    .single()

  // Fetch profile
  const { data: profile } = await supabase
    .from('profiles')
    .select('full_name, avatar_url')
    .eq('user_id', user.id)
    .single()

  console.log({
    user,
    role: roleData?.role,
    organization: org,
    profile
  })
}

Sign Out

End the user session:
const { error } = await supabase.auth.signOut()

if (error) {
  console.error('Sign out error:', error.message)
} else {
  console.log('Signed out successfully')
}

Password Reset

Request Password Reset

const { error } = await supabase.auth.resetPasswordForEmail(
  '[email protected]',
  {
    redirectTo: 'https://yourapp.com/reset-password'
  }
)

if (error) {
  console.error('Reset request error:', error.message)
} else {
  console.log('Reset email sent')
}

Update Password

const { error } = await supabase.auth.updateUser({
  password: 'new-secure-password'
})

if (error) {
  console.error('Password update error:', error.message)
} else {
  console.log('Password updated successfully')
}

Authorization

Check User Role

Use the has_role function to verify user permissions:
const { data: isClient } = await supabase.rpc('has_role', {
  _user_id: user.id,
  _role: 'client'
})

if (isClient) {
  // User has client role
}

Check Organization Membership

Verify if a user belongs to an organization:
const { data: isMember } = await supabase.rpc('is_org_member', {
  _user_id: user.id,
  _org_id: organizationId
})

if (isMember) {
  // User is a member of the organization
}

Session Management

Listen for Auth Changes

supabase.auth.onAuthStateChange((event, session) => {
  switch (event) {
    case 'SIGNED_IN':
      console.log('User signed in:', session?.user)
      break
    case 'SIGNED_OUT':
      console.log('User signed out')
      break
    case 'TOKEN_REFRESHED':
      console.log('Token refreshed:', session?.access_token)
      break
    case 'USER_UPDATED':
      console.log('User updated:', session?.user)
      break
  }
})

Automatic Token Refresh

Supabase automatically refreshes tokens before they expire when autoRefreshToken is enabled in the client configuration.

Making Authenticated Requests

All Supabase client requests automatically include the auth token:
// The Authorization header is automatically added
const { data: events } = await supabase
  .from('events')
  .select('*')
  .eq('organization_id', orgId)

// For edge functions, the token is also included automatically
const { data } = await supabase.functions.invoke('send-confirmation-email', {
  body: { attendee_ids: ['uuid-1', 'uuid-2'] }
})

Row Level Security (RLS)

PassTru uses PostgreSQL Row Level Security to enforce access control at the database level. Users can only access data they own or have been granted permission to view.

Example RLS Policies

Events: Users can only access events from their organization
CREATE POLICY "Users can view their organization's events"
ON events FOR SELECT
USING (
  organization_id IN (
    SELECT id FROM organizations WHERE owner_id = auth.uid()
  )
);
Attendees: Users can only manage attendees from events they own or are assigned to
CREATE POLICY "Users can manage attendees from their events"
ON attendees FOR ALL
USING (
  event_id IN (
    SELECT id FROM events WHERE organization_id IN (
      SELECT id FROM organizations WHERE owner_id = auth.uid()
    )
  )
);

Security Best Practices

Never expose your SUPABASE_SERVICE_ROLE_KEY in client-side code. Use it only in secure server environments.
  1. Use environment variables for API keys
  2. Validate user input before database operations
  3. Implement rate limiting for sensitive operations
  4. Enable MFA for admin accounts
  5. Rotate tokens regularly
  6. Monitor auth logs for suspicious activity

Next Steps

Events API

Start creating and managing events

Attendees API

Add attendees to your events

Build docs developers (and LLMs) love