Initial commit: Project setup phase 1.1

Completed Phase 1.1 of the project setup with the following components:
- Initialized Go module with Go 1.24
- Set up logging with log/slog
- Created error types and handling mechanisms
- Implemented configuration structures
- Created directory structure for the project
This commit is contained in:
Justin Hammond 2025-05-20 22:09:10 +08:00
commit 719aee56e8
10 changed files with 1993 additions and 0 deletions

168
CLAUDE.md Normal file
View file

@ -0,0 +1,168 @@
# Authentication Library Implementation Guide
This document provides specifications for implementing a comprehensive, modular authentication library in Go. The implementation must be complete and production-ready.
## Core Requirements
Your implementation MUST include ALL of the following:
1. **Pluggable Architecture**
- Create a modular system with clear interfaces for all components
- Support pluggable primary authentication methods
- Support pluggable MFA (multi-factor authentication) methods
- Allow for custom implementation of any component
2. **Authentication Methods**
- **Primary Authentication**:
- Username/Password with secure password hashing
- OAuth2 support (Google, GitHub, Microsoft, Facebook at minimum)
- SAML authentication
- **MFA Methods**:
- TOTP (Time-based One-Time Password)
- FIDO2/WebAuthn (passkeys)
- Email OTP (One-Time Password)
- Backup codes
3. **Session Management**
- Implement Cookie-based sessions
- JWT token authentication
- Bearer token authentication
- Secure session revocation and refresh mechanisms
4. **Storage Adapters**
- Standard library SQL interface
- GORM integration
- Ent ORM integration
- In-memory implementation for testing
5. **HTTP Framework Integration**
- Create adapters for ALL of these frameworks:
- Standard library (net/http)
- Chi router
- Echo framework
- Fiber
- Gin
- Gorilla mux
- httprouter
- Huma
- FastHTTP
6. **Security Features**
- Brute force protection with configurable limits
- Rate limiting
- CSRF protection
- Secure cookie handling
- User account locking/unlocking
- Password reset functionality
- Account recovery options
7. **RBAC (Role-Based Access Control)**
- Permission management
- Role assignment
- Group-based permissions
- Resource access control
8. **Developer API**
- Clean, intuitive API
- Consistent error handling
- Comprehensive documentation
- Minimal boilerplate for common use cases
9. **Testing**
- Black box unit tests for ALL components
- Mock implementations for testing
- Test utilities to simplify testing
- Test coverage > 80%
- Deterministic tests (no reliance on time, randomness)
- Table-driven tests for comprehensive scenarios
## Architecture Guidelines
1. **Clean Architecture**
- Separate domain logic from infrastructure
- Use interfaces (ports) for all dependencies
- Implement adapters for specific technologies
2. **Hexagonal Design**
- Core domain should be independent of external systems
- Use dependency injection throughout
- Clear boundaries between application layers
3. **Error Handling**
- Custom error types for specific scenarios
- Proper error wrapping with context
- Exported errors for application testing
4. **Concurrency**
- Use goroutines and channels appropriately
- Context propagation for cancellation
- Thread-safe implementations
5. **Logging**
- Structured logging with log/slog
- Appropriate log levels
- Contextual information in logs
- Proper redaction of sensitive data
## Implementation Details
1. **User Management**
- Registration flow
- Account verification
- Profile management
- Account status tracking
2. **Authentication Flow**
- Registration
- Login
- Logout
- MFA enrollment and verification
- Account recovery
- Password reset
3. **Session Handling**
- Session creation
- Session validation
- Session expiration and renewal
- Session revocation
4. **OAuth Integration**
- OAuth2 provider configuration
- OAuth2 callback handling
- User profile mapping
- Token management
5. **RBAC Implementation**
- Permission definition and assignment
- Role creation and management
- User-role relationships
- Permission checking middleware
6. **Security Measures**
- Rate limiting implementation
- Brute force detection and prevention
- Security event logging
- Audit trail
## Package Structure
1. Consult @docs/DESIGN.md
## CRITICAL IMPLEMENTATION REQUIREMENTS
1. The implementation MUST be complete and production-ready
2. Every feature MUST be fully implemented, not stubbed
3. All security features MUST be properly implemented (not placeholder)
4. Every component MUST have thorough unit tests
5. All interfaces MUST be well-defined with proper documentation
6. Error handling MUST be comprehensive
7. The library MUST be usable in a production environment
8. EVERY HTTP framework integration MUST be fully implemented
9. ALL storage adapters MUST be properly implemented
10. Documentation MUST be complete with examples
Create this authentication library with the same comprehensive features and flexibility as the original codebase, ensuring it meets all the requirements outlined above.
## Additional Documents
1. Detailed Project Design @docs/DESIGN.md
2. Project Plan @docs/PROJECT_PLAN.md

415
docs/DESIGN.md Normal file
View file

@ -0,0 +1,415 @@
# Auth2 Library Design Document
## Overview
Auth2 is a comprehensive, modular authentication library for Go applications. It provides a complete solution for user authentication, authorization, session management, and user data handling with a focus on security, flexibility, and ease of integration.
## Core Architecture
The library follows hexagonal/clean architecture principles to maintain separation of concerns and enable high modularity:
1. **Domain Layer**: Core entities and business rules
2. **Application Layer**: Use cases and application logic
3. **Infrastructure Layer**: External systems integration and adapters
4. **Interface Layer**: HTTP and framework integration
### Key Design Principles
1. Interface-driven design for all components
2. Dependency injection for loose coupling
3. Clear error handling with descriptive errors
4. Comprehensive logging with context
5. Thread-safe implementations
## Package Structure
```
github.com/Fishwaldo/auth2/
├── pkg/
│ ├── auth/ # Core authentication logic
│ │ ├── providers/ # Authentication providers
│ │ │ ├── basic/ # Username/password
│ │ │ ├── oauth/ # OAuth providers
│ │ │ │ ├── google/ # Google OAuth
│ │ │ │ ├── github/ # GitHub OAuth
│ │ │ │ ├── microsoft/ # Microsoft OAuth
│ │ │ │ └── facebook/ # Facebook OAuth
│ │ │ └── saml/ # SAML implementation
│ │ ├── mfa/ # MFA implementations
│ │ │ ├── totp/ # Time-based OTP
│ │ │ ├── webauthn/ # WebAuthn/FIDO2
│ │ │ ├── email/ # Email OTP
│ │ │ └── backupcodes/ # Backup codes
│ │ └── verification/ # Account verification
│ │ ├── email/ # Email verification
│ │ └── providers/ # Email providers
│ ├── session/ # Session management
│ │ ├── cookie/ # Cookie-based sessions
│ │ ├── jwt/ # JWT token authentication
│ │ └── token/ # Bearer token authentication
│ ├── user/ # User management
│ │ ├── profile/ # User profile handling
│ │ └── password/ # Password management
│ ├── rbac/ # Role-based access control
│ │ ├── role/ # Role management
│ │ ├── permission/ # Permission management
│ │ └── group/ # Group management
│ ├── storage/ # Storage interfaces
│ │ ├── sql/ # Standard SQL implementation
│ │ ├── gorm/ # GORM implementation
│ │ ├── ent/ # Ent implementation
│ │ └── memory/ # In-memory implementation
│ ├── cache/ # Cache interfaces and implementations
│ │ └── redis/ # Redis cache implementation
│ ├── http/ # HTTP framework adapters
│ │ ├── std/ # Standard library
│ │ ├── chi/ # Chi router
│ │ ├── echo/ # Echo framework
│ │ ├── fiber/ # Fiber
│ │ ├── gin/ # Gin
│ │ ├── gorilla/ # Gorilla mux
│ │ ├── httprouter/ # httprouter
│ │ ├── huma/ # Huma
│ │ └── fasthttp/ # FastHTTP
│ ├── security/ # Security features
│ │ ├── ratelimit/ # Rate limiting
│ │ ├── csrf/ # CSRF protection
│ │ ├── bruteforce/ # Brute force protection
│ │ └── recovery/ # Account recovery
│ ├── config/ # Configuration
│ └── log/ # Logging utilities
├── internal/ # Internal implementation details
│ ├── utils/ # Utility functions
│ └── errors/ # Error definitions
├── test/ # Integration tests
│ └── mocks/ # Mock implementations
└── examples/ # Example integrations
├── basic/ # Simple integration
├── complete/ # Complete implementation
└── custom/ # Custom provider implementation
```
## Core Components Design
### Authentication Provider Interface
```go
type AuthProvider interface {
// Authenticate verifies user credentials and returns a user ID if successful
Authenticate(ctx context.Context, credentials interface{}) (string, error)
// Supports returns true if this provider supports the given credentials type
Supports(credentials interface{}) bool
// GetID returns the unique identifier for this provider
GetID() string
// Initialize sets up the provider with necessary configuration
Initialize(config interface{}) error
}
```
### MFA Provider Interface
```go
type MFAProvider interface {
// Setup initializes the MFA method for a user
Setup(ctx context.Context, userID string) (SetupData, error)
// Verify checks if the provided code is valid
Verify(ctx context.Context, userID string, code string) (bool, error)
// GetID returns the unique identifier for this MFA provider
GetID() string
// Initialize sets up the provider with necessary configuration
Initialize(config interface{}) error
}
```
### Dual-Mode Providers
Some authentication methods like FIDO2/WebAuthn can function both as primary authentication and as MFA methods. These providers implement both interfaces:
```go
// DualModeProvider implements both AuthProvider and MFAProvider interfaces
type DualModeProvider interface {
AuthProvider
MFAProvider
}
```
This design allows WebAuthn/FIDO2 to be used either as a standalone authentication method (passwordless) or as a second factor alongside another authentication method.
### Storage Interface
```go
type UserStore interface {
// CreateUser creates a new user
CreateUser(ctx context.Context, user User) (string, error)
// GetUser retrieves a user by ID
GetUser(ctx context.Context, userID string) (User, error)
// GetUserByUsername retrieves a user by username
GetUserByUsername(ctx context.Context, username string) (User, error)
// UpdateUser updates an existing user
UpdateUser(ctx context.Context, user User) error
// DeleteUser deletes a user
DeleteUser(ctx context.Context, userID string) error
// GetUserProfile gets a user's profile data
GetUserProfile(ctx context.Context, userID string) (map[string]interface{}, error)
// UpdateUserProfile updates a user's profile data
UpdateUserProfile(ctx context.Context, userID string, profile map[string]interface{}) error
}
```
### Session Management Interface
```go
type SessionManager interface {
// CreateSession creates a new session for a user
CreateSession(ctx context.Context, userID string, data map[string]interface{}) (Session, error)
// GetSession retrieves a session by ID
GetSession(ctx context.Context, sessionID string) (Session, error)
// RefreshSession extends the session lifetime
RefreshSession(ctx context.Context, sessionID string) error
// RevokeSession invalidates a session
RevokeSession(ctx context.Context, sessionID string) error
// RevokeAllUserSessions invalidates all sessions for a user
RevokeAllUserSessions(ctx context.Context, userID string) error
}
```
### RBAC Interface
```go
type RBACManager interface {
// CreateRole creates a new role
CreateRole(ctx context.Context, role Role) (string, error)
// GetRole retrieves a role by ID
GetRole(ctx context.Context, roleID string) (Role, error)
// UpdateRole updates an existing role
UpdateRole(ctx context.Context, role Role) error
// DeleteRole deletes a role
DeleteRole(ctx context.Context, roleID string) error
// AssignRoleToUser assigns a role to a user
AssignRoleToUser(ctx context.Context, userID, roleID string) error
// RevokeRoleFromUser revokes a role from a user
RevokeRoleFromUser(ctx context.Context, userID, roleID string) error
// HasPermission checks if a user has a specific permission
HasPermission(ctx context.Context, userID, permission string) (bool, error)
}
```
## HTTP Framework Integration
Each HTTP framework adapter will implement the following interface:
```go
type HTTPAdapter interface {
// Middleware returns middleware for the specific framework
Middleware() interface{}
// RegisterRoutes registers authentication routes
RegisterRoutes() error
// ParseRequest extracts authentication data from requests
ParseRequest(request interface{}) (AuthData, error)
// WriteResponse writes authentication responses
WriteResponse(response interface{}, data interface{}) error
}
```
## Security Features Implementation
### Rate Limiting
```go
type RateLimiter interface {
// Allow checks if the operation is allowed based on the key
Allow(ctx context.Context, key string) (bool, error)
// Reset resets the counter for a key
Reset(ctx context.Context, key string) error
}
```
### CSRF Protection
```go
type CSRFProtector interface {
// GenerateToken generates a new CSRF token
GenerateToken(ctx context.Context, userID string) (string, error)
// ValidateToken validates a CSRF token
ValidateToken(ctx context.Context, userID, token string) (bool, error)
}
```
## Main Package API
```go
// Auth2 is the main entry point for the library
type Auth2 struct {
// Configuration
Config *Config
// User management
UserManager *user.Manager
// Authentication
AuthManager *auth.Manager
// Session management
SessionManager session.Manager
// RBAC
RBACManager rbac.Manager
// Security features
Security *security.Manager
}
// New creates a new Auth2 instance with the provided configuration
func New(config *Config) (*Auth2, error) {
// Initialize all components
// ...
}
// RegisterHTTPAdapter registers an HTTP framework adapter
func (a *Auth2) RegisterHTTPAdapter(adapter http.Adapter) error {
// Register adapter
// ...
}
// RegisterAuthProvider registers an authentication provider
func (a *Auth2) RegisterAuthProvider(provider auth.Provider) error {
// Register provider
// ...
}
// RegisterMFAProvider registers an MFA provider
func (a *Auth2) RegisterMFAProvider(provider auth.MFAProvider) error {
// Register provider
// ...
}
// RegisterStorageAdapter registers a storage adapter
func (a *Auth2) RegisterStorageAdapter(adapter storage.Adapter) error {
// Register adapter
// ...
}
```
## Error Handling
All errors will be defined in a central location:
```go
package errors
// Common errors
var (
ErrUserNotFound = errors.New("user not found")
ErrInvalidCredentials = errors.New("invalid credentials")
ErrUserDisabled = errors.New("user account is disabled")
ErrSessionExpired = errors.New("session has expired")
ErrInvalidToken = errors.New("invalid token")
ErrPermissionDenied = errors.New("permission denied")
ErrRateLimitExceeded = errors.New("rate limit exceeded")
ErrInvalidMFACode = errors.New("invalid MFA code")
// ...
)
```
## Configuration
```go
// Config holds the configuration for the entire library
type Config struct {
// General settings
AppName string
Environment string
// Auth settings
Auth struct {
PasswordPolicy *password.Policy
SessionDuration time.Duration
RequireEmailVerification bool
MaxLoginAttempts int
LockoutDuration time.Duration
}
// Storage settings
Storage struct {
Type string // sql, gorm, ent, memory
ConnectionString string
}
// Security settings
Security struct {
CSRFTokenExpiry time.Duration
SecureCookies bool
SameSite string
}
// Logging
Logger slog.Logger
}
```
## Testing Strategy
1. **Black Box Unit Testing**:
- Test all components through their public interfaces only
- Do not test internal implementation details
- Use table-driven tests for comprehensive coverage
- Mock all external dependencies
- Each component must be testable in isolation
- Test both success and failure scenarios
2. **Integration Testing**:
- Test integration with all supported storage adapters
- Test integration with all supported HTTP frameworks
- Use in-memory implementations where appropriate
- Focus on contract adherence between components
3. **End-to-End Testing**:
- Test complete authentication flows
- Test security features effectiveness
- Test all public APIs
- Verify proper error handling and error messages
## Documentation
1. **Package Documentation**:
- Godoc-compliant documentation for all exported functions and types
- Usage examples for all major components
2. **Integration Guides**:
- Step-by-step guides for each supported HTTP framework
- Configuration examples for different deployment scenarios
3. **API Reference**:
- Complete API reference with examples
- OpenAPI specification for REST APIs
4. **Security Recommendations**:
- Best practices for secure configuration
- Security considerations for production deployment

315
docs/PROJECT_PLAN.md Normal file
View file

@ -0,0 +1,315 @@
# Auth2 Library Implementation Plan
This document outlines the step-by-step implementation plan for the Auth2 library, a comprehensive, production-ready authentication solution for Go applications.
## Phase 1: Foundation & Core Architecture
### 1.1 Project Setup
- [x] Initialize Go module and directory structure
- [x] Set up logging with log/slog
- [x] Create error types and handling mechanisms
- [x] Implement configuration structures
### 1.2 Plugin System Architecture
- [ ] Design plugin registry interface
- [ ] Implement plugin loading mechanism
- [ ] Create provider discovery system
- [ ] Build versioning and compatibility checking
### 1.3 Core Domain Models
- [ ] Define User model and interfaces
- [ ] Create Context wrapper for auth context
- [ ] Implement base interfaces for all components
- [ ] Build error handling patterns
## Phase 2: Core Authentication Framework
### 2.1 Authentication Provider Interface
- [ ] Define AuthProvider interface
- [ ] Create ProviderManager for managing multiple providers
- [ ] Implement provider registration system
- [ ] Build chain-of-responsibility pattern for auth attempts
### 2.2 Basic Authentication
- [ ] Implement username/password provider
- [ ] Create password hashing utilities (bcrypt, argon2id)
- [ ] Build password policy enforcement
- [ ] Implement account locking mechanism
### 2.3 WebAuthn/FIDO2 as Primary Authentication
- [ ] Implement WebAuthn passwordless registration
- [ ] Create WebAuthn passwordless authentication
- [ ] Build attestation verification
- [ ] Implement credential storage and management
- [ ] Create dual-mode provider interface for both primary and MFA use
### 2.4 OAuth2 Framework
- [ ] Design generic OAuth2 provider
- [ ] Implement OAuth2 flow handlers
- [ ] Create token storage and validation
- [ ] Build user profile mapping utilities
### 2.5 OAuth2 Providers
- [ ] Implement Google OAuth2 provider
- [ ] Implement GitHub OAuth2 provider
- [ ] Implement Microsoft OAuth2 provider
- [ ] Implement Facebook OAuth2 provider
### 2.6 SAML Authentication
- [ ] Implement SAML provider interface
- [ ] Create SAML assertion parser
- [ ] Build SAML request/response handlers
- [ ] Implement metadata handlers
## Phase 3: Multi-Factor Authentication
### 3.1 MFA Framework
- [ ] Define MFA provider interface
- [ ] Create MFA registration flow
- [ ] Implement MFA verification flow
- [ ] Build MFA fallback mechanisms
### 3.2 TOTP Implementation
- [ ] Implement TOTP algorithm (RFC 6238)
- [ ] Create QR code generation for setup
- [ ] Build key storage and management
- [ ] Implement validation with drift windows
### 3.3 WebAuthn/FIDO2 as MFA
- [ ] Implement WebAuthn MFA registration
- [ ] Create WebAuthn MFA verification
- [ ] Build integration with primary authentication methods
- [ ] Implement fallback mechanisms
### 3.4 Email OTP
- [ ] Create email OTP generation
- [ ] Implement OTP storage and validation
- [ ] Build email delivery interface
- [ ] Create rate limiting for OTP requests
### 3.5 Backup Codes
- [ ] Implement secure backup code generation
- [ ] Create storage and validation
- [ ] Build code regeneration mechanism
- [ ] Implement usage tracking
## Phase 4: Session Management
### 4.1 Session Framework
- [ ] Define Session interface
- [ ] Create SessionManager interface
- [ ] Implement session creation/validation flow
- [ ] Build session store interface
### 4.2 Cookie Sessions
- [ ] Implement secure cookie creation
- [ ] Create cookie signing and encryption
- [ ] Build cookie session validation
- [ ] Implement cookie refresh mechanism
### 4.3 JWT Sessions
- [ ] Implement JWT generation and validation
- [ ] Create claims mapping system
- [ ] Build key rotation mechanism
- [ ] Implement token blacklisting for revocation
### 4.4 Bearer Token Management
- [ ] Create token generation and validation
- [ ] Implement token refresh mechanism
- [ ] Build token revocation system
- [ ] Create token metadata storage
## Phase 5: RBAC Implementation
### 5.1 RBAC Core
- [ ] Define Role and Permission models
- [ ] Create RBACManager interface
- [ ] Implement permission checking
- [ ] Build role assignment mechanisms
### 5.2 Permission Management
- [ ] Implement permission creation and management
- [ ] Create permission inheritance system
- [ ] Build permission checking optimization
- [ ] Implement permission caching
### 5.3 Role Management
- [ ] Create role hierarchy system
- [ ] Implement role assignment to users
- [ ] Build role relationship management
- [ ] Create role-based permission resolution
### 5.4 Group Management
- [ ] Implement group creation and management
- [ ] Create user group assignment
- [ ] Build group-role relationships
- [ ] Implement group-based permission resolution
## Phase 6: Storage Adapters
### 6.1 Storage Interface
- [ ] Define comprehensive storage interfaces
- [ ] Create adapter registration system
- [ ] Implement transaction support
- [ ] Build query interface
### 6.2 In-Memory Storage
- [ ] Implement in-memory user storage
- [ ] Create in-memory session storage
- [ ] Build in-memory RBAC storage
- [ ] Implement test utilities
### 6.3 SQL Adapter
- [ ] Create standard SQL implementation
- [ ] Implement SQL schema management
- [ ] Build query optimization
- [ ] Create connection pooling
### 6.4 GORM Adapter
- [ ] Implement GORM models
- [ ] Create GORM repository implementations
- [ ] Build efficient query patterns
- [ ] Implement migration utilities
### 6.5 Ent Adapter
- [ ] Create Ent schema definitions
- [ ] Implement Ent client wrappers
- [ ] Build repository implementations
- [ ] Create efficient query builders
## Phase 7: HTTP Framework Integration
### 7.1 HTTP Framework Interface
- [ ] Define middleware interface
- [ ] Create request parser interface
- [ ] Implement response writer interface
- [ ] Build route registration system
### 7.2 Standard Library Integration
- [ ] Implement net/http middleware
- [ ] Create request handlers
- [ ] Build response utilities
- [ ] Implement session management
### 7.3 Framework-Specific Adapters
- [ ] Implement Chi integration
- [ ] Create Echo integration
- [ ] Build Fiber integration
- [ ] Implement Gin integration
- [ ] Create Gorilla Mux integration
- [ ] Build httprouter integration
- [ ] Implement Huma integration
- [ ] Create FastHTTP integration
## Phase 8: Security Features
### 8.1 Rate Limiting
- [ ] Implement rate limiter interface
- [ ] Create in-memory rate limiter
- [ ] Build distributed rate limiter
- [ ] Implement rate limit middleware
### 8.2 Brute Force Protection
- [ ] Create failed attempt tracking
- [ ] Implement progressive backoff
- [ ] Build account locking mechanism
- [ ] Create notification system
### 8.3 CSRF Protection
- [ ] Implement token generation and validation
- [ ] Create CSRF middleware
- [ ] Build token storage
- [ ] Implement SameSite cookie protection
### 8.4 Password Security
- [ ] Create password strength validation
- [ ] Implement password history
- [ ] Build password rotation policies
- [ ] Create secure password reset flow
### 8.5 Account Recovery
- [ ] Implement secure recovery flow
- [ ] Create recovery token management
- [ ] Build multi-channel verification
- [ ] Implement account recovery audit
## Phase 9: User Management
### 9.1 Registration Flow
- [ ] Implement user registration
- [ ] Create email verification
- [ ] Build user activation flow
- [ ] Implement profile creation
### 9.2 Profile Management
- [ ] Create profile update functionality
- [ ] Implement data validation
- [ ] Build custom field support
- [ ] Create profile data encryption
### 9.3 Account Management
- [ ] Implement account locking/unlocking
- [ ] Create password reset flow
- [ ] Build account deletion
- [ ] Implement account merging
## Phase 10: Testing & Documentation
### 10.1 Unit Testing
- [ ] Create comprehensive test suite for core components
- [ ] Implement mock providers
- [ ] Build test utilities
- [ ] Create test coverage reports
### 10.2 Integration Testing
- [ ] Implement end-to-end authentication flow tests
- [ ] Create storage adapter tests
- [ ] Build HTTP integration tests
- [ ] Implement security feature tests
### 10.3 Documentation
- [ ] Create comprehensive API documentation
- [ ] Build usage examples
- [ ] Implement godoc-compatible documentation
- [ ] Create security best practices guide
### 10.4 Example Applications
- [ ] Build basic authentication example
- [ ] Create complete feature showcase
- [ ] Implement custom provider example
- [ ] Build framework integration examples
## Deliverables Timeline
### Milestone 1: Core Framework (Weeks 1-2)
- [x] Project setup complete
- [ ] Plugin system architecture implemented
- [ ] Core domain models defined
- [ ] Basic authentication working
### Milestone 2: Authentication Providers (Weeks 3-4)
- [ ] OAuth2 framework implemented
- [ ] All OAuth2 providers working
- [ ] SAML authentication working
- [ ] WebAuthn passwordless authentication working
- [ ] Session management framework complete
### Milestone 3: MFA & Security (Weeks 5-6)
- [ ] All MFA methods implemented
- [ ] Security features working
- [ ] RBAC implementation complete
- [ ] User management flows working
### Milestone 4: Storage & HTTP Integration (Weeks 7-8)
- [ ] All storage adapters implemented
- [ ] HTTP framework integration complete
- [ ] Integration tests passing
- [ ] Documentation complete
## Quality Assurance Approach
### Code Quality
- [ ] All code must pass linting and formatting checks
- [ ] Test coverage must exceed 80%
- [ ] No exported function, type, or variable without documentation
- [ ] No known security vulnerabilities

72
docs/README.md Normal file
View file

@ -0,0 +1,72 @@
# Auth2 Documentation
Welcome to the Auth2 documentation. This guide will help you understand how to use and extend the Auth2 authentication library.
## Table of Contents
1. [Getting Started](./getting-started.md)
- Installation
- Basic Setup
- Configuration Options
2. [Core Concepts](./core-concepts.md)
- Architecture Overview
- Authentication Flow
- Session Management
3. [Authentication Methods](./auth-methods/README.md)
- [Username/Password](./auth-methods/basic.md)
- [OAuth2](./auth-methods/oauth2.md)
- [SAML](./auth-methods/saml.md)
4. [Multi-Factor Authentication](./mfa/README.md)
- [TOTP](./mfa/totp.md)
- [WebAuthn/FIDO2](./mfa/webauthn.md)
- [Email OTP](./mfa/email-otp.md)
- [Backup Codes](./mfa/backup-codes.md)
5. [Storage Adapters](./storage/README.md)
- [Memory](./storage/memory.md)
- [SQL](./storage/sql.md)
- [GORM](./storage/gorm.md)
- [Ent ORM](./storage/ent.md)
6. [HTTP Framework Integration](./http/README.md)
- [Standard Library](./http/std.md)
- [Chi](./http/chi.md)
- [Echo](./http/echo.md)
- [Fiber](./http/fiber.md)
- [Gin](./http/gin.md)
- [More Frameworks](./http/other.md)
7. [Role-Based Access Control](./rbac/README.md)
- [Roles and Permissions](./rbac/roles.md)
- [User-Role Assignment](./rbac/assignment.md)
- [Permission Checking](./rbac/checking.md)
8. [Security Features](./security/README.md)
- [Brute Force Protection](./security/brute-force.md)
- [Rate Limiting](./security/rate-limiting.md)
- [CSRF Protection](./security/csrf.md)
- [Account Recovery](./security/account-recovery.md)
9. [Examples](./examples/README.md)
- [Basic Authentication](./examples/basic-auth.md)
- [OAuth Integration](./examples/oauth.md)
- [Multi-Factor Authentication](./examples/mfa.md)
- [Custom Authentication Provider](./examples/custom-provider.md)
10. [API Reference](./api/README.md)
- [Public API](./api/public.md)
- [Interfaces](./api/interfaces.md)
- [Error Types](./api/errors.md)
11. [Advanced Usage](./advanced/README.md)
- [Custom Authentication Providers](./advanced/custom-auth.md)
- [Custom Storage Adapters](./advanced/custom-storage.md)
- [Extending the Core](./advanced/extending.md)
12. [Contributing](./contributing.md)
- Development Guidelines
- Testing
- Pull Request Process

3
go.mod Normal file
View file

@ -0,0 +1,3 @@
module github.com/Fishwaldo/auth2
go 1.24

251
internal/errors/errors.go Normal file
View file

@ -0,0 +1,251 @@
package errors
import (
"errors"
"fmt"
)
// Standard errors in the auth2 package
var (
// General errors
ErrInternal = errors.New("internal error")
ErrNotImplemented = errors.New("not implemented")
ErrInvalidArgument = errors.New("invalid argument")
ErrInvalidOperation = errors.New("invalid operation")
// Authentication errors
ErrAuthFailed = errors.New("authentication failed")
ErrInvalidCredentials = errors.New("invalid credentials")
ErrUserNotFound = errors.New("user not found")
ErrUserDisabled = errors.New("user account is disabled")
ErrUserLocked = errors.New("user account is locked")
// MFA errors
ErrMFARequired = errors.New("multi-factor authentication required")
ErrMFAFailed = errors.New("multi-factor authentication failed")
ErrMFANotEnabled = errors.New("multi-factor authentication not enabled")
ErrInvalidMFACode = errors.New("invalid multi-factor authentication code")
// Session errors
ErrSessionExpired = errors.New("session has expired")
ErrInvalidSession = errors.New("invalid session")
ErrSessionNotFound = errors.New("session not found")
// Token errors
ErrInvalidToken = errors.New("invalid token")
ErrTokenExpired = errors.New("token has expired")
// Permission errors
ErrPermissionDenied = errors.New("permission denied")
ErrRoleNotFound = errors.New("role not found")
// Rate limiting errors
ErrRateLimitExceeded = errors.New("rate limit exceeded")
// Plugin errors
ErrPluginNotFound = errors.New("plugin not found")
ErrIncompatiblePlugin = errors.New("incompatible plugin")
)
// AuthError represents an authentication-related error
type AuthError struct {
Err error
Code string
UserID string
Reason string
Temporary bool
}
// Error implements the error interface
func (e *AuthError) Error() string {
if e.UserID != "" {
return fmt.Sprintf("[%s] %s: %s (user: %s)", e.Code, e.Err.Error(), e.Reason, e.UserID)
}
return fmt.Sprintf("[%s] %s: %s", e.Code, e.Err.Error(), e.Reason)
}
// Unwrap returns the wrapped error
func (e *AuthError) Unwrap() error {
return e.Err
}
// NewAuthError creates a new AuthError
func NewAuthError(err error, code string, reason string, userID string, temporary bool) *AuthError {
return &AuthError{
Err: err,
Code: code,
UserID: userID,
Reason: reason,
Temporary: temporary,
}
}
// IsAuthError checks if an error is an AuthError
func IsAuthError(err error) bool {
var authErr *AuthError
return errors.As(err, &authErr)
}
// GetAuthError extracts an AuthError from an error
func GetAuthError(err error) (*AuthError, bool) {
var authErr *AuthError
if errors.As(err, &authErr) {
return authErr, true
}
return nil, false
}
// PluginError represents an error related to plugins
type PluginError struct {
Err error
PluginName string
PluginType string
Description string
}
// Error implements the error interface
func (e *PluginError) Error() string {
return fmt.Sprintf("plugin error [%s - %s]: %s: %s",
e.PluginType, e.PluginName, e.Err.Error(), e.Description)
}
// Unwrap returns the wrapped error
func (e *PluginError) Unwrap() error {
return e.Err
}
// NewPluginError creates a new PluginError
func NewPluginError(err error, pluginType, pluginName, description string) *PluginError {
return &PluginError{
Err: err,
PluginType: pluginType,
PluginName: pluginName,
Description: description,
}
}
// IsPluginError checks if an error is a PluginError
func IsPluginError(err error) bool {
var pluginErr *PluginError
return errors.As(err, &pluginErr)
}
// GetPluginError extracts a PluginError from an error
func GetPluginError(err error) (*PluginError, bool) {
var pluginErr *PluginError
if errors.As(err, &pluginErr) {
return pluginErr, true
}
return nil, false
}
// ValidationError represents an error related to validation
type ValidationError struct {
Err error
Field string
Value interface{}
Constraint string
}
// Error implements the error interface
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation error [%s]: %s (got: %v, constraint: %s)",
e.Field, e.Err.Error(), e.Value, e.Constraint)
}
// Unwrap returns the wrapped error
func (e *ValidationError) Unwrap() error {
return e.Err
}
// NewValidationError creates a new ValidationError
func NewValidationError(err error, field string, value interface{}, constraint string) *ValidationError {
return &ValidationError{
Err: err,
Field: field,
Value: value,
Constraint: constraint,
}
}
// IsValidationError checks if an error is a ValidationError
func IsValidationError(err error) bool {
var validationErr *ValidationError
return errors.As(err, &validationErr)
}
// GetValidationError extracts a ValidationError from an error
func GetValidationError(err error) (*ValidationError, bool) {
var validationErr *ValidationError
if errors.As(err, &validationErr) {
return validationErr, true
}
return nil, false
}
// StorageError represents an error related to storage operations
type StorageError struct {
Err error
Operation string
Entity string
StorageType string
}
// Error implements the error interface
func (e *StorageError) Error() string {
return fmt.Sprintf("storage error [%s - %s - %s]: %s",
e.StorageType, e.Entity, e.Operation, e.Err.Error())
}
// Unwrap returns the wrapped error
func (e *StorageError) Unwrap() error {
return e.Err
}
// NewStorageError creates a new StorageError
func NewStorageError(err error, storageType, entity, operation string) *StorageError {
return &StorageError{
Err: err,
StorageType: storageType,
Entity: entity,
Operation: operation,
}
}
// IsStorageError checks if an error is a StorageError
func IsStorageError(err error) bool {
var storageErr *StorageError
return errors.As(err, &storageErr)
}
// GetStorageError extracts a StorageError from an error
func GetStorageError(err error) (*StorageError, bool) {
var storageErr *StorageError
if errors.As(err, &storageErr) {
return storageErr, true
}
return nil, false
}
// Is checks if an error matches a target error
func Is(err, target error) bool {
return errors.Is(err, target)
}
// As finds the first error in err's chain that matches target
func As(err error, target interface{}) bool {
return errors.As(err, target)
}
// New creates a new error with the given message
func New(text string) error {
return errors.New(text)
}
// Wrap wraps an error with additional context
func Wrap(err error, message string) error {
if err == nil {
return nil
}
return fmt.Errorf("%s: %w", message, err)
}

172
pkg/auth2/auth2.go Normal file
View file

@ -0,0 +1,172 @@
package auth2
import (
"context"
"fmt"
"sync"
"github.com/Fishwaldo/auth2/internal/errors"
"github.com/Fishwaldo/auth2/pkg/config"
"github.com/Fishwaldo/auth2/pkg/log"
)
// Auth2 is the main entry point for the auth2 library
type Auth2 struct {
// Configuration
config *config.Config
// Logger
logger *log.Logger
// Registered plugins and providers
providers map[string]interface{}
// Mutex for concurrent access
mu sync.RWMutex
}
// New creates a new Auth2 instance with the provided configuration
func New(cfg *config.Config) (*Auth2, error) {
if cfg == nil {
cfg = config.DefaultConfig()
}
// Validate configuration
if err := cfg.Validate(); err != nil {
return nil, err
}
// Configure logging
logger := cfg.ConfigureLogging()
a := &Auth2{
config: cfg,
logger: logger,
providers: make(map[string]interface{}),
}
return a, nil
}
// Config returns the current configuration
func (a *Auth2) Config() *config.Config {
return a.config
}
// Logger returns the logger
func (a *Auth2) Logger() *log.Logger {
return a.logger
}
// GetProvider returns a registered provider by name and type
func (a *Auth2) GetProvider(name string, providerType interface{}) (interface{}, error) {
a.mu.RLock()
defer a.mu.RUnlock()
key := providerTypeKey(name, providerType)
if provider, ok := a.providers[key]; ok {
return provider, nil
}
return nil, errors.NewPluginError(
errors.ErrPluginNotFound,
getProviderTypeName(providerType),
name,
"provider not registered",
)
}
// RegisterProvider registers a new provider
func (a *Auth2) RegisterProvider(name string, providerType interface{}, provider interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
key := providerTypeKey(name, providerType)
// Check if already registered
if _, exists := a.providers[key]; exists {
return errors.NewPluginError(
errors.ErrInvalidOperation,
getProviderTypeName(providerType),
name,
"provider already registered",
)
}
a.providers[key] = provider
a.logger.Info("Registered provider", "name", name, "type", getProviderTypeName(providerType))
return nil
}
// UnregisterProvider removes a registered provider
func (a *Auth2) UnregisterProvider(name string, providerType interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
key := providerTypeKey(name, providerType)
if _, exists := a.providers[key]; !exists {
return errors.NewPluginError(
errors.ErrPluginNotFound,
getProviderTypeName(providerType),
name,
"provider not registered",
)
}
delete(a.providers, key)
a.logger.Info("Unregistered provider", "name", name, "type", getProviderTypeName(providerType))
return nil
}
// providerTypeKey generates a key for the providers map
func providerTypeKey(name string, providerType interface{}) string {
return name + ":" + getProviderTypeName(providerType)
}
// getProviderTypeName returns a string representation of the provider type
func getProviderTypeName(providerType interface{}) string {
switch providerType.(type) {
case nil:
return "unknown"
default:
return fmt.Sprintf("%T", providerType)
}
}
// Context returns a new context with the auth2 instance
func (a *Auth2) Context(ctx context.Context) context.Context {
return context.WithValue(ctx, auth2Key{}, a)
}
// FromContext retrieves the auth2 instance from the context
func FromContext(ctx context.Context) (*Auth2, bool) {
if auth, ok := ctx.Value(auth2Key{}).(*Auth2); ok {
return auth, true
}
return nil, false
}
// auth2Key is used as the key for storing the auth2 instance in the context
type auth2Key struct{}
// Initialize sets up the Auth2 instance
func (a *Auth2) Initialize(ctx context.Context) error {
// Initialize components based on configuration
a.logger.Info("Initializing auth2", "version", Version)
// Additional initialization logic will be added as components are implemented
return nil
}
// Shutdown performs cleanup operations
func (a *Auth2) Shutdown(ctx context.Context) error {
a.logger.Info("Shutting down auth2")
// Cleanup logic will be added as components are implemented
return nil
}

37
pkg/auth2/version.go Normal file
View file

@ -0,0 +1,37 @@
package auth2
import (
"fmt"
"runtime"
)
// Version information
var (
// Version is the current version of the auth2 library
Version = "0.1.0"
// GitCommit is the git commit hash at build time
GitCommit = "unknown"
// BuildDate is the date the binary was built
BuildDate = "unknown"
// GoVersion is the Go version used to compile the binary
GoVersion = runtime.Version()
)
// VersionInfo returns a formatted string with version information
func VersionInfo() string {
return fmt.Sprintf(
"auth2 version %s\nGit commit: %s\nBuilt on: %s\nGo version: %s",
Version,
GitCommit,
BuildDate,
GoVersion,
)
}
// GetVersion returns the current version
func GetVersion() string {
return Version
}

404
pkg/config/config.go Normal file
View file

@ -0,0 +1,404 @@
package config
import (
"encoding/json"
"os"
"time"
"github.com/Fishwaldo/auth2/internal/errors"
"github.com/Fishwaldo/auth2/pkg/log"
)
// Config holds the main configuration for the auth2 library
type Config struct {
// General settings
AppName string `json:"app_name"`
Environment string `json:"environment"` // "development", "production", "testing"
// Logging configuration
Logging *LoggingConfig `json:"logging"`
// Authentication settings
Auth *AuthConfig `json:"auth"`
// Session management
Session *SessionConfig `json:"session"`
// RBAC configuration
RBAC *RBACConfig `json:"rbac"`
// Security settings
Security *SecurityConfig `json:"security"`
// Storage configuration
Storage *StorageConfig `json:"storage"`
}
// LoggingConfig holds logging-specific configuration
type LoggingConfig struct {
Level string `json:"level"` // "debug", "info", "warn", "error"
Format string `json:"format"` // "json", "text"
AddSource bool `json:"add_source"`
Fields []string `json:"fields"` // Additional fields to log from context
}
// AuthConfig holds authentication-specific configuration
type AuthConfig struct {
PasswordPolicy *PasswordPolicy `json:"password_policy"`
RequireEmailVerification bool `json:"require_email_verification"`
MaxLoginAttempts int `json:"max_login_attempts"`
LockoutDuration time.Duration `json:"lockout_duration"`
MFAEnabled bool `json:"mfa_enabled"`
DefaultMFAType string `json:"default_mfa_type"` // "totp", "webauthn", "email", "backup"
VerificationExpiry time.Duration `json:"verification_expiry"`
}
// PasswordPolicy defines password requirements
type PasswordPolicy struct {
MinLength int `json:"min_length"`
RequireUppercase bool `json:"require_uppercase"`
RequireLowercase bool `json:"require_lowercase"`
RequireDigits bool `json:"require_digits"`
RequireSpecial bool `json:"require_special"`
MaxRepeatedChars int `json:"max_repeated_chars"`
PreventReuse bool `json:"prevent_reuse"`
PreventReuseCount int `json:"prevent_reuse_count"`
}
// SessionConfig holds session-specific configuration
type SessionConfig struct {
Type string `json:"type"` // "cookie", "jwt", "token"
Duration time.Duration `json:"duration"`
RefreshEnabled bool `json:"refresh_enabled"`
RefreshDuration time.Duration `json:"refresh_duration"`
Cookie *CookieConfig `json:"cookie"`
JWT *JWTConfig `json:"jwt"`
RedisEnabled bool `json:"redis_enabled"`
RedisAddress string `json:"redis_address"`
RedisPassword string `json:"redis_password"`
RedisDB int `json:"redis_db"`
DisableIPTracking bool `json:"disable_ip_tracking"`
}
// CookieConfig holds cookie-specific configuration
type CookieConfig struct {
Name string `json:"name"`
Domain string `json:"domain"`
Path string `json:"path"`
Secure bool `json:"secure"`
HTTPOnly bool `json:"http_only"`
SameSite string `json:"same_site"` // "strict", "lax", "none"
Encryption bool `json:"encryption"`
}
// JWTConfig holds JWT-specific configuration
type JWTConfig struct {
SigningMethod string `json:"signing_method"` // "HS256", "HS384", "HS512", "RS256", "RS384", "RS512"
SigningKey string `json:"signing_key"`
PublicKey string `json:"public_key"`
PrivateKey string `json:"private_key"`
KeyFile string `json:"key_file"`
KeyRotation bool `json:"key_rotation"`
KeyRotationInterval time.Duration `json:"key_rotation_interval"`
}
// RBACConfig holds RBAC-specific configuration
type RBACConfig struct {
EnableHierarchy bool `json:"enable_hierarchy"`
EnableGroups bool `json:"enable_groups"`
CacheEnabled bool `json:"cache_enabled"`
CacheDuration time.Duration `json:"cache_duration"`
DefaultRole string `json:"default_role"`
SystemRoles []string `json:"system_roles"`
DisablePermissions bool `json:"disable_permissions"`
}
// SecurityConfig holds security-specific configuration
type SecurityConfig struct {
CSRFEnabled bool `json:"csrf_enabled"`
CSRFTokenExpiry time.Duration `json:"csrf_token_expiry"`
RateLimitEnabled bool `json:"rate_limit_enabled"`
RateLimitRequests int `json:"rate_limit_requests"`
RateLimitDuration time.Duration `json:"rate_limit_duration"`
BruteForceEnabled bool `json:"brute_force_enabled"`
BruteForceMaxAttempts int `json:"brute_force_max_attempts"`
BruteForceWindow time.Duration `json:"brute_force_window"`
BruteForceCooldown time.Duration `json:"brute_force_cooldown"`
}
// StorageConfig holds storage-specific configuration
type StorageConfig struct {
Type string `json:"type"` // "memory", "sql", "gorm", "ent"
ConnectionString string `json:"connection_string"`
MaxConnections int `json:"max_connections"`
ConnTimeout time.Duration `json:"conn_timeout"`
QueryTimeout time.Duration `json:"query_timeout"`
SQLDriver string `json:"sql_driver"` // "postgres", "mysql", "sqlite"
MigrationsPath string `json:"migrations_path"`
AutoMigrate bool `json:"auto_migrate"`
}
// DefaultConfig returns a default configuration
func DefaultConfig() *Config {
return &Config{
AppName: "auth2",
Environment: "development",
Logging: &LoggingConfig{
Level: "info",
Format: "json",
AddSource: false,
},
Auth: &AuthConfig{
PasswordPolicy: &PasswordPolicy{
MinLength: 8,
RequireUppercase: true,
RequireLowercase: true,
RequireDigits: true,
RequireSpecial: true,
MaxRepeatedChars: 3,
PreventReuse: true,
PreventReuseCount: 5,
},
RequireEmailVerification: true,
MaxLoginAttempts: 5,
LockoutDuration: 15 * time.Minute,
MFAEnabled: false,
DefaultMFAType: "totp",
VerificationExpiry: 24 * time.Hour,
},
Session: &SessionConfig{
Type: "cookie",
Duration: 24 * time.Hour,
RefreshEnabled: true,
RefreshDuration: 7 * 24 * time.Hour,
Cookie: &CookieConfig{
Name: "auth2_session",
Path: "/",
Secure: true,
HTTPOnly: true,
SameSite: "lax",
Encryption: true,
},
JWT: &JWTConfig{
SigningMethod: "HS256",
KeyRotation: false,
KeyRotationInterval: 24 * time.Hour,
},
DisableIPTracking: false,
},
RBAC: &RBACConfig{
EnableHierarchy: true,
EnableGroups: true,
CacheEnabled: true,
CacheDuration: 5 * time.Minute,
DefaultRole: "user",
SystemRoles: []string{"admin", "user", "guest"},
},
Security: &SecurityConfig{
CSRFEnabled: true,
CSRFTokenExpiry: 1 * time.Hour,
RateLimitEnabled: true,
RateLimitRequests: 100,
RateLimitDuration: 1 * time.Minute,
BruteForceEnabled: true,
BruteForceMaxAttempts: 5,
BruteForceWindow: 10 * time.Minute,
BruteForceCooldown: 30 * time.Minute,
},
Storage: &StorageConfig{
Type: "memory",
MaxConnections: 10,
ConnTimeout: 5 * time.Second,
QueryTimeout: 10 * time.Second,
AutoMigrate: true,
},
}
}
// LoadFromFile loads configuration from a JSON file
func LoadFromFile(path string) (*Config, error) {
file, err := os.Open(path)
if err != nil {
return nil, errors.Wrap(err, "failed to open config file")
}
defer file.Close()
config := DefaultConfig()
if err := json.NewDecoder(file).Decode(config); err != nil {
return nil, errors.Wrap(err, "failed to decode config file")
}
return config, nil
}
// Save writes the configuration to a file in JSON format
func (c *Config) Save(path string) error {
file, err := os.Create(path)
if err != nil {
return errors.Wrap(err, "failed to create config file")
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
if err := encoder.Encode(c); err != nil {
return errors.Wrap(err, "failed to encode config")
}
return nil
}
// Validate checks if the configuration is valid
func (c *Config) Validate() error {
// Validate logging configuration
if c.Logging != nil {
level := c.Logging.Level
if level != "debug" && level != "info" && level != "warn" && level != "error" {
return errors.NewValidationError(
errors.ErrInvalidArgument,
"logging.level",
level,
"must be one of: debug, info, warn, error",
)
}
format := c.Logging.Format
if format != "json" && format != "text" {
return errors.NewValidationError(
errors.ErrInvalidArgument,
"logging.format",
format,
"must be one of: json, text",
)
}
}
// Validate authentication configuration
if c.Auth != nil {
if c.Auth.PasswordPolicy != nil {
if c.Auth.PasswordPolicy.MinLength < 6 {
return errors.NewValidationError(
errors.ErrInvalidArgument,
"auth.password_policy.min_length",
c.Auth.PasswordPolicy.MinLength,
"must be at least 6",
)
}
}
if c.Auth.MaxLoginAttempts < 1 {
return errors.NewValidationError(
errors.ErrInvalidArgument,
"auth.max_login_attempts",
c.Auth.MaxLoginAttempts,
"must be at least 1",
)
}
if c.Auth.MFAEnabled {
mfaType := c.Auth.DefaultMFAType
if mfaType != "totp" && mfaType != "webauthn" && mfaType != "email" && mfaType != "backup" {
return errors.NewValidationError(
errors.ErrInvalidArgument,
"auth.default_mfa_type",
mfaType,
"must be one of: totp, webauthn, email, backup",
)
}
}
}
// Validate session configuration
if c.Session != nil {
sessionType := c.Session.Type
if sessionType != "cookie" && sessionType != "jwt" && sessionType != "token" {
return errors.NewValidationError(
errors.ErrInvalidArgument,
"session.type",
sessionType,
"must be one of: cookie, jwt, token",
)
}
if c.Session.Cookie != nil && c.Session.Type == "cookie" {
sameSite := c.Session.Cookie.SameSite
if sameSite != "strict" && sameSite != "lax" && sameSite != "none" {
return errors.NewValidationError(
errors.ErrInvalidArgument,
"session.cookie.same_site",
sameSite,
"must be one of: strict, lax, none",
)
}
}
if c.Session.JWT != nil && c.Session.Type == "jwt" {
method := c.Session.JWT.SigningMethod
validMethods := map[string]bool{
"HS256": true, "HS384": true, "HS512": true,
"RS256": true, "RS384": true, "RS512": true,
}
if !validMethods[method] {
return errors.NewValidationError(
errors.ErrInvalidArgument,
"session.jwt.signing_method",
method,
"must be one of: HS256, HS384, HS512, RS256, RS384, RS512",
)
}
}
}
// Validate storage configuration
if c.Storage != nil {
storageType := c.Storage.Type
if storageType != "memory" && storageType != "sql" && storageType != "gorm" && storageType != "ent" {
return errors.NewValidationError(
errors.ErrInvalidArgument,
"storage.type",
storageType,
"must be one of: memory, sql, gorm, ent",
)
}
if storageType == "sql" && c.Storage.SQLDriver == "" {
return errors.NewValidationError(
errors.ErrInvalidArgument,
"storage.sql_driver",
c.Storage.SQLDriver,
"cannot be empty when storage.type is 'sql'",
)
}
}
return nil
}
// ConfigureLogging sets up the logging system based on the configuration
func (c *Config) ConfigureLogging() *log.Logger {
var level log.Level
switch c.Logging.Level {
case "debug":
level = log.LevelDebug
case "info":
level = log.LevelInfo
case "warn":
level = log.LevelWarn
case "error":
level = log.LevelError
default:
level = log.LevelInfo
}
logConfig := &log.Config{
Level: level,
Format: c.Logging.Format,
AddSource: c.Logging.AddSource,
ContextKeys: c.Logging.Fields,
}
logger := log.New(logConfig)
log.SetDefault(logger)
return logger
}

156
pkg/log/log.go Normal file
View file

@ -0,0 +1,156 @@
package log
import (
"context"
"io"
"log/slog"
"os"
)
// Level defines the logging level
type Level = slog.Level
// Predefined logging levels
const (
LevelDebug = slog.LevelDebug
LevelInfo = slog.LevelInfo
LevelWarn = slog.LevelWarn
LevelError = slog.LevelError
)
// Logger wraps slog.Logger to provide a consistent logging interface
type Logger struct {
*slog.Logger
}
// Config holds the configuration for the logger
type Config struct {
Level Level
Format string // "json" or "text"
Writer io.Writer
AddSource bool
ContextKeys []string
}
// DefaultConfig returns the default logging configuration
func DefaultConfig() *Config {
return &Config{
Level: LevelInfo,
Format: "json",
Writer: os.Stderr,
AddSource: false,
}
}
// New creates a new Logger with the given configuration
func New(cfg *Config) *Logger {
if cfg == nil {
cfg = DefaultConfig()
}
var handler slog.Handler
handlerOpts := &slog.HandlerOptions{
Level: cfg.Level,
AddSource: cfg.AddSource,
}
if cfg.Format == "json" {
handler = slog.NewJSONHandler(cfg.Writer, handlerOpts)
} else {
handler = slog.NewTextHandler(cfg.Writer, handlerOpts)
}
return &Logger{
Logger: slog.New(handler),
}
}
// WithContext adds context values to the logger
func (l *Logger) WithContext(ctx context.Context) *Logger {
logger := l.Logger
// Extract values from context and add them to the logger
// This can be expanded to include specific context keys
// based on the application's needs
return &Logger{
Logger: logger,
}
}
// WithField adds a field to the logger
func (l *Logger) WithField(key string, value interface{}) *Logger {
return &Logger{
Logger: l.Logger.With(key, value),
}
}
// WithFields adds multiple fields to the logger
func (l *Logger) WithFields(fields map[string]interface{}) *Logger {
logger := l.Logger
for k, v := range fields {
logger = logger.With(k, v)
}
return &Logger{
Logger: logger,
}
}
// Global default logger
var defaultLogger = New(DefaultConfig())
// SetDefault sets the default logger
func SetDefault(logger *Logger) {
defaultLogger = logger
slog.SetDefault(logger.Logger)
}
// Default returns the default logger
func Default() *Logger {
return defaultLogger
}
// GetLoggerFromContext retrieves a logger from the context
// If no logger is found in the context, the default logger is returned
func GetLoggerFromContext(ctx context.Context) *Logger {
if ctx == nil {
return defaultLogger
}
// Check for logger in context
if loggerValue := ctx.Value(loggerKey{}); loggerValue != nil {
if logger, ok := loggerValue.(*Logger); ok {
return logger
}
}
return defaultLogger
}
// loggerKey is used as the key for storing the logger in the context
type loggerKey struct{}
// ContextWithLogger returns a new context with the logger
func ContextWithLogger(ctx context.Context, logger *Logger) context.Context {
return context.WithValue(ctx, loggerKey{}, logger)
}
// Convenience methods to use the default logger
func Debug(msg string, args ...interface{}) {
defaultLogger.Debug(msg, args...)
}
func Info(msg string, args ...interface{}) {
defaultLogger.Info(msg, args...)
}
func Warn(msg string, args ...interface{}) {
defaultLogger.Warn(msg, args...)
}
func Error(msg string, args ...interface{}) {
defaultLogger.Error(msg, args...)
}