Authorization Package
This package provides JWT-based authorization services for the Tender Management system, following Clean Architecture principles and best practices.
Features
- JWT Token Management: Generate and validate access and refresh tokens
- Middleware Support: Ready-to-use Echo middleware for authentication and authorization
- Role-Based Access Control: Built-in role validation middleware
- Company Scoping: Company-level access control middleware
- Token Blacklisting: Support for token invalidation (Redis-ready)
- Injectable Configuration: Dependency injection for flexible configuration management
- Configuration Providers: Multiple ways to create configurations (environment, custom, hybrid)
- Security Best Practices: Secure token generation and validation
Quick Start
1. Basic Usage
package main
import (
"tm/pkg/authorization"
"tm/pkg/logger"
)
func main() {
// Create logger
logger := logger.NewLogger()
// Create configuration provider (environment-based)
configProvider := authorization.NewEnvConfigProvider()
config := configProvider.CreateConfig()
// Create authorization service
authService := authorization.NewAuthorizationService(config, logger)
// Generate tokens
tokenPair, err := authService.GenerateTokenPair(
"user123",
"user@example.com",
"admin",
"company456",
)
if err != nil {
log.Fatal(err)
}
// Validate tokens
result, err := authService.ValidateAccessToken(tokenPair.AccessToken)
if err != nil {
log.Fatal(err)
}
if result.Valid {
fmt.Printf("User ID: %s, Role: %s\n", result.Claims.UserID, result.Claims.Role)
}
}
2. Using with Echo Framework
package main
import (
"github.com/labstack/echo/v4"
"tm/pkg/authorization"
"tm/pkg/logger"
)
func main() {
e := echo.New()
// Setup authorization
logger := logger.NewLogger()
configProvider := authorization.NewEnvConfigProvider()
config := configProvider.CreateConfig()
authService := authorization.NewAuthorizationService(config, logger)
// Public routes
e.POST("/login", loginHandler)
// Protected routes
protected := e.Group("/api")
protected.Use(authorization.AuthMiddleware(authService))
{
protected.GET("/profile", getProfileHandler)
protected.PUT("/profile", updateProfileHandler)
}
// Admin routes
admin := protected.Group("/admin")
admin.Use(authorization.AdminMiddleware(authService))
{
admin.POST("/users", createUserHandler)
admin.GET("/users", listUsersHandler)
}
// Company-scoped routes
company := protected.Group("/company/:company_id")
company.Use(authorization.CompanyMiddleware(authService))
{
company.GET("/tenders", getCompanyTendersHandler)
}
e.Start(":8080")
}
3. Configuration Management
The authorization package uses dependency injection for configuration. You can create configurations using different providers:
Configuration Providers
The package provides several configuration providers that implement the ConfigProvider interface:
type ConfigProvider interface {
CreateConfig() *AuthorizationConfig
}
Environment-based Configuration
// Create configuration from environment variables
configProvider := authorization.NewEnvConfigProvider()
config := configProvider.CreateConfig()
Custom Configuration
// Create custom configuration
customConfig := &authorization.AuthorizationConfig{
AccessTokenSecret: "my-secret-key",
RefreshTokenSecret: "my-refresh-secret",
AccessTokenExpiry: 30 * time.Minute,
RefreshTokenExpiry: 14 * 24 * time.Hour,
Issuer: "my-app",
Audience: "my-api",
}
configProvider := authorization.NewCustomConfigProvider(customConfig)
config := configProvider.CreateConfig()
Hybrid Configuration
// Combine multiple configuration sources with priority
envProvider := authorization.NewEnvConfigProvider()
customProvider := authorization.NewCustomConfigProvider(customConfig)
hybridProvider := authorization.NewHybridConfigProvider(envProvider, customProvider)
config := hybridProvider.CreateConfig()
// The hybrid provider applies configurations in order:
// 1. Environment variables (base)
// 2. Custom overrides (priority)
File-based Configuration (Future)
// File-based configuration (placeholder for future implementation)
fileProvider := authorization.NewFileConfigProvider("config.yaml")
config := fileProvider.CreateConfig()
Environment Variables
# Required
export TM_ACCESS_TOKEN_SECRET="your-super-secret-access-key-here"
export TM_REFRESH_TOKEN_SECRET="your-super-secret-refresh-key-here"
# Optional (with defaults)
export TM_ACCESS_TOKEN_EXPIRY="15m"
export TM_REFRESH_TOKEN_EXPIRY="168h" # 7 days
export TM_JWT_ISSUER="tender-management"
export TM_JWT_AUDIENCE="tender-management-api"
API Reference
AuthorizationService Interface
type AuthorizationService interface {
// Generate tokens
GenerateTokenPair(userID, email, role, companyID string) (*TokenPair, error)
GenerateAccessToken(userID, email, role, companyID string) (string, error)
GenerateRefreshToken(userID, email, role, companyID string) (string, error)
// Validate tokens
ValidateToken(tokenString string) (*TokenValidationResult, error)
ValidateAccessToken(tokenString string) (*TokenValidationResult, error)
ValidateRefreshToken(tokenString string) (*TokenValidationResult, error)
// Token management
RefreshAccessToken(refreshToken string) (string, error)
InvalidateToken(tokenString string) error
IsTokenBlacklisted(tokenString string) (bool, error)
}
Middleware Functions
// Authentication middleware
AuthMiddleware(authService AuthorizationService) echo.MiddlewareFunc
// Role-based access control
RoleMiddleware(authService AuthorizationService, requiredRoles ...string) echo.MiddlewareFunc
// Admin-only access
AdminMiddleware(authService AuthorizationService) echo.MiddlewareFunc
// Company-scoped access
CompanyMiddleware(authService AuthorizationService) echo.MiddlewareFunc
// Optional authentication
OptionalAuthMiddleware(authService AuthorizationService) echo.MiddlewareFunc
Context Helpers
// Extract user information from context
userID, email, role, companyID, authenticated := GetUserFromContext(c)
// Extract token claims
claims := GetTokenClaimsFromContext(c)
// Check authentication status
if IsAuthenticated(c) {
// User is authenticated
}
Token Structure
Access Token Claims
{
"user_id": "uuid-string",
"email": "user@example.com",
"role": "admin",
"company_id": "company-uuid",
"token_type": "access",
"iss": "tender-management",
"sub": "uuid-string",
"aud": ["tender-management-api"],
"exp": 1640995200,
"nbf": 1640994300,
"iat": 1640994300
}
Refresh Token Claims
{
"user_id": "uuid-string",
"email": "user@example.com",
"role": "admin",
"company_id": "company-uuid",
"token_type": "refresh",
"iss": "tender-management",
"sub": "uuid-string",
"aud": ["tender-management-api"],
"exp": 1641600000,
"nbf": 1640994300,
"iat": 1640994300
}
Security Features
Token Security
- Separate Secrets: Access and refresh tokens use different signing secrets
- Short Expiry: Access tokens expire quickly (default: 15 minutes)
- Long Expiry: Refresh tokens last longer (default: 7 days)
- Type Validation: Tokens are validated against their intended type
- Blacklist Support: Tokens can be invalidated and blacklisted
Best Practices
- Environment Variables: Never hardcode secrets in code
- Strong Secrets: Use cryptographically strong random secrets
- HTTPS Only: Always use HTTPS in production
- Token Rotation: Implement refresh token rotation for security
- Audit Logging: Log all authentication and authorization events
Configuration Validation
The package automatically validates configuration on startup:
config := authorization.LoadConfigFromEnv()
if err := config.ValidateConfig(); err != nil {
log.Fatal("Invalid authorization configuration:", err)
}
Validation checks:
- Non-empty secrets
- Valid expiry durations
- Access token expiry < refresh token expiry
- Non-empty issuer and audience
Error Handling
All functions return descriptive errors:
result, err := authService.ValidateAccessToken(token)
if err != nil {
// Handle validation error
return err
}
if !result.Valid {
if result.Expired {
return response.Unauthorized(c, "Token has expired")
}
return response.Unauthorized(c, result.Error)
}
Future Enhancements
- Redis Integration: Full token blacklist implementation
- Rate Limiting: Token generation rate limiting
- Audit Trail: Comprehensive authentication audit logging
- Multi-Tenant: Enhanced company-level security
- OAuth2 Support: Integration with external OAuth providers
Dependencies
github.com/golang-jwt/jwt/v5- JWT implementationgithub.com/labstack/echo/v4- HTTP frameworktm/pkg/logger- Internal logging packagetm/pkg/response- Internal response package
License
This package is part of the Tender Management system and follows the same license terms.