Add Better Auth (TypeScript) and FastAPI Users (Python) to example agents#7
Merged
teallarson merged 12 commits intomainfrom Mar 3, 2026
Merged
Add Better Auth (TypeScript) and FastAPI Users (Python) to example agents#7teallarson merged 12 commits intomainfrom
teallarson merged 12 commits intomainfrom
Conversation
…ents Replaces the hand-rolled bcrypt+session auth in all three templates with batteries-included auth libraries: TypeScript (ai-sdk + mastra templates): - Swap bcrypt for better-auth@^1.2.7; drop @types/bcrypt - New lib/auth.ts: Better Auth server config with Drizzle (SQLite) adapter - New lib/auth-client.ts: React client (signIn, signUp, signOut) - New lib/db/schema.ts: Better Auth core tables (user, session, account, verification) - New app/api/auth/[...all]/route.ts: Better Auth Next.js catch-all handler - Remove old login/, register/, logout/ routes (replaced by Better Auth) - Update login-form.tsx: use authClient.signIn/signUp instead of fetch - Update dashboard handleLogout: use authClient.signOut() - Add BETTER_AUTH_SECRET + BETTER_AUTH_URL to .env.example Python (langchain template): - Add fastapi-users[sqlalchemy]>=14.0; drop bcrypt - New app/auth_manager.py: FastAPI Users setup (UserManager, cookie JWT backend) - Replace app/models.py: User extends SQLAlchemyBaseUserTableUUID, no sessions table - Replace app/auth.py: get_current_user() reads FastAPI Users JWT cookie - Replace app/routes/auth.py: register/login/logout backed by UserManager - Update alembic/versions/001_initial.py: FastAPI Users schema (UUID id, hashed_password, is_active/superuser/verified; no sessions table) All existing Arcade OAuth (MCP) code is unchanged. Route handlers that call get_current_user(request, db) continue to work without modification. Closes TOO-466 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Keep Better Auth (delete old login/register routes, keep catch-all handler) - Use @arcadeai/design-system imports in login-form.tsx alongside authClient - Use dashboard page references from main with Better Auth descriptions from PR branch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mapToolToSource was returning "other" for any unrecognized tool, causing all unknown providers to collide on the same key and show a single "Other" row. Now extracts the actual namespace from the tool name so each provider gets its own entry (e.g. "notion", "twitter") and displays its real name. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Delete ToolStatusBar and its SSE "sources" handler — redundant now that the stats-bar filter tiles show source breakdown - Add GoogleServicesIcon (stacked Gmail + Calendar) for google_calendar entries in the source auth gate pre-flight screen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add coerce_database_url validator in config.py so an empty/blank DATABASE_URL env var (from the user's shell) falls back to the default SQLite URL instead of crashing alembic during scaffolding - Add "alembic upgrade head" to the printed next-steps for Python templates so users know to run migrations after configuring .env Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
load_dotenv() with no args uses find_dotenv() which walks UP the directory tree and can pick up a stray .env from a parent directory (e.g. /tmp/.env with DATABASE_URL=local.db). Pin it to the project root with dotenv_path=".env" so it only reads the project's own .env. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LangChain: "LangChain (Python)" → "LangChain (FastAPI + Python)" to match the ai-sdk/mastra pattern of embedding the stack in the label - Clear all hints (were redundant with the label and caused "(Python) (hint)" double-paren display when the item was selected in clack) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the validator only guarded against empty strings, so a value like DATABASE_URL=local.db (no ://) would pass through and cause an Alembic / SQLAlchemy parse error. Now any value missing :// falls back to the default sqlite+aiosqlite:///local.db URL. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Alembic runs during scaffolding before the user sets up .env, so any DATABASE_URL in the shell environment (even a stale one from a parent directory) could cause a parse error. By copying .env.example → .env right before migrations run, we ensure alembic always uses the safe SQLite default. The copy is skipped if .env already exists. Also update Python next-steps: since .env already exists, replace the cp command with a reminder to fill in API keys. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both TS and Python templates now have .env auto-created from .env.example before migrations run. Updated printSuccess for both branches to say "fill in .env" instead of showing the cp command. Removed the now-unused copyCmd variable. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _map_tool_to_source: split on [._] so Slack_WhoAmI → slack
(was split(".") only, causing everything to show as "Other")
- ChatAnthropic: set max_tokens=16384 to avoid hitting the 200k
combined context limit (langchain-anthropic defaults to 64000)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the hand-rolled bcrypt+session auth in all three templates with batteries-included auth libraries, per TOO-466.
TypeScript templates (ai-sdk + mastra):
bcryptforbetter-auth— email/password auth, Drizzle (SQLite) adapter, httpOnly session cookieslib/auth.ts: Better Auth server config + backwards-compatiblegetSession()export (no changes required in existing route files)lib/auth-client.ts: React client (signIn,signUp,signOut) for client componentslib/db/schema.ts: Better Auth tables (user,session,account,verification)app/api/auth/[...all]/route.ts: Better Auth catch-all handler (replaces old login/register/logout routes)login-form.tsxand dashboard logout to useauthClientBETTER_AUTH_SECRET+BETTER_AUTH_URLto.env.examplePython template (langchain):
fastapi-users[sqlalchemy]>=14.0, dropbcryptapp/auth_manager.py: FastAPI Users setup (UserManager, cookie JWT backend, 7-day sessions)app/models.py:UserextendsSQLAlchemyBaseUserTableUUID(UUID id,hashed_password,is_active/is_superuser/is_verified; no sessions table — stateless JWT)app/auth.py:get_current_user(request, db)now decodes FastAPI Users JWT — all callers (chat, arcade, plan routes) unchangedapp/routes/auth.py: register/login/logout delegate toUserManagerAll existing Arcade OAuth (MCP Gateway) code is unchanged. Existing route files that call
get_current_user(request, db)continue to work without modification.Test plan
bunx drizzle-kit generate && bunx drizzle-kit migrate, start dev server, register account, verify login/logout cycleBETTER_AUTH_SECRETenv var is required and sessions expire correctlyalembic upgrade head, start server, register/login/logout cycle🤖 Generated with Claude Code