Skip to content

Quick Start: Testing Authentication with the SDK

This guide will help you test the new JWT authentication system end-to-end using the Foundry SDK.

Prerequisites

  • .NET 10 SDK installed
  • Node.js 18+ and pnpm installed
  • PostgreSQL running (via Aspire or standalone)

Step 1: Start the Backend

# From repo root
cd src/Aspire/LBS.AspireHost
dotnet run

Wait for the Aspire dashboard to start. The API will be available at http://localhost:5000.

Step 2: Create a Test User

You need a user account to test login. There are several ways to create one:

Create create-test-user.py in the repo root:

import asyncio
from foundry_sdk import FoundryClient, FoundryClientConfig
from foundry_sdk.modules.core.imports import user_seeding

async def create_user():
    # Note: For user seeding, you'll need an admin token or service account
    # For now, create the user directly in the database or use the backend endpoints

    client = FoundryClient(FoundryClientConfig(
        base_url='http://localhost:5000'
    ))

    # If you have an admin token, use this:
    # client.config.auth_token = 'your-admin-token'

    result = await client.import_data('UserSeedingImport', user_seeding(
        enabled=True,
        skip_if_users_exist=False,
        users=[{
            'email': 'test@example.com',
            'display_name': 'Test User',
            'password': 'TestPassword123!',  # Add this field to the import contract
            'roles': ['Member']
        }]
    ))

    print(f"User created: {result.success}")

asyncio.run(create_user())

Option B: Create User via SQL (Quick and Easy)

-- Connect to PostgreSQL
-- Database: foundry_dev (or your configured database)

INSERT INTO mt_doc_user_contract (id, data, mt_last_modified)
VALUES (
    gen_random_uuid(),
    '{
        "Id": "' || gen_random_uuid()::text || '",
        "Email": "test@example.com",
        "DisplayName": "Test User",
        "PasswordHash": "$2a$11$X8K9yZQQwVfR5vGqJKL.1eYJxRXY7FZhPZ8yGKL.1eYJxRXY7FZhP",
        "Roles": ["Member"],
        "IsActive": true,
        "CreatedAt": "' || NOW()::text || '"
    }'::jsonb,
    NOW()
);

Note: The password hash above is for password123. You'll need to generate proper password hashes in production.

Option C: Use the Backend CreateUser Command (Best Practice)

This requires authentication setup, which is what we're testing, so skip this for now.

Step 3: Set Up Environment Variables

For foundry-web

cd src/Apps/foundry-web
cp .env.example .env

Edit .env:

VITE_API_BASE_URL=http://localhost:5000

Step 4: Start foundry-web

cd src/Apps/foundry-web
pnpm install  # If not already installed
pnpm dev

The app will be available at http://localhost:5173.

Step 5: Test Login

  1. Navigate to http://localhost:5173/login
  2. Enter credentials:
  3. Email: test@example.com
  4. Password: password123 (or whatever you set)
  5. Click "Sign in"

Expected Behavior:

Success: - You should be redirected to / (home page) - Check browser console: Should see no errors - Check localStorage: Should see foundry_access_token and foundry_refresh_token - Check Network tab: Should see POST to /auth/login with 200 response

Failure: - 401 Unauthorized: Check username/password - CORS error: Check backend CORS configuration - Network error: Check backend is running - 500 Server error: Check backend logs in Aspire dashboard

Step 6: Test Token Refresh (Optional)

Open browser console after successful login:

// Get the auth store
const auth = window.__FOUNDRY_AUTH_STORE__;

// Wait for token to expire (default: 60 seconds)
console.log('Waiting 65 seconds for token to expire...');
await new Promise(resolve => setTimeout(resolve, 65000));

// Try to use an authenticated endpoint
// The SDK should automatically refresh the token
const result = await auth.client.getCurrentUser();
console.log('Token auto-refreshed!', result);

Step 7: Test with Python SDK (Optional)

Create test-login.py:

import asyncio
from foundry_sdk import FoundryClient, FoundryClientConfig, FoundryError

async def test_login():
    client = FoundryClient(FoundryClientConfig(
        base_url='http://localhost:5000',
        auto_refresh=True
    ))

    try:
        # Login
        print('Logging in...')
        result = await client.login('test@example.com', 'password123')

        print(f'Login successful!')
        print(f'   User: {result.user.email}')
        print(f'   Roles: {result.user.roles}')
        print(f'   Token expires in: {result.expires_in} seconds')

        # Test authenticated endpoint
        print('\nGetting current user...')
        user = await client.get_current_user()
        print(f'Current user: {user.email}')

        # Logout
        print('\nLogging out...')
        await client.logout()
        print('Logged out successfully')

    except FoundryError as e:
        print(f'Error: {e}')
        if e.status_code:
            print(f'   Status code: {e.status_code}')

asyncio.run(test_login())

Run it:

# Install SDK first
pip install -e sdk/python

# Run test
python test-login.py

Troubleshooting

Login Returns 401 Unauthorized

Check: 1. User exists in database 2. Password is correct 3. Backend is running (dotnet run in AspireHost) 4. Check backend logs in Aspire dashboard

Fix:

# Verify backend is running
curl http://localhost:5000/health

# Check user exists
# Connect to PostgreSQL and run:
SELECT data->>'Email', data->>'DisplayName'
FROM mt_doc_user_contract
WHERE data->>'Email' = 'test@example.com';

CORS Errors in Browser

Check: Backend CORS configuration in src/Aspire/LBS.AspireHost/Program.cs

Fix: Ensure CORS allows http://localhost:5173:

builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.WithOrigins("http://localhost:5173")
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials();
    });
});

// Later in the file
app.UseCors();

SDK Not Found in foundry-web

Check:

cd src/Apps/foundry-web
pnpm list @luckboxstudios/foundry-sdk

Fix:

# Rebuild SDK
cd sdk/typescript
pnpm install
pnpm build

# Link to foundry-web
pnpm link --global

cd ../../src/Apps/foundry-web
pnpm link --global @luckboxstudios/foundry-sdk
pnpm dev

Token Refresh Not Working

Check: 1. autoRefresh: true in FoundryClient config (default) 2. Refresh token is stored in localStorage 3. Backend /auth/refresh endpoint is working

Test:

// In browser console
localStorage.getItem('foundry_refresh_token'); // Should return a token

Fix: Check backend logs for refresh endpoint errors.

Database Connection Errors

Check: PostgreSQL is running

Fix:

# If using Aspire
dotnet run  # Aspire will start PostgreSQL container

# If standalone
docker run --name foundry-postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:16

Next Steps

Once authentication is working:

  1. Add Protected Routes: Create a route guard to redirect unauthenticated users to login
  2. Add Logout Button: Add logout functionality to your app header/menu
  3. Test Token Expiration: Verify auto-refresh works after 60 seconds
  4. Add Role-Based Access: Check user.roles for authorization
  5. Create More Users: Add user management UI or import script

Additional Resources

Success Criteria

You should be able to: - Log in with email and password - See user info in browser console/localStorage - Make authenticated API calls - Logout successfully - Auto-refresh expired tokens

If all of these work, your authentication setup is complete!