TellaAI
API Reference/Authentication

Authentication

User authentication and session management in SophieAI

Authentication

SophieAI uses Better-auth for secure user authentication and session management. This guide covers all authentication-related endpoints and integration patterns.

Authentication Methods

The authentication system supports:

  • Email/Password: Traditional email and password authentication
  • Google OAuth: Google social login with calendar permissions
  • Session Management: Secure session tokens with automatic renewal
  • Account Verification: Email verification for new accounts
  • Password Reset: Secure password reset flow

Client-Side Integration

For easy frontend integration with the authentication system, you can use the Better Auth client-side utilities. The SophieAI backend is fully compatible with Better Auth's client-side implementation.

Recommended approach: Follow the Better Auth Client-Side Documentation for seamless integration with your frontend application.

The client-side package provides:

  • Automatic session management
  • Type-safe authentication methods
  • Built-in error handling
  • Reactive session state
  • OAuth flow helpers

Base Configuration

Base URL: http://localhost:5000 (development)

Authentication Endpoints: /api/auth/* Account Management Endpoints: /api/account/*

Session Duration: Configurable (default: persistent until logout)

Authentication Endpoints

Email/Password Sign Up

Create a new account with email and password:

POST /api/auth/signup
Content-Type: application/json

{
  "name": "John Doe",
  "email": "john@example.com", 
  "password": "securePassword123"
}

Email/Password Sign In

Authenticate with existing credentials:

POST /api/auth/signin
Content-Type: application/json

{
  "email": "john@example.com",
  "password": "securePassword123"
}

Session Validation

Include session token in subsequent requests:

Cookie: session_token=your_session_token_here

Google OAuth Flow

Web Application Flow

For web applications, use the standard OAuth redirect flow:

GET /api/auth/signin/google

This redirects to Google OAuth consent screen with the following scopes:

  • https://www.googleapis.com/auth/calendar.readonly
  • https://www.googleapis.com/auth/calendar.events

Google redirects back to your application:

GET /api/auth/callback/google

Desktop Application Flow

For desktop applications (like Electron), the OAuth flow requires special handling to open the system browser and handle deep link callbacks.

1. Initiate OAuth

Make a POST request to get the OAuth URL:

POST /api/auth/sign-in/social
Content-Type: application/json

{
  "provider": "google"
}

Response:

{
  "url": "https://accounts.google.com/oauth/authorize?client_id=..."
}

2. Open System Browser

Open the OAuth URL in the system browser (not within the app):

// Desktop implementation example
if (window.electronAPI && window.electronAPI.openExternal) {
  await window.electronAPI.openExternal(oauthData.url);
} else {
  window.open(oauthData.url, '_blank');
}

The backend redirects to a custom protocol scheme after successful authentication:

tella://auth/callback?session_token=encoded_session_token

4. Process Callback in Desktop App

Extract and store the session token from the deep link:

// Example callback handler
async handleOAuthCallback(callbackData: any): Promise<AuthResponse> {
  if (callbackData.session_token) {
    // Decode and extract the actual token
    const sessionToken = decodeURIComponent(callbackData.session_token);
    const actualToken = sessionToken.split('.')[0];
    
    // Store token for future requests
    localStorage.setItem('bearer_token', actualToken);
    
    // Verify session
    const session = await this.getSession();
    if (session && session.user) {
      return { success: true, user: session.user };
    }
  }
  
  return { success: false, error: 'OAuth callback failed' };
}

Register your custom protocol scheme in your Electron app:

// main.ts
const PROTOCOL_SCHEME = 'tella';

// Register protocol handler
if (process.defaultApp) {
  if (process.argv.length >= 2) {
    app.setAsDefaultProtocolClient(PROTOCOL_SCHEME, process.execPath, [path.resolve(process.argv[1])]);
  }
} else {
  app.setAsDefaultProtocolClient(PROTOCOL_SCHEME);
}

// Handle deep link events
app.on('open-url', (event, url) => {
  event.preventDefault();
  handleDeeplink(url);
});

6. Backend Configuration

The backend automatically detects desktop OAuth callbacks and redirects to the custom protocol:

// Backend redirect logic
if (url.includes('/callback/google') || url.includes('google')) {
  const redirectUrl = 'tella://auth/callback';
  const params = new URLSearchParams({
    session_token: encodeURIComponent(`${sessionToken}.${userId}`)
  });
  return `${redirectUrl}?${params.toString()}`;
}

Android Application Flow

For Android applications, implement OAuth using Android's built-in browser and intent filters.

1. Get OAuth URL

Same as desktop - POST to /api/auth/sign-in/social with provider: "google"

2. Open Chrome Custom Tabs

// Open OAuth URL in Chrome Custom Tabs
val intent = CustomTabsIntent.Builder().build()
intent.launchUrl(context, Uri.parse(oauthUrl))

3. Handle Intent Filter

Configure your app to handle the callback URL:

<!-- AndroidManifest.xml -->
<activity android:name=".OAuthCallbackActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="yourapp" android:host="auth" />
    </intent-filter>
</activity>

4. Extract Session Token

// In OAuthCallbackActivity
val sessionToken = intent.data?.getQueryParameter("session_token")
// Store token and redirect to main activity

Session Management

Get Current Session

GET /api/auth/session

Returns current user session if authenticated.

Sign Out

POST /api/auth/signout

Terminates current session.

Account Management

Get Account Details

Retrieve user's linked accounts and active sessions:

GET /api/account/details

Response:

{
  "success": true,
  "data": {
    "accounts": [
      {
        "id": "account_clx1234567890",
        "providerId": "google",
        "accountId": "google_account_id",
        "createdAt": "2024-01-01T00:00:00.000Z",
        "accessToken": true,
        "refreshToken": true
      }
    ],
    "sessions": [
      {
        "id": "session_clx0987654321",
        "createdAt": "2024-01-01T00:00:00.000Z",
        "updatedAt": "2024-01-01T12:00:00.000Z",
        "expiresAt": "2024-01-08T00:00:00.000Z",
        "ipAddress": "192.168.1.1",
        "userAgent": "Mozilla/5.0..."
      }
    ]
  },
  "message": "Account details retrieved successfully"
}

Remove a linked social account:

POST /api/account/unlink
Content-Type: application/json

{
  "accountId": "account_clx1234567890"
}

Response:

{
  "success": true,
  "message": "Account unlinked successfully"
}

Revoke Session

Terminate a specific session:

POST /api/account/revoke-session
Content-Type: application/json

{
  "sessionId": "session_clx0987654321"
}

Response:

{
  "success": true,
  "message": "Session revoked successfully"
}

Response Formats

Successful Authentication Response

{
  "user": {
    "id": "user_clx1234567890",
    "name": "John Doe",
    "email": "john@example.com",
    "emailVerified": true,
    "image": "https://lh3.googleusercontent.com/...",
    "createdAt": "2024-01-01T00:00:00.000Z",
    "updatedAt": "2024-01-01T12:00:00.000Z"
  },
  "session": {
    "id": "session_clx0987654321",
    "expiresAt": "2024-01-08T00:00:00.000Z",
    "token": "session_token_here",
    "createdAt": "2024-01-01T00:00:00.000Z",
    "updatedAt": "2024-01-01T00:00:00.000Z",
    "ipAddress": "192.168.1.1",
    "userAgent": "Mozilla/5.0..."
  }
}

Error Response

{
  "success": false,
  "error": "Invalid credentials",
  "code": "INVALID_CREDENTIALS"
}

Session Security

  • Sessions are stored in PostgreSQL database
  • Automatic session renewal on authenticated requests
  • IP address and user agent tracking for security
  • Secure session token generation
  • Cross-origin protection with trusted origins