A JWT-based authentication microservice built with Go, PostgreSQL, and Chi router.
- User registration and login
- JWT access tokens (1 hour expiry) and refresh tokens (7 days expiry)
- Scope-based access control (RBAC)
- User profile management
- Admin user management endpoints
- Password hashing with bcrypt
- Kubernetes ready with full manifest set
- Automated expired token cleanup
- Start PostgreSQL:
docker compose up -d- Run the server:
go run ./cmd/serverThe server starts on http://localhost:8080.
- Update secrets in
k8s/secret.yaml - Update ingress host in
k8s/ingress.yaml - Deploy:
./deploy.sh applySee Kubernetes Deployment for more details.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/auth/register |
Register a new user |
| POST | /api/v1/auth/login |
Login and get tokens |
| POST | /api/v1/auth/refresh |
Refresh access token |
| Method | Endpoint | Scope Required | Description |
|---|---|---|---|
| GET | /api/v1/profile |
profile:read |
Get current user profile |
| PUT | /api/v1/profile |
profile:update |
Update profile |
| POST | /api/v1/profile/password |
profile:update |
Change password |
| Method | Endpoint | Scope Required | Description |
|---|---|---|---|
| GET | /api/v1/users |
users:read |
List all users |
| GET | /api/v1/users/{id} |
users:read |
Get user by ID |
| POST | /api/v1/users |
users:create |
Create a new user |
| PUT | /api/v1/users/{id} |
users:update |
Update user |
| DELETE | /api/v1/users/{id} |
users:delete |
Delete user |
| Scope | Description |
|---|---|
profile:read |
Read own profile |
profile:update |
Update own profile |
users:read |
Read all users |
users:create |
Create users |
users:update |
Update users |
users:delete |
Delete users |
admin:read |
Admin read access |
admin:create |
Admin create access |
admin:update |
Admin update access |
admin:delete |
Admin delete access |
curl -X POST http://localhost:8080/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"username": "john", "email": "john@example.com", "password": "password123", "name": "John Doe"}'curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "john", "password": "password123"}'curl http://localhost:8080/api/v1/profile \
-H "Authorization: Bearer <auth_token>"curl -X POST http://localhost:8080/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "<refresh_token>"}'| Variable | Default | Description |
|---|---|---|
SERVER_PORT |
8080 | Server port |
DB_HOST |
localhost | PostgreSQL host |
DB_PORT |
5432 | PostgreSQL port |
DB_USER |
postgres | PostgreSQL user |
DB_PASSWORD |
postgres | PostgreSQL password |
DB_NAME |
goauth | Database name |
JWT_SECRET |
(insecure default) | JWT signing secret |
ACCESS_TOKEN_EXPIRY |
60 | Access token expiry (minutes) |
REFRESH_TOKEN_EXPIRY |
10080 | Refresh token expiry (minutes) |
DB_GO_ROUTINES |
false | Enable in-process token cleanup goroutine |
Expired refresh tokens are cleaned up automatically. Two options:
The k8s/db-cron.yaml runs a cleanup job every hour. This is the recommended approach for Kubernetes deployments with multiple replicas.
go run ./cmd/cleanupSet DB_GO_ROUTINES=true to enable a background goroutine that cleans up expired tokens every hour. Only use this for single-instance deployments.
| File | Description |
|---|---|
namespace.yaml |
Creates go-auth namespace |
configmap.yaml |
Non-sensitive configuration |
secret.yaml |
Sensitive config (update before deploy) |
deployment.yaml |
2 replicas with health probes |
service.yaml |
ClusterIP service on port 80 |
ingress.yaml |
Nginx ingress with TLS (update hostname) |
hpa.yaml |
Horizontal Pod Autoscaler (2-10 pods) |
pdb.yaml |
Pod Disruption Budget (min 1 available) |
db-cron.yaml |
Token cleanup CronJob (hourly) |
./deploy.sh apply # Deploy all manifests
./deploy.sh delete # Delete all resources
./deploy.sh status # Show deployment status
./deploy.sh logs # Tail application logs
./deploy.sh restart # Rolling restart-
Update
k8s/secret.yamlwith production values:DB_PASSWORDJWT_SECRET(use a long random string)
-
Update
k8s/ingress.yaml:- Set your domain in
host - Configure TLS secret name
- Set your domain in
-
Update
k8s/configmap.yaml:- Set correct
DB_HOSTfor your PostgreSQL instance
- Set correct
-
Update image name in
deployment.yamlanddb-cron.yaml
Backup the PostgreSQL database to a file using pg_dump:
# Default: creates backup_<timestamp>.sql
go run ./cmd/backup
# Specify output file
go run ./cmd/backup -o backup.sql
# Custom format (for pg_restore)
go run ./cmd/backup -F custom -o backup.dump
# Tar format
go run ./cmd/backup -F tar -o backup.tar| Format | Flag | Description |
|---|---|---|
plain |
-F plain |
SQL script (default) |
custom |
-F custom |
Compressed, use with pg_restore |
tar |
-F tar |
Tar archive, use with pg_restore |
directory |
-F directory |
Directory with multiple files |
# Plain SQL format
psql -h localhost -U postgres -d goauth < backup.sql
# Custom or tar format
pg_restore -h localhost -U postgres -d goauth backup.dumpNote: Requires pg_dump to be installed (comes with PostgreSQL client tools).
.
├── cmd/
│ ├── server/ # Main API server
│ ├── cleanup/ # Token cleanup CLI
│ └── backup/ # Database backup CLI
├── internal/
│ ├── auth/ # JWT and password utilities
│ ├── config/ # Configuration loading
│ ├── database/ # Database connection and repositories
│ ├── handlers/ # HTTP handlers
│ ├── middleware/ # Auth and scope middleware
│ └── models/ # Data models and scopes
├── k8s/ # Kubernetes manifests
├── deploy.sh # Deployment script
├── docker-compose.yml # Local PostgreSQL setup
└── README.md
# Build server
go build -o server ./cmd/server
# Build cleanup CLI
go build -o cleanup ./cmd/cleanup
# Build backup CLI
go build -o backup ./cmd/backupcurl http://localhost:8080/healthReturns {"status": "ok"} when the service is running.