Volcano/ Docs
Volcano DocumentationAuthenticationBackend

Function Integration

Access authenticated user context in your serverless functions.

Access authenticated user context in your serverless functions.

Receiving User Context

When a function is invoked with an auth user token, the user context is injected into the event:

exports.handler = async (event) => {
  // Check if user is authenticated
  if (!event.__volcano_auth) {
    return {
      statusCode: 401,
      body: JSON.stringify({ error: 'Unauthorized' })
    };
  }

  // Access user information
  const { user_id, email, project_id, role } = event.__volcano_auth;
  
  console.log('User ID:', user_id);
  console.log('Email:', email);
  console.log('Project:', project_id);
  console.log('Role:', role);  // "authenticated" or "anonymous"
};

User Context Fields

event.__volcano_auth = {
  user_id: 'uuid',           // Auth user's unique ID
  email: 'user@example.com', // User's email
  project_id: 'uuid',        // Project the user belongs to
  role: 'authenticated'      // "authenticated" or "anonymous"
}

Role values:

  • "authenticated" - User signed up with email/password or OAuth
  • "anonymous" - Guest user (created via signUpAnonymous())

Note: Both user types have a user_id. When an anonymous user converts to authenticated, their user_id remains the same - preserving all their data.

With service key

If invoked with a service key (not auth user token), __volcano_auth is undefined:

if (!event.__volcano_auth) {
  // Invoked with service key (service-to-service)
  // Or no authentication
}

Handling Different User Types

exports.handler = async (event) => {
  const auth = event.__volcano_auth;
  
  if (!auth) {
    return { statusCode: 401, body: 'Unauthorized' };
  }

  // Handle based on user type
  if (auth.role === 'anonymous') {
    // Guest user - maybe limit features
    return {
      statusCode: 200,
      body: JSON.stringify({
        data: limitedData,
        suggestion: 'Create an account to unlock all features'
      })
    };
  }

  // Full authenticated user
  return {
    statusCode: 200,
    body: JSON.stringify({ data: fullData })
  };
};

Database Integration

Set PostgreSQL session variables for Row-Level Security:

const { Pool } = require('pg');
const pool = new Pool({
  connectionString: process.env.DATABASE_URL
});

exports.handler = async (event) => {
  if (!event.__volcano_auth) {
    return { statusCode: 401, body: 'Unauthorized' };
  }

  const { user_id, email, role } = event.__volcano_auth;
  const client = await pool.connect();

  try {
    // Set user context in PostgreSQL session
    await client.query('SET request.jwt.claim.sub = $1', [user_id]);
    await client.query('SET request.jwt.claim.email = $1', [email]);
    await client.query('SET request.jwt.claim.role = $1', [role]);

    // Now RLS policies work automatically
    // Works for both authenticated and anonymous users
    const result = await client.query('SELECT * FROM posts');
    
    return {
      statusCode: 200,
      body: JSON.stringify(result.rows)
    };
  } finally {
    client.release();
  }
};

Complete Example

const { Pool } = require('pg');
const pool = new Pool({ connectionString: process.env.DATABASE_URL });

exports.handler = async (event) => {
  // 1. Verify authentication
  if (!event.__volcano_auth) {
    return { statusCode: 401, body: 'Unauthorized' };
  }

  const { user_id, email, role } = event.__volcano_auth;

  // 2. Get database connection
  const client = await pool.connect();

  try {
    // 3. Set user context for RLS
    await client.query('SET request.jwt.claim.sub = $1', [user_id]);
    await client.query('SET request.jwt.claim.role = $1', [role]);

    // 4. Handle different actions
    const { action } = event;

    switch (action) {
      case 'create_post':
        await client.query(
          'INSERT INTO posts (title, content) VALUES ($1, $2)',
          [event.title, event.content]
          // user_id automatically set by trigger
        );
        return { statusCode: 201, body: 'Post created' };

      case 'get_posts':
        // RLS automatically filters to user's posts
        // Works for both anonymous and authenticated users
        const result = await client.query('SELECT * FROM posts');
        return {
          statusCode: 200,
          body: JSON.stringify(result.rows)
        };

      default:
        return { statusCode: 400, body: 'Unknown action' };
    }
  } finally {
    client.release();
  }
};

Environment Variables

Volcano automatically injects these into your functions:

process.env.DATABASE_URL  // Your database connection string
// Plus any variables you configure

See Also

On this page