Base44 Authentication Guide

Understanding authentication in Base44 apps

IMPORTANT: Two Types of Apps

1. TricklAI Admin Platform (THIS APP)

  • Managed by YOU (the platform owner)
  • Admin users are invited through Dashboard → Users
  • Uses Base44's built-in authentication
  • Only needs a few admin users to manage the platform

2. Micro Apps (Built by other developers)

  • Can have their OWN login/signup systems
  • Can have THOUSANDS of end users
  • Manage their own user authentication however they want
  • Use TricklAI API key to access SMS/Email services
  • Only need to track SMS opt-ins (stored in TricklAI)

📝 Summary:

TricklAI Admin: Small number of admins, invited manually
Micro Apps: Unlimited end users, they handle their own auth

For TricklAI Admin Platform ONLY

This applies ONLY to the TricklAI admin dashboard (this app):

  • DON'T create login pages - Base44 handles it
  • Admin users are invited through Dashboard → Users
  • You only need a few admins to manage the platform

✅ For Micro Apps (separate apps):

Micro apps can create their own login/signup flows for their end users!

How Authentication Works in Base44 (for TricklAI Admin)

1. User Entity is Built-In

Every Base44 app has a User entity automatically created with these fields:

  • id
  • email
  • full_name
  • role (either 'admin' or 'user')
  • created_date

2. Users Are Invited, Not Registered

Users don't "sign up" - they're invited through the Base44 dashboard:

  • Go to Dashboard → Users → Invite User
  • Enter their email and role
  • They receive an invitation email
  • They click the link and create their password

3. Login Redirect

When a user needs to login, redirect them to the Base44 login page:

import { base44 } from "@/api/base44Client";

// Redirect to login (will return to current page after login)
base44.auth.redirectToLogin();

// Or redirect to a specific page after login
base44.auth.redirectToLogin(createPageUrl("Dashboard"));
How Micro Apps Use TricklAI

Step 1: Micro App Has Their Own Users

Example: "Equipment Rentals App" can have signup/login however they want:

// Equipment Rentals App can have normal signup
export default function Signup() {
  const handleSignup = async (email, password) => {
    // Create user in THEIR database
    await theirOwnAuth.createUser(email, password);
  };
}

Step 2: User Opts In to SMS

When user wants SMS notifications, redirect to TricklAI opt-in page:

// In Equipment Rentals App
<a href="https://apps.tricklai.com/pages/SMSOptIn?app=equipment-rentals">
  Enable SMS Notifications
</a>

// Or embed the opt-in form in their app
// User enters phone and consents - stored in TricklAI

Step 3: Micro App Sends SMS

Equipment Rentals App calls TricklAI API with THEIR api key:

// Equipment Rentals backend
fetch('https://apps.tricklai.com/functions/sendSMS', {
  method: 'POST',
  headers: {
    'x-trickl-api-key': 'their_api_key_from_tricklai',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    phone_number: '+1234567890',
    message: 'Your rental is ready for pickup!'
  })
});

// TricklAI checks:
// 1. Is the API key valid? ✓
// 2. Does this app have SMS enabled? ✓
// 3. Has this user opted in? ✓
// Then sends the SMS!
Which Authentication Pattern to Use

✅ Use Base44 Built-in Auth When:

  • Building TricklAI admin platform pages
  • You need only a few admin users
  • You want quick, secure auth without building it
// For TricklAI admin pages
useEffect(() => {
  base44.auth.me()
    .then(setUser)
    .catch(() => base44.auth.redirectToLogin());
}, []);

✅ Build Your Own Auth When:

  • Building a micro app for end users
  • You expect many users (hundreds/thousands)
  • You want custom signup flows
  • Your app is separate from TricklAI admin
// For micro apps - can use any auth
// Firebase, Auth0, Clerk, or custom
export default function Signup() {
  const handleSignup = async () => {
    await yourAuth.signUp(email, password);
    // Then optionally redirect to TricklAI for SMS opt-in
  };
}
Authentication Patterns for TricklAI Admin Pages

✅ Pattern 1: Check if user is logged in

import { base44 } from "@/api/base44Client";
import { useEffect, useState } from "react";
import { createPageUrl } from "@/utils";

export default function MyPage() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const checkAuth = async () => {
      try {
        const currentUser = await base44.auth.me();
        setUser(currentUser);
      } catch (error) {
        // Not logged in - redirect to login
        base44.auth.redirectToLogin(createPageUrl("MyPage"));
      }
      setLoading(false);
    };
    checkAuth();
  }, []);

  if (loading) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>Welcome, {user.full_name}!</h1>
    </div>
  );
}

✅ Pattern 2: Public pages that work for everyone

import { base44 } from "@/api/base44Client";
import { useEffect, useState } from "react";

export default function PublicPage() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const checkAuth = async () => {
      try {
        const currentUser = await base44.auth.me();
        setUser(currentUser);
      } catch (error) {
        // Not logged in - that's okay for public pages
        console.log("User not logged in");
      }
    };
    checkAuth();
  }, []);

  return (
    <div>
      <h1>Public Content</h1>
      {user ? (
        <p>Welcome back, {user.full_name}!</p>
      ) : (
        <button onClick={() => base44.auth.redirectToLogin()}>
          Login
        </button>
      )}
    </div>
  );
}

✅ Pattern 3: Logout

import { base44 } from "@/api/base44Client";

// Simple logout (reloads the page)
<button onClick={() => base44.auth.logout()}>
  Logout
</button>

// Logout and redirect to specific page
<button onClick={() => base44.auth.logout(createPageUrl("Home"))}>
  Logout
</button>
Common Mistakes That Cause Loops (for TricklAI Admin)

❌ Creating a Login page

// DON'T DO THIS for TricklAI Admin!
export default function Login() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  
  const handleLogin = async () => {
    // This will NOT work in Base44 for admin users!
    await someLoginFunction(email, password);
  };
  // ... rest of login form
}

Instead: Use base44.auth.redirectToLogin()

❌ Creating a signup/register page

// DON'T DO THIS for TricklAI Admin!
export default function Signup() {
  const handleSignup = async () => {
    await base44.entities.User.create({
      email: email,
      password: password
    });
  };
  // ... rest of signup form
}

Instead: Admin users are invited through the Base44 dashboard

❌ Trying to manage authentication state manually

// DON'T DO THIS for TricklAI Admin!
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [authToken, setAuthToken] = useState(null);

// Manual authentication management causes loops!

Instead: Use base44.auth.me() and let Base44 handle the session

Adding Custom User Fields (for TricklAI Admin)

You CAN add custom fields to the Base44 User entity, but DON'T override built-in fields:

// ✅ CORRECT: Add custom fields to User entity
// File: entities/User.json
{
  "name": "User",
  "type": "object",
  "properties": {
    "phone_number": {
      "type": "string",
      "description": "User's phone number"
    },
    "company": {
      "type": "string",
      "description": "User's company name"
    },
    "preferences": {
      "type": "object",
      "description": "User preferences"
    }
  }
}

// Note: DON'T include id, email, full_name, role - they're automatic!

To update user data:

import { base44 } from "@/api/base44Client";

// Update current user's custom fields
await base44.auth.updateMe({
  phone_number: "+1234567890",
  company: "Acme Corp"
});

// Access the data
const user = await base44.auth.me();
console.log(user.phone_number); // "+1234567890"
Quick Fixes for Authentication Loops (in TricklAI Admin)
  1. Delete any Login/Signup pages

    Base44 handles this automatically

  2. Replace login forms with:
    <button onClick={() => base44.auth.redirectToLogin()}>
      Login
    </button>
  3. In protected pages, check auth like this:
    useEffect(() => {
      base44.auth.me()
        .then(setUser)
        .catch(() => base44.auth.redirectToLogin());
    }, []);
  4. Invite users through dashboard, not code

    Dashboard → Users → Invite User