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_hereGoogle OAuth Flow
Web Application Flow
For web applications, use the standard OAuth redirect flow:
GET /api/auth/signin/googleThis redirects to Google OAuth consent screen with the following scopes:
https://www.googleapis.com/auth/calendar.readonlyhttps://www.googleapis.com/auth/calendar.events
Google redirects back to your application:
GET /api/auth/callback/googleDesktop 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');
}3. Handle Deep Link Callback
The backend redirects to a custom protocol scheme after successful authentication:
tella://auth/callback?session_token=encoded_session_token4. 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' };
}5. Deep Link Protocol Setup
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 activitySession Management
Get Current Session
GET /api/auth/sessionReturns current user session if authenticated.
Sign Out
POST /api/auth/signoutTerminates current session.
Account Management
Get Account Details
Retrieve user's linked accounts and active sessions:
GET /api/account/detailsResponse:
{
"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"
}Unlink Social Account
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