Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions .github/workflows/examples-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ jobs:
- testing-scenarios
- observer-pattern
- health-aware-reverse-proxy
- auth-demo
- cache-demo
- scheduler-demo
- eventbus-demo
- jsonschema-demo
- letsencrypt-demo
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down Expand Up @@ -279,6 +285,106 @@ jobs:
exit 1
fi

elif [ "${{ matrix.example }}" = "auth-demo" ]; then
# Auth demo needs to test authentication endpoints
timeout 10s ./example &
PID=$!
sleep 3

# Test health endpoint
if curl -f http://localhost:8080/health; then
echo "✅ auth-demo health check passed"
else
echo "❌ auth-demo health check failed"
kill $PID 2>/dev/null || true
exit 1
fi

kill $PID 2>/dev/null || true

elif [ "${{ matrix.example }}" = "cache-demo" ]; then
# Cache demo needs to test cache endpoints
timeout 10s ./example &
PID=$!
sleep 3

# Test health endpoint
if curl -f http://localhost:8080/health; then
echo "✅ cache-demo health check passed"
else
echo "❌ cache-demo health check failed"
kill $PID 2>/dev/null || true
exit 1
fi

kill $PID 2>/dev/null || true

elif [ "${{ matrix.example }}" = "scheduler-demo" ]; then
# Scheduler demo needs to test job scheduling
timeout 10s ./example &
PID=$!
sleep 3

# Test health endpoint
if curl -f http://localhost:8080/health; then
echo "✅ scheduler-demo health check passed"
else
echo "❌ scheduler-demo health check failed"
kill $PID 2>/dev/null || true
exit 1
fi

kill $PID 2>/dev/null || true

elif [ "${{ matrix.example }}" = "eventbus-demo" ]; then
# EventBus demo needs to test pub/sub functionality
timeout 10s ./example &
PID=$!
sleep 3

# Test health endpoint
if curl -f http://localhost:8080/health; then
echo "✅ eventbus-demo health check passed"
else
echo "❌ eventbus-demo health check failed"
kill $PID 2>/dev/null || true
exit 1
fi

kill $PID 2>/dev/null || true

elif [ "${{ matrix.example }}" = "jsonschema-demo" ]; then
# JSON Schema demo needs to test validation endpoints
timeout 10s ./example &
PID=$!
sleep 3

# Test health endpoint
if curl -f http://localhost:8080/health; then
echo "✅ jsonschema-demo health check passed"
else
echo "❌ jsonschema-demo health check failed"
kill $PID 2>/dev/null || true
exit 1
fi

kill $PID 2>/dev/null || true

elif [ "${{ matrix.example }}" = "letsencrypt-demo" ]; then
# Let's Encrypt demo just needs to start (won't actually get certificates in CI)
timeout 5s ./example &
PID=$!
sleep 3

# Check if process is still running (no immediate crash)
if kill -0 $PID 2>/dev/null; then
echo "✅ letsencrypt-demo started successfully"
kill $PID 2>/dev/null || true
else
echo "❌ letsencrypt-demo failed to start or crashed immediately"
exit 1
fi

elif [ "${{ matrix.example }}" = "reverse-proxy" ] || [ "${{ matrix.example }}" = "http-client" ] || [ "${{ matrix.example }}" = "advanced-logging" ] || [ "${{ matrix.example }}" = "verbose-debug" ] || [ "${{ matrix.example }}" = "instance-aware-db" ] || [ "${{ matrix.example }}" = "feature-flag-proxy" ]; then
# These apps just need to start without immediate errors
timeout 5s ./example &
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/module-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ on:
- chimux
- database
- eventbus
- eventlogger
- httpclient
- httpserver
- jsonschema
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ The `examples/` directory contains complete, working examples that demonstrate h
| [**http-client**](./examples/http-client/) | HTTP client with proxy backend | HTTP client integration, request routing |
| [**advanced-logging**](./examples/advanced-logging/) | Advanced HTTP client logging | Verbose logging, file output, request/response inspection |
| [**observer-pattern**](./examples/observer-pattern/) | Event-driven architecture demo | Observer pattern, CloudEvents, event logging, real-time events |
| [**feature-flag-proxy**](./examples/feature-flag-proxy/) | Feature flag controlled routing | Reverse proxy with tenant-aware feature flags |
| [**health-aware-reverse-proxy**](./examples/health-aware-reverse-proxy/) | Health monitoring proxy | Reverse proxy with backend health checks |
| [**instance-aware-db**](./examples/instance-aware-db/) | Multiple database connections | Instance-aware environment configuration |
| [**multi-tenant-app**](./examples/multi-tenant-app/) | Multi-tenant application | Tenant-aware modules and configuration |
| [**observer-demo**](./examples/observer-demo/) | Event system demonstration | Observer pattern with event logging |
| [**testing-scenarios**](./examples/testing-scenarios/) | Testing and integration patterns | Various testing scenarios and configurations |
| [**verbose-debug**](./examples/verbose-debug/) | Debugging and diagnostics | Verbose logging and debug output |
| [**auth-demo**](./examples/auth-demo/) | Authentication system | JWT tokens, password hashing, protected routes |
| [**cache-demo**](./examples/cache-demo/) | Caching system | In-memory and Redis caching with TTL |
| [**scheduler-demo**](./examples/scheduler-demo/) | Job scheduling system | Cron jobs, one-time tasks, job management |

### Quick Start with Examples

Expand All @@ -121,6 +131,12 @@ Visit the [examples directory](./examples/) for detailed documentation, configur
- **Explore [http-client](./examples/http-client/)** for HTTP client integration patterns
- **Study [advanced-logging](./examples/advanced-logging/)** for debugging and monitoring techniques
- **Learn [observer-pattern](./examples/observer-pattern/)** for event-driven architecture with CloudEvents
- **Examine [multi-tenant-app](./examples/multi-tenant-app/)** for building SaaS applications
- **Investigate [instance-aware-db](./examples/instance-aware-db/)** for multiple database configurations
- **Review [feature-flag-proxy](./examples/feature-flag-proxy/)** for dynamic routing and tenant features
- **Check [auth-demo](./examples/auth-demo/)** for JWT authentication and security patterns
- **Explore [cache-demo](./examples/cache-demo/)** for caching strategies and performance optimization
- **Study [scheduler-demo](./examples/scheduler-demo/)** for automated task scheduling and job management

## Installation

Expand Down
98 changes: 98 additions & 0 deletions examples/auth-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Authentication Module Demo

This example demonstrates how to use the auth module for JWT-based authentication, password hashing, and user management.

## Overview

The example sets up:
- JWT token generation and validation
- Password hashing with bcrypt
- User registration and login endpoints
- Protected routes that require authentication
- In-memory user storage for demonstration

## Features Demonstrated

1. **JWT Authentication**: Generate and validate JWT tokens
2. **Password Security**: Hash passwords with bcrypt
3. **User Management**: Register new users and authenticate existing ones
4. **Protected Routes**: Secure endpoints that require valid tokens
5. **HTTP Integration**: RESTful API endpoints for auth operations

## API Endpoints

- `POST /api/register` - Register a new user
- `POST /api/login` - Login with username/password
- `GET /api/profile` - Get user profile (requires JWT token)
- `POST /api/refresh` - Refresh JWT token

## Running the Example

1. Start the application:
```bash
go run main.go
```

2. The application will start on port 8080

## Testing Authentication

### Register a new user
```bash
curl -X POST http://localhost:8080/api/register \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "SecurePassword123!"}'
```

### Login with credentials
```bash
curl -X POST http://localhost:8080/api/login \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "SecurePassword123!"}'
```

This will return a JWT token that you can use for authenticated requests.

### Access protected endpoint
```bash
# Replace {TOKEN} with the JWT token from login
curl -H "Authorization: Bearer {TOKEN}" \
http://localhost:8080/api/profile
```

### Refresh token
```bash
# Replace {TOKEN} with the JWT token
curl -X POST http://localhost:8080/api/refresh \
-H "Authorization: Bearer {TOKEN}"
```

## Configuration

The auth module is configured in `config.yaml`:

```yaml
auth:
jwt_secret: "your-super-secret-key-change-in-production"
jwt_expiration: 3600 # 1 hour in seconds
password_min_length: 8
bcrypt_cost: 12
```

## Security Features

1. **Strong Password Requirements**: Configurable minimum length and complexity
2. **JWT Expiration**: Tokens expire after a configurable time
3. **Secure Password Hashing**: Uses bcrypt with configurable cost
4. **Token Validation**: Comprehensive JWT token validation

## Error Handling

The example includes proper error handling for:
- Invalid credentials
- Expired tokens
- Malformed requests
- User registration conflicts
- Password validation failures

This demonstrates how to build secure authentication into modular applications.
18 changes: 18 additions & 0 deletions examples/auth-demo/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
auth:
jwt:
secret: "demo-secret-key-change-in-production"
expiration: 1h # 1 hour
password:
min_length: 8
bcrypt_cost: 12

httpserver:
port: 8080
host: "localhost"

chimux:
cors:
enabled: true
allowed_origins: ["*"]
allowed_methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
allowed_headers: ["*"]
37 changes: 37 additions & 0 deletions examples/auth-demo/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module auth-demo

go 1.24.2

toolchain go1.24.5

require (
github.com/GoCodeAlone/modular v0.0.0-00010101000000-000000000000
github.com/GoCodeAlone/modular/modules/auth v0.0.0-00010101000000-000000000000
github.com/GoCodeAlone/modular/modules/chimux v0.0.0-00010101000000-000000000000
github.com/GoCodeAlone/modular/modules/httpserver v0.0.0-00010101000000-000000000000
github.com/go-chi/chi/v5 v5.2.2
)

require (
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/cloudevents/sdk-go/v2 v2.16.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.3 // indirect
github.com/golobby/cast v1.3.3 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/GoCodeAlone/modular => ../../

replace github.com/GoCodeAlone/modular/modules/auth => ../../modules/auth

replace github.com/GoCodeAlone/modular/modules/chimux => ../../modules/chimux

replace github.com/GoCodeAlone/modular/modules/httpserver => ../../modules/httpserver
72 changes: 72 additions & 0 deletions examples/auth-demo/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/cloudevents/sdk-go/v2 v2.16.1 h1:G91iUdqvl88BZ1GYYr9vScTj5zzXSyEuqbfE63gbu9Q=
github.com/cloudevents/sdk-go/v2 v2.16.1/go.mod h1:v/kVOaWjNfbvc6tkhhlkhvLapj8Aa8kvXiH5GiOHCKI=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golobby/cast v1.3.3 h1:s2Lawb9RMz7YyYf8IrfMQY4IFmA1R/lgfmj97Vc6fig=
github.com/golobby/cast v1.3.3/go.mod h1:0oDO5IT84HTXcbLDf1YXuk0xtg/cRDrxhbpWKxwtJCY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading