| name | lvt-gen-auth |
| description | Add authentication to an existing LiveTemplate app - generates session management, password auth, magic links, email confirmation, CSRF protection, and E2E tests |
| keywords | ["lvt","livetemplate","lt"] |
| category | core |
| version | 1.0.0 |
lvt-gen-auth
Adds a complete authentication system to an existing LiveTemplate application. Generates password authentication, magic link authentication, session management, email confirmation, password reset, CSRF protection, route protection middleware, and comprehensive E2E tests.
🎯 ACTIVATION RULES
Context Detection
This skill typically runs in existing LiveTemplate projects (.lvtrc exists).
✅ Context Established By:
- Project context -
.lvtrc exists (most common scenario)
- Agent context - User is working with
lvt-assistant agent
- Keyword context - User mentions "lvt", "livetemplate", or "lt"
Keyword matching (case-insensitive): lvt, livetemplate, lt
Trigger Patterns
With Context:
✅ "add authentication"
✅ "I need user login"
✅ "generate auth system"
Without Context (needs keywords):
✅ "add authentication to my lvt app"
✅ "use livetemplate to add user login"
❌ "add authentication" (no context, no keywords)
User Prompts
This skill should activate when the user requests to add authentication:
Explicit prompts:
- "Add authentication to my app"
- "Generate auth for my app"
- "I need user authentication"
- "Add login and registration"
- "Set up auth system"
Implicit prompts:
- "Add user accounts"
- "I want users to sign in"
- "Add password login"
- "Set up user sessions"
- "Protect routes with authentication"
- "Add magic link login"
Examples:
- "Add authentication with password and magic links"
- "Generate auth system for my blog app"
- "I need user login for my app"
- "Set up authentication with email confirmation"
- "Add auth but skip magic links"
Context Awareness
Before executing this skill, verify:
-
In Project Directory:
- Check for
.lvtrc file (confirms it's an lvt project)
- Check for
go.mod (confirms it's a Go project)
- Check for
database/ directory
-
Dependencies Available:
lvt binary is installed and accessible
- Project was created with
lvt new
- Database initialized
-
Not Already Exists:
- Check if
app/auth/ already exists
- Warn user if auth is already generated (will overwrite)
⚠️ CONFIRM BEFORE EXECUTING
Authentication is a significant change. Show what will be created and ask for confirmation:
I'll add authentication with these features:
- Password auth (email + password login)
- Magic link auth (passwordless email login)
- Email confirmation
- Password reset
- Session management UI
- CSRF protection
This will create:
- app/auth/auth.go (handler)
- app/auth/auth.tmpl (templates)
- app/auth/middleware.go (route protection)
- shared/password/password.go (bcrypt utilities)
- shared/email/email.go (email sender interface)
- database/migrations/..._create_auth_tables.sql
**Proceed?**
- **yes** - add authentication
- **change X** - modify features
- **advanced** - explore options (disable features, custom names)
- **no** - cancel
If user says "advanced", show:
⚙️ **Advanced Auth Options**
| Feature | Default | Can Disable |
|---------|---------|-------------|
| Password auth | enabled | --no-password |
| Magic link auth | enabled | --no-magic-link |
| Email confirmation | enabled | --no-email-confirm |
| Password reset | enabled | --no-password-reset |
| Sessions UI | enabled | --no-sessions-ui |
| CSRF protection | enabled | --no-csrf |
**Custom names:**
- Struct name: User (default)
- Table name: users (default)
What would you like to change?
Then WAIT for user response before running commands.
Checklist
Confirmation Phase:
Execution Phase (after approval):
Authentication Features
Password Authentication
What it generates:
- Registration with email + password
- Login with email + password
- Password hashing with bcrypt (cost 12)
- Password validation (min 8 chars)
- Logout
Database:
users table with email, password_hash columns
- Unique index on email
Routes:
- POST /auth/register - User registration
- POST /auth/login - User login
- POST /auth/logout - User logout
Magic Link Authentication
What it generates:
- Passwordless login via email
- Secure token generation (32 bytes, crypto/rand)
- Token expiration (15 minutes default)
- One-time use tokens
Database:
magic_link_tokens table
- Tokens expire after use or timeout
Routes:
- POST /auth/magic-link/request - Request magic link
- GET /auth/magic-link/verify - Verify and login
Email Confirmation
What it generates:
- Email verification tokens
- Confirmation email sending
- User status tracking (verified/unverified)
- Resend confirmation option
Database:
email_verification_tokens table
users.email_verified boolean column
Routes:
- POST /auth/confirm/send - Send confirmation email
- GET /auth/confirm/verify - Verify email
Password Reset
What it generates:
- Password reset token generation
- Reset email sending
- Secure token validation
- Password update after reset
Database:
password_reset_tokens table
- Tokens expire after 1 hour
Routes:
- POST /auth/password/reset/request - Request password reset
- POST /auth/password/reset/verify - Verify token and reset password
Session Management
What it generates:
- Secure session tokens (32 bytes)
- Session storage in database
- Session expiration (30 days default)
- Active session tracking
- Session revocation
Database:
sessions table with user_id, token, expires_at
- Index on token for fast lookup
Middleware:
- RequireAuth(next http.Handler) - Protect routes
- Session validation on each request
- Auto-cleanup of expired sessions
CSRF Protection
What it generates:
- CSRF token generation per session
- Token validation middleware
- Form helper for templates
- Automatic token rotation
How it works:
- Token stored in session
- Must be included in forms
- Validated on state-changing requests
Sessions UI (Optional)
What it generates:
- View active sessions
- Revoke individual sessions
- Revoke all sessions except current
- Session details (IP, user agent, last active)
Routes:
- GET /auth/sessions - List all sessions
- POST /auth/sessions/revoke - Revoke session
Wiring Auth Routes
IMPORTANT: Routes are NOT auto-injected. You must manually wire them in main.go.
Why manual wiring?
- Gives you control over which auth features to expose
- Allows custom route prefixes
- Enables middleware customization
- Prevents accidental exposure of features you don't want
Example wiring (all features):
import (
"myapp/app/auth"
"myapp/shared/email"
)
emailSender := &email.ConsoleEmailSender{}
authHandler := auth.New(queries, emailSender)
http.Handle("/auth/register", authHandler.HandleRegister())
http.Handle("/auth/login", authHandler.HandleLogin())
http.Handle("/auth/logout", authHandler.HandleLogout())
http.Handle("/auth/magic-link/request", authHandler.HandleMagicLinkRequest())
http.Handle("/auth/magic-link/verify", authHandler.HandleMagicLinkVerify())
http.Handle("/auth/confirm/send", authHandler.HandleConfirmationSend())
http.Handle("/auth/confirm/verify", authHandler.HandleConfirmationVerify())
http.Handle("/auth/password/reset/request", authHandler.HandlePasswordResetRequest())
http.Handle("/auth/password/reset/verify", authHandler.HandlePasswordResetVerify())
http.Handle("/auth/sessions", authHandler.HandleSessions())
http.Handle("/auth/sessions/revoke", authHandler.HandleSessionRevoke())
http.Handle("/dashboard", auth.RequireAuth(queries, dashboardHandler))
http.Handle("/profile", auth.RequireAuth(queries, profileHandler))
Example wiring (password-only, no email features):
authHandler := auth.New(queries, nil)
http.Handle("/auth/register", authHandler.HandleRegister())
http.Handle("/auth/login", authHandler.HandleLogin())
http.Handle("/auth/logout", authHandler.HandleLogout())
http.Handle("/dashboard", auth.RequireAuth(queries, dashboardHandler))
Email Configuration
Development (Console Sender):
import "myapp/shared/email"
emailSender := &email.ConsoleEmailSender{}
Production (SMTP):
import "myapp/shared/email"
emailSender := email.NewSMTPSender(email.SMTPConfig{
Host: "smtp.gmail.com",
Port: 587,
Username: "your-email@gmail.com",
Password: "your-app-password",
From: "noreply@yourapp.com",
})
Custom Implementation:
Implement the email.Sender interface:
type Sender interface {
Send(to, subject, body string) error
}
LiveTemplate v0.5.1+ HTTP APIs
The generated authentication code uses LiveTemplate v0.5.1+ ActionContext HTTP methods for cookie and redirect operations. These APIs make it easy to handle authentication flows directly from your Change() handler.
Available HTTP Methods
Setting Cookies:
func (s *AuthState) handleLogin(ctx *livetemplate.ActionContext) error {
err := ctx.SetCookie(&http.Cookie{
Name: "session_token",
Value: token,
Path: "/",
MaxAge: 30 * 24 * 60 * 60,
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
})
if err != nil {
return fmt.Errorf("failed to set cookie: %w", err)
}
return nil
}
Reading Cookies:
func (s *AuthState) checkSession(ctx *livetemplate.ActionContext) error {
cookie, err := ctx.GetCookie("session_token")
if err != nil {
return err
}
}
Deleting Cookies:
func (s *AuthState) handleLogout(ctx *livetemplate.ActionContext) error {
ctx.DeleteCookie("session_token")
return ctx.Redirect("/", http.StatusSeeOther)
}
Redirecting:
func (s *AuthState) handleLogin(ctx *livetemplate.ActionContext) error {
return ctx.Redirect("/dashboard", http.StatusSeeOther)
}
Headers:
ctx.SetHeader("X-Custom-Header", "value")
userAgent := ctx.GetHeader("User-Agent")
Checking HTTP Context:
if ctx.IsHTTP() {
}
Security Best Practices
Cookie Security:
- Always set
HttpOnly: true to prevent JavaScript access
- Set
Secure: true in production (requires HTTPS)
- Use
SameSite: http.SameSiteStrictMode to prevent CSRF
- Set appropriate
MaxAge for session duration
Redirect Validation:
- ActionContext validates redirects automatically
- Only relative paths starting with
/ are allowed
- Prevents open redirect vulnerabilities
Error Handling:
ctx.SetCookie() returns ErrNoHTTPContext if called from WebSocket
ctx.Redirect() returns ErrInvalidRedirectCode for invalid status codes
ctx.Redirect() returns ErrInvalidRedirectURL for non-relative URLs
Common Issues and Fixes
Issue 1: "failed to load project config"
Why it's wrong: Not in an lvt project directory
Fix:
ls .lvtrc
lvt new myapp
cd myapp
lvt gen auth
Issue 2: "at least one authentication method must be enabled"
Why it's wrong: Used both --no-password and --no-magic-link
Fix:
lvt gen auth --no-password --no-magic-link
lvt gen auth --no-magic-link
lvt gen auth --no-password
lvt gen auth
Issue 3: Build fails with "User undefined"
Why it's wrong: Forgot to run sqlc generate after migration
Fix:
lvt migration up
cd database
go run github.com/sqlc-dev/sqlc/cmd/sqlc generate
cd ../..
go mod tidy
go build ./cmd/myapp
Issue 4: Auth routes return 404
Why it's wrong: Forgot to wire auth routes in main.go
Fix:
authHandler := auth.New(queries, emailSender)
http.Handle("/auth/login", authHandler.HandleLogin())
Issue 5: "email sender not configured"
Why it's wrong: Passed nil for email sender when using email features
Fix:
authHandler := auth.New(queries, nil)
emailSender := &email.ConsoleEmailSender{}
authHandler := auth.New(queries, emailSender)
Issue 6: CSRF token validation fails
Why it's wrong: Missing CSRF token in forms or using GET for state changes
Fix:
<form method="POST" action="/auth/login">
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
</form>
data := struct {
CSRFToken string
}{
CSRFToken: session.CSRFToken,
}
Issue 7: Sessions expire too quickly/slowly
Why it's wrong: Using default expiration (30 days)
Fix:
const SessionDuration = 7 * 24 * time.Hour
Running E2E Tests
Prerequisites:
- Docker installed and running (for headless Chrome)
- Auth system generated
- Migrations applied
Run tests:
go test ./app/auth -run TestAuthE2E -v
go test ./app/auth -run TestAuthE2E/Registration -v
go test ./app/auth -run TestAuthE2E/Login -v
go test ./app/auth -run TestAuthE2E/MagicLink -v
What the tests verify:
- ✓ User registration flow
- ✓ Login with correct credentials
- ✓ Login rejection with wrong credentials
- ✓ Magic link request and verification
- ✓ Email confirmation flow
- ✓ Password reset flow
- ✓ Session management (active sessions, revocation)
- ✓ Route protection (middleware)
- ✓ CSRF protection
Success Response
After successful auth generation, provide:
✅ Authentication system generated successfully!
📁 Generated files:
- app/auth/auth.go
- app/auth/auth.tmpl
- app/auth/middleware.go
- app/auth/auth_e2e_test.go
- shared/password/password.go
- shared/email/email.go
- database/migrations/<timestamp>_create_auth_tables.sql
📝 Files updated:
- database/queries.sql
⚠️ IMPORTANT: Auth routes are NOT auto-injected
🚀 Next steps:
1. Run migrations:
lvt migration up
2. Generate sqlc code:
cd database && go run github.com/sqlc-dev/sqlc/cmd/sqlc generate && cd ../..
3. Wire auth routes in main.go (see skill for examples)
4. Configure email sender (see shared/email/email.go)
5. Run E2E tests:
go test ./app/auth -run TestAuthE2E -v
💡 Tip: Check app/auth/auth.go for complete usage examples!
Common User Scenarios
Scenario 1: Full authentication with all features
- User: "Add authentication to my app"
- Command:
lvt gen auth
- Features: Password, magic links, email confirm, password reset, sessions UI, CSRF
- Next: Wire all routes, configure email sender
Scenario 2: Simple password-only auth
- User: "Add password login, skip magic links and email"
- Command:
lvt gen auth --no-magic-link --no-email-confirm --no-password-reset
- Features: Password auth only, sessions, CSRF
- Next: Wire basic routes (register, login, logout), no email needed
Scenario 3: Passwordless (magic link only)
- User: "I want magic link authentication only"
- Command:
lvt gen auth --no-password
- Features: Magic links, email confirm, sessions, CSRF
- Next: Wire magic link routes, configure email sender
Scenario 4: Custom struct/table names
- User: "Add auth for admins instead of users"
- Command:
lvt gen auth Admin admin_users
- Features: All features but with Admin struct and admin_users table
- Next: Same wiring, but use Admin instead of User in code
Scenario 5: Auth for multi-tenant app
- User: "Add auth for accounts table"
- Command:
lvt gen auth Account accounts
- Features: All features with Account struct
- Next: Wire routes, potentially add tenant isolation logic
Validation Criteria
Auth generation is successful if:
- ✅ All files created without errors
- ✅ Migration file created with correct tables
- ✅ Queries appended to queries.sql
- ✅
lvt migration up succeeds
- ✅
sqlc generate succeeds
- ✅
go build succeeds
- ✅ No compilation errors
- ✅ E2E tests can be run (docker required)
Advanced Customization
After generation, users can customize:
1. Session duration:
const SessionDuration = 7 * 24 * time.Hour
2. Password requirements:
const MinPasswordLength = 12
3. Token expiration:
const MagicLinkExpiration = 5 * time.Minute
const ResetTokenExpiration = 30 * time.Minute
4. Add custom user fields:
ALTER TABLE users ADD COLUMN display_name TEXT;
ALTER TABLE users ADD COLUMN avatar_url TEXT;
5. Add OAuth providers:
- Extend auth handler with OAuth endpoints
- Add OAuth provider tables
- Implement OAuth flow in auth.go
6. Add 2FA/MFA:
- Generate TOTP secrets
- Add verification step after login
- Store backup codes
7. Add role-based access control (RBAC):
- Add roles table
- Add user_roles junction table
- Create role-checking middleware
- Extend RequireAuth to check roles
Notes
- Auth routes are intentionally NOT auto-injected for security and flexibility
- Email sender must be configured for magic links, email confirm, and password reset
- CSRF protection is enabled by default - include token in all forms
- Sessions are stored in database (not cookies) for security
- Bcrypt cost is 12 by default (good balance of security and performance)
- E2E tests require Docker for headless Chrome
- Generated code includes extensive comments and examples
- Middleware can be applied to individual routes or route groups
- Sessions auto-expire after inactivity
- One user can have multiple active sessions (different devices)