A storage-agnostic JWT authentication library for Go applications with pluggable authentication providers and flexible data persistence.
- Storage-Agnostic: Works with any data storage (MySQL, PostgreSQL, Redis, in-memory, external APIs)
- Pluggable Providers: Basic Auth, API Key authentication (extensible for OAuth, LDAP, etc.)
- JWT Tokens: Access tokens and refresh tokens with custom claims
- Clean Architecture: Clear separation of concerns with dependency injection
- Zero Database Dependencies: Library core has no hardcoded storage requirements
go get github.com/responsible-api/responsible-authPerfect for testing, development, or simple applications:
package main
import (
"log"
"time"
"github.com/responsible-api/responsible-auth/auth"
"github.com/responsible-api/responsible-auth/resource/access"
"github.com/responsible-api/responsible-auth/service"
"github.com/responsible-api/responsible-auth/storage/memory"
)
func main() {
// 1. Create in-memory storage (includes sample data)
storage := memory.NewInMemoryStorage()
// 2. Initialize auth service
authService := auth.NewAuth(service.NewBasicAuth(), storage, auth.AuthOptions{
SecretKey: "your-super-secure-secret-key-here",
TokenDuration: 5 * time.Hour,
RefreshTokenDuration: 24 * 7 * time.Hour,
TokenLeeway: 10 * time.Second,
Issuer: "https://your-app.com",
Subject: "your-app-user",
// Add custom claims
CustomClaims: map[string]interface{}{
"organization": "your-org",
"tier": "premium",
},
})
// 3. Authenticate user (test@example.com:password123)
user, pass, err := authService.Provider.Decode("dGVzdEBleGFtcGxlLmNvbTpwYXNzd29yZDEyMw==")
if err != nil {
log.Fatalf("Failed to decode credentials: %v", err)
}
// 4. Generate access token
accessToken, err := authService.Provider.CreateAccessToken(user, pass)
if err != nil {
log.Fatalf("Failed to create access token: %v", err)
}
// 5. Generate refresh token
refreshToken, err := authService.Provider.CreateRefreshToken(user, pass)
if err != nil {
log.Fatalf("Failed to create refresh token: %v", err)
}
// 6. Build response
expiry, _ := accessToken.GetExpirationTime()
model := access.NewModel()
model.WithAccessToken(accessToken.GetToken())
model.WithRefreshToken(refreshToken.GetToken())
model.WithExpiresIn(expiry.Unix())
model.WithCreatedAt(time.Now().Unix())
response := model.ToResponseDTO()
log.Printf("π Authentication successful!")
log.Printf("Access Token: %s", response.AccessToken)
log.Printf("Refresh Token: %s", response.RefreshToken)
log.Printf("Expires In: %d seconds", response.ExpiresIn)
}Run it:
go run examples/memory-storage/main.goFor production applications with persistent storage:
package main
import (
"log"
"time"
"github.com/responsible-api/responsible-auth/auth"
"github.com/responsible-api/responsible-auth/service"
"github.com/responsible-api/responsible-auth/storage/mysql"
"github.com/responsible-api/responsible-auth/tools"
)
func main() {
// 1. Connect to MySQL database
db, err := tools.NewDatabase()
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
// 2. Create MySQL storage implementation
storage := mysql.NewMySQLStorage(db)
// 3. Initialize auth service
authService := auth.NewAuth(service.NewBasicAuth(), storage, auth.AuthOptions{
SecretKey: "your-super-secure-secret-key-here",
TokenDuration: 5 * time.Hour,
RefreshTokenDuration: 24 * 7 * time.Hour,
TokenLeeway: 10 * time.Second,
Issuer: "https://your-app.com",
Subject: "your-app-user",
})
// 4. Same authentication flow as above...
// (decode credentials, create tokens, build response)
}Setup MySQL:
- Execute
migration/schema.sqlto create database schema - Set environment variables:
export DB_HOST="localhost" export DB_PORT="3306" export DB_USER="your_user" export DB_PASS="your_password" export DB_NAME="responsible_api"
Run it:
go run cmd/api/main.goCreate your own storage backend for Redis, PostgreSQL, external APIs, etc.:
package main
import (
"github.com/responsible-api/responsible-auth/resource/user"
"github.com/responsible-api/responsible-auth/storage"
)
// RedisStorage implements UserStorage interface with Redis
type RedisStorage struct {
client *redis.Client
}
func (r *RedisStorage) FindUserByCredentials(username, credentials string) (*user.User, error) {
// Query Redis for user
userData, err := r.client.HGetAll(ctx, "user:"+username).Result()
if err != nil {
return nil, err
}
// Validate credentials
if userData["secret"] != credentials {
return nil, errors.New("invalid credentials")
}
// Convert to user struct and return
return &user.User{
AccountID: parseUint64(userData["account_id"]),
Name: userData["name"],
Mail: userData["mail"],
Secret: userData["secret"],
// ... other fields
}, nil
}
func (r *RedisStorage) FindUserByAPIKey(apiKey string) (*user.User, error) {
// Your Redis API key lookup logic
}
func (r *RedisStorage) UpdateRefreshToken(userID, refreshToken string) error {
// Your Redis refresh token storage logic
}
func (r *RedisStorage) ValidateRefreshToken(refreshToken string) (*user.User, error) {
// Your Redis refresh token validation logic
}
func NewRedisStorage(client *redis.Client) storage.UserStorage {
return &RedisStorage{client: client}
}
// Usage
func main() {
redisClient := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
storage := NewRedisStorage(redisClient)
authService := auth.NewAuth(service.NewBasicAuth(), storage, options)
}Switch to API Key authentication instead of Basic Auth:
// Use API Key provider instead of Basic Auth
storage := memory.NewInMemoryStorage()
apiProvider := auth.NewAuth(service.NewApiKeyAuth(), storage, auth.AuthOptions{
SecretKey: "your-super-secure-secret-key-here", // Replace with a secure key
TokenDuration: 5 * time.Hour, // 5 minute token duration
RefreshTokenDuration: 24 * 7 * time.Hour, // 7 day refresh token duration
TokenLeeway: 10 * time.Second, // 10 seconds leeway
CookieDuration: 7 * 24 * time.Hour, // 7 days cookie duration
Issuer: "https://example.com", // Replace with your issuer
IssuedAt: time.Now().Unix(), // Time we issued the token, can be now or in the future
NotBefore: time.Now().Unix(), // Time before which the token is not valid
Subject: "test-user", // Replace with your subject,
// Custom claims if needed
CustomClaims: map[string]interface{}{
"organization": "example-org",
"tier": "premium",
},
})
apiUser, apiPass, err := apiProvider.Provider.Decode("api_key_12345")
if err != nil {
log.Fatalf("Failed to validate API key: %v", err)
}
log.Printf("API Key valid: %v", apiUser != "" && apiPass != "")
apiToken, err := apiProvider.Provider.CreateAccessToken(apiUser, apiPass)
if err != nil {
log.Fatalf("Failed to create access token for API user %s: %v", apiUser, err)
}
log.Printf(" -- - API Access Token created: %s", apiToken.GetToken())# Install dependencies
go mod tidy
# Run tests
go test ./...
# Run MySQL example (requires database)
go run cmd/api/main.go
# Run in-memory example (no dependencies)
go run examples/memory-storage/main.go
# Build binary
go build -o bin/api cmd/api/main.goresponsible-auth/
βββ auth/ # Core authentication logic
βββ service/ # Authentication providers (Basic Auth, API Key)
βββ storage/ # Storage interface and implementations
β βββ interface.go # UserStorage interface definition
β βββ mysql/ # MySQL implementation
β βββ memory/ # In-memory implementation
βββ resource/ # Data models and DTOs
βββ internal/ # JWT token creation and validation
βββ examples/ # Complete usage examples
βββ migration/ # Database schema
βββ tools/ # Database utilities
- Storage Flexibility: Use any database or storage system
- Provider Extensibility: Easy to add new authentication methods
- Zero Lock-in: No vendor or database dependencies
- Production Ready: Includes MySQL implementation with proper schema
- Developer Friendly: In-memory option for quick testing
- Clean Architecture: Clear separation of concerns
- Storage Guide: Comprehensive guide for implementing custom storage
- API Documentation: Complete API reference
- Examples: See
examples/directory for complete implementations
If upgrading from MySQL-only versions:
// Old
authService := auth.NewAuth(provider, options)
// New (with MySQL)
db, _ := tools.NewDatabase()
storage := mysql.NewMySQLStorage(db)
authService := auth.NewAuth(provider, storage, options)
// New (with in-memory for testing)
storage := memory.NewInMemoryStorage()
authService := auth.NewAuth(provider, storage, options)go test ./...go test -v ./...go test ./service -v
go test ./examples/memory -v
go test ./internal -vFeedback and suggestions are welcome! Please open an issue for any bugs or feature requests.
Author of this project @vince-scarpa