-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Description
Summary
Implement the local auth provider with email/password authentication, bcrypt password hashing, and JWT token generation/validation.
Context
The local provider is the primary auth mechanism for both self-hosted users who want authentication and the initial SaaS deployment. It handles everything in-house without external dependencies.
Implementation
LocalProvider (internal/auth/local.go)
Register flow:
- Validate input (email format, password strength, org name)
- Check email uniqueness
- If
MULTI_TENANT=true: create new Organization + User (is_owner=true) - If
MULTI_TENANT=false: reject if an org already exists (only invite-based registration) - Hash password with bcrypt (cost 12)
- Generate JWT token pair (access + refresh)
Authenticate flow:
- Find user by email
- Compare bcrypt hash
- Generate JWT token pair
ValidateToken flow:
- Parse JWT, validate signature + expiration
- Extract claims (user_id, org_id, email, name)
- Return Claims struct
RefreshToken flow:
- Validate refresh token
- Generate new access token (rotate refresh if near expiry)
API Endpoints
POST /api/auth/register
{
"org_name": "Acme Corp",
"name": "John Doe",
"email": "john@acme.com",
"password": "secureP@ss123"
}
→ 201 { "access_token": "...", "refresh_token": "...", "expires_in": 86400, "user": {...}, "organization": {...} }
POST /api/auth/login
{
"email": "john@acme.com",
"password": "..."
}
→ 200 { "access_token": "...", "refresh_token": "...", "expires_in": 86400, "user": {...} }
POST /api/auth/refresh
{
"refresh_token": "..."
}
→ 200 { "access_token": "...", "refresh_token": "...", "expires_in": 86400 }
JWT Structure
Access Token Claims:
{
"sub": "user-uuid",
"org_id": "org-uuid",
"email": "john@acme.com",
"name": "John Doe",
"exp": 1234567890,
"iat": 1234567890,
"type": "access"
}Refresh Token Claims:
{
"sub": "user-uuid",
"exp": 1234567890,
"type": "refresh"
}Password Validation Rules
- Minimum 8 characters
- At least 1 uppercase, 1 lowercase, 1 digit
- No maximum length (bcrypt handles truncation at 72 bytes)
Environment Variables
| Variable | Default | Description |
|---|---|---|
| JWT_SECRET | (required when local) | HMAC-SHA256 signing key |
| JWT_ACCESS_EXPIRATION | 24h | Access token TTL |
| JWT_REFRESH_EXPIRATION | 7d | Refresh token TTL |
Acceptance Criteria
- LocalProvider implements AuthProvider interface
- Register creates org + user with hashed password
- Login validates credentials and returns JWT
- Refresh token rotation works
- Password validation rules enforced
- Email uniqueness enforced
- JWT signing with HMAC-SHA256
- Unit tests for all flows (register, login, validate, refresh)
- Integration test with real DB
Dependencies
- Depends on: feat: Organization, User, and Invite models + DB migration #49 (models), feat: Auth provider interface + noop implementation #50 (interface)
Related Issues
Part of Auth & Multi-tenancy epic (Phase 1).
Reactions are currently unavailable