Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

feat: Local auth provider (email/password + JWT) #51

@sre-helmcode

Description

@sre-helmcode

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:

  1. Validate input (email format, password strength, org name)
  2. Check email uniqueness
  3. If MULTI_TENANT=true: create new Organization + User (is_owner=true)
  4. If MULTI_TENANT=false: reject if an org already exists (only invite-based registration)
  5. Hash password with bcrypt (cost 12)
  6. Generate JWT token pair (access + refresh)

Authenticate flow:

  1. Find user by email
  2. Compare bcrypt hash
  3. Generate JWT token pair

ValidateToken flow:

  1. Parse JWT, validate signature + expiration
  2. Extract claims (user_id, org_id, email, name)
  3. Return Claims struct

RefreshToken flow:

  1. Validate refresh token
  2. 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

Related Issues

Part of Auth & Multi-tenancy epic (Phase 1).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions