- System Overview
- Architecture Diagram
- Component Breakdown
- Data Flow
- Authentication Flow
- RBAC Flow
- Database Schema
- API Endpoints
- Security Considerations
Go-Auth is a lightweight authentication microservice designed for multi-backend architectures. It provides:
- JWT-based authentication with RS256 signing
- JWKS (JSON Web Key Set) endpoint for public key distribution
- Role-Based Access Control (RBAC) with flexible permission management
- Email verification and password reset workflows
- Audit logging for all RBAC operations
- Redis caching for sessions and JWKS keys
- PostgreSQL for persistent data storage
- Stateless Authentication: JWT tokens enable stateless authentication
- Public Key Distribution: JWKS allows multiple services to verify tokens independently
- Separation of Concerns: Modular architecture with clear boundaries
- Idempotent Operations: RBAC initialization can run multiple times safely
- Audit Trail: All RBAC changes are logged with actor information
┌─────────────────────────────────────────────────────────────────────┐
│ Go-Auth Service │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ HTTP Server (Gin) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Middleware │ │ Middleware │ │ Middleware │ │ │
│ │ │ - Logger │ │ - CORS │ │ - Auth │ │ │
│ │ │ - RequestID │ │ - Recovery │ │ - Permissions│ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────┐ │ │
│ │ │ Router Groups │ │ │
│ │ │ │ │ │
│ │ │ /api/v1/auth/* /api/v1/rbac/* │ │ │
│ │ │ ┌───────────────┐ ┌───────────────┐ │ │ │
│ │ │ │ Auth Module │ │ RBAC Module │ │ │ │
│ │ │ │ - signup │ │ - roles │ │ │ │
│ │ │ │ - signin │ │ - permissions │ │ │ │
│ │ │ │ - logout │ │ - user roles │ │ │ │
│ │ │ │ - verify │ │ - audit logs │ │ │ │
│ │ │ │ - reset pwd │ │ │ │ │ │
│ │ │ └───────────────┘ └───────────────┘ │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────┐ │ │
│ │ │ Service Layer │ │ │
│ │ │ - AuthService │ │ │
│ │ │ - RBACService │ │ │
│ │ │ - EmailService │ │ │
│ │ │ - BootstrapService │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Cobra CLI Commands │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ server │ │ init │ │ admin │ │ jobs │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ Start │ │ Bootstrap│ │ Create │ │ JWKS │ │ │
│ │ │ HTTP │ │ RBAC │ │ Superuser│ │ Refresh │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└───────────────────────────────┬───────────────────────────────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌────────▼────────┐ ┌───▼────────┐ ┌───▼────────┐
│ PostgreSQL │ │ Redis │ │ Mailhog │
│ │ │ │ │ (Dev) / │
│ - Users │ │ - Sessions │ │ SES (Prod)│
│ - Roles │ │ - JWKS │ │ │
│ - Permissions │ │ - Cache │ │ - Verify │
│ - UserRoles │ │ │ │ - Reset │
│ - RolePerms │ │ │ │ - Welcome │
│ - AuditLogs │ │ │ │ │
│ - EmailLogs │ │ │ │ │
└─────────────────┘ └────────────┘ └────────────┘
Location: cmd/server.go
- Handles HTTP requests and responses
- Applies global middleware (logging, CORS, recovery)
- Routes requests to appropriate module handlers
- Provides health check endpoints (
/health,/ready)
Middleware:
- Logger: Structured logging with request ID
- CORS: Cross-origin resource sharing configuration
- RequestID: Unique identifier for each request
- RequireAuth: JWT validation middleware
- RequirePermission: Permission-based access control
Controller (controller/auth_controller.go):
- Handles HTTP requests for authentication operations
- Validates input using Gin binding
- Delegates business logic to AuthService
Service (service/auth_service.go):
Signup(): Create new user account, assign default role, send verification emailSignin(): Authenticate user, create JWT token, store session in RedisLogout(): Invalidate session in RedisGetUserInfo(): Retrieve user profileUpdateProfile(): Update user informationForgotPassword(): Generate reset token, send emailResetPassword(): Validate token, update passwordVerifyEmail(): Confirm email addressResendVerification(): Regenerate verification token
Router (router.go):
- Registers routes under
/api/v1/auth - Applies authentication middleware where needed
Controller (controller/rbac_controller.go):
- Handles HTTP requests for RBAC operations
- Extracts actor ID from authenticated context
- Validates role and permission IDs
Service (service/rbac_service.go):
ListRoles(): Get all rolesGetRole(): Get role with permissionsListPermissions(): Get all permissionsGetUserRoles(): Get roles assigned to userAssignRole(): Assign role to user (with max_users validation)RemoveRole(): Remove role from userGetUserPermissions(): Compute effective permissions from all rolesUpdateRolePermissions(): Modify role permissions (system roles protected)GetAuditLogs(): Query audit logs with filterscreateAuditLog(): Internal helper to log RBAC changes
Bootstrap (bootstrap/bootstrap.go):
BootstrapPermissions(): Idempotent permission creation from YAMLBootstrapRoles(): Idempotent role creation with permission assignment- Supports wildcard permission matching (
*,users.*)
Router (router.go):
- Registers routes under
/api/v1/rbac - Public routes for listing roles/permissions
- Protected routes for management operations
Provider Interface (provider/provider.go):
type EmailProvider interface {
SendEmail(message *models.EmailMessage) error
}Implementations:
- MailhogProvider (
provider/mailhog.go): SMTP for local development - SESProvider (
provider/ses.go): AWS SES stub for production
Service (service/email_service.go):
SendVerificationEmail(): HTML/text email with verification linkSendPasswordResetEmail(): Reset link with tokenSendWelcomeEmail(): Onboarding messageGenerateVerificationToken(): Create email verification tokenGeneratePasswordResetToken(): Create password reset token- Logs all email delivery attempts to
email_logstable
Root Command (cmd/root.go):
- Entry point for all CLI operations
- Loads configuration
Server Command (cmd/server.go):
- Starts HTTP server on configured port
- Initializes database, Redis, JWKS keys
- Handles graceful shutdown on SIGTERM/SIGINT
Init Command (cmd/init.go):
- Bootstraps RBAC from
configs/rbac-config.yaml - Idempotent: can run multiple times safely
- Reports created/updated permissions and roles
Admin Command (cmd/admin.go):
create-superuser: Creates user with super-admin role- Validates super-admin role exists
- Checks max_users constraint
Jobs Command (cmd/jobs.go):
jwks-refresh: Rotates JWKS keys at specified interval- Generates new RSA key pair
- Updates Redis cache
Schemas (ent/schema/):
users.go: User accounts with email, password hash, name fieldsroles.go: Roles with code, system flag, max_users constraintpermissions.go: Permissions with code, resource, actionuser_roles.go: Join table for user-role relationshipsrole_permissions.go: Join table for role-permission relationshipsaudit_logs.go: RBAC change trackingemail_logs.go: Email delivery trackingemail_verifications.go: Email verification tokenspassword_resets.go: Password reset tokens
Auto-migration: storage.AutoMigrate() runs on server start
Database (internal/storage/postgres.go):
- Connection pooling
- Auto-migration
- Ent client initialization
Redis (internal/storage/redis.go):
- Session storage
- JWKS caching
- Rate limiting (future)
JWT (internal/auth/jwt.go):
GenerateToken(): Creates RS256-signed JWT with user claimsInitializeKeys(): Generates RSA key pair, caches in RedisGetPublicKeyFromCache(): Retrieves public key for verification
Password Hashing (internal/auth/passwords.go):
HashPassword(): bcrypt hashing with cost 10ComparePasswords(): Constant-time comparison
1. Client → POST /api/v1/auth/signup
{email, password, first_name, last_name}
2. AuthController → AuthService.Signup()
3. AuthService:
- Hash password with bcrypt
- Create user in database (ent)
- Assign default role (from roles.is_default = true)
- Generate verification token
- Create email_verifications record
4. EmailService.SendVerificationEmail()
- Generate HTML/text templates
- Send via MailhogProvider/SESProvider
- Log to email_logs table
5. Response → Client
{status: "success", message: "Check email", data: {user_id, email}}
1. Client → POST /api/v1/auth/signin
{email, password}
2. AuthController → AuthService.Signin()
3. AuthService:
- Find user by email
- Compare password hash (bcrypt)
- Check is_active and is_verified
- Generate JWT token (RS256)
- Store session in Redis (key: "session:{user_id}", value: token, TTL: 24h)
- Update last_login timestamp
4. Response → Client
{status: "success", data: {token, user: {...}}}
1. Client → GET /api/v1/auth/me
Headers: Authorization: Bearer <token>
2. Middleware.RequireAuth():
- Extract token from Authorization header
- Parse JWT and get kid (key ID)
- Fetch public key from Redis (GetPublicKeyFromCache)
- Verify signature
- Validate expiration
- Check session exists in Redis
- Set user_id in Gin context
3. AuthController.GetUserInfo():
- Get user_id from context
- Query database for user
- Return user info
4. Response → Client
{status: "success", data: {user_id, email, first_name, last_name, ...}}
1. Admin Client → POST /api/v1/rbac/users/assign-role
Headers: Authorization: Bearer <admin_token>
Body: {user_id, role_id}
2. Middleware.RequireAuth():
- Validate admin's JWT
- Set admin_id (actor_id) in context
3. RBACController.AssignRole():
- Extract actor_id from context
- Validate request body
- Call RBACService.AssignRole(user_id, role_id, actor_id)
4. RBACService.AssignRole():
- Check user exists
- Check role exists
- Check max_users constraint
- Check if already assigned
- Create user_roles record
- Create audit_logs record (actor_id, action: "role.assign", metadata)
5. Response → Admin Client
{status: "success", message: "Role assigned successfully"}
┌──────────┐
│ Client │
└────┬─────┘
│
│ 1. POST /api/v1/auth/signin
│ {email, password}
▼
┌─────────────────┐
│ AuthController │
└────┬────────────┘
│
│ 2. Validate input
▼
┌─────────────────┐
│ AuthService │
│ │
│ 3. Find user │────────┐
│ 4. Verify pwd │ │
│ 5. Gen JWT │ │
│ 6. Store sess │◄───┐ │
└────┬────────────┘ │ │
│ │ │
│ │ │
▼ │ │
┌──────────┐ ┌─────▼───▼──┐
│ Client │◄────│ Response │
│ │ │ {token} │
└────┬─────┘ └────────────┘
│
│ 7. Store token
│
│ 8. GET /api/v1/auth/me
│ Headers: Bearer <token>
▼
┌──────────────────┐
│ RequireAuth │
│ Middleware │
│ │
│ 9. Extract JWT │
│ 10. Get pub key │───────┐
│ 11. Verify sig │ │
│ 12. Check Redis │◄───┐ │
└────┬─────────────┘ │ │
│ ┌───▼──▼───┐
│ │ Redis │
▼ └──────────┘
┌──────────────────┐
│ AuthController │
│ GetUserInfo() │
└────┬─────────────┘
│
│ 13. Get user_id from context
│ 14. Query database
▼
┌──────────────────┐
│ PostgreSQL │
└────┬─────────────┘
│
│ 15. User data
▼
┌──────────┐
│ Client │
└──────────┘
User → UserRoles → Roles → RolePermissions → Permissions
Example:
User ID: 123
↓
UserRoles:
- role_id: 1 (admin)
- role_id: 3 (editor)
↓
RolePermissions (role_id = 1):
- permission_id: 10 (users.read)
- permission_id: 11 (users.write)
↓
RolePermissions (role_id = 3):
- permission_id: 20 (content.read)
- permission_id: 21 (content.write)
↓
Computed Permissions (deduplicated):
[users.read, users.write, content.read, content.write]
Every RBAC operation creates an audit log:
{
"id": "uuid",
"actor_id": "admin_user_id",
"action_type": "role.assign",
"resource_type": "user_role",
"resource_id": "target_user_id",
"metadata": {
"user_id": "target_user_id",
"role_id": 2
},
"ip_address": "192.168.1.1",
"user_agent": "curl/7.68.0",
"created_at": "2025-10-19T10:30:00Z"
}users
id(UUID, PK)email(string, unique)password_hash(string)first_name(string)last_name(string)is_active(bool, default: true)is_verified(bool, default: false)last_login(timestamp)created_at(timestamp)updated_at(timestamp)
roles
id(int, PK)code(string, unique)name(string)description(string, optional)is_system(bool, default: false)is_default(bool, default: false)max_users(int, optional)created_at(timestamp)updated_at(timestamp)
permissions
id(int, PK)code(string, unique)name(string)description(string, optional)resource(string, optional)action(string, optional)created_at(timestamp)updated_at(timestamp)
user_roles
id(int, PK)user_id(UUID, FK → users)role_id(int, FK → roles)assigned_at(timestamp)assigned_by(UUID, optional FK → users)- UNIQUE(user_id, role_id)
role_permissions
id(int, PK)role_id(int, FK → roles)permission_id(int, FK → permissions)- UNIQUE(role_id, permission_id)
audit_logs
id(UUID, PK)actor_id(UUID, optional FK → users)action_type(string)resource_type(string)resource_id(string, optional)metadata(JSON)changes(JSON, optional)ip_address(string, optional)user_agent(string, optional)created_at(timestamp)
email_logs
id(UUID, PK)user_id(UUID, FK → users)email_type(string)recipient(string)subject(string)status(string: sent, failed)error_message(string, optional)sent_at(timestamp)
email_verifications
id(UUID, PK)user_id(UUID, FK → users)token(string, unique)expires_at(timestamp)verified_at(timestamp, optional)created_at(timestamp)
password_resets
id(UUID, PK)user_id(UUID, FK → users)token(string, unique)expires_at(timestamp)used_at(timestamp, optional)created_at(timestamp)
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /signup |
No | Create new account |
| POST | /signin |
No | Authenticate user |
| POST | /logout |
Yes | Invalidate session |
| GET | /me |
Yes | Get user info |
| PUT | /me |
Yes | Update profile |
| POST | /forgot-password |
No | Request password reset |
| POST | /reset-password |
No | Complete password reset |
| GET | /verify-email |
No | Verify email address |
| POST | /resend-verification |
No | Resend verification email |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /roles |
No | List all roles |
| GET | /roles/:id |
No | Get role with permissions |
| GET | /permissions |
No | List all permissions |
| GET | /users/:user_id/roles |
Yes | Get user's roles |
| GET | /users/:user_id/permissions |
Yes | Get computed permissions |
| POST | /users/assign-role |
Yes | Assign role to user |
| POST | /users/remove-role |
Yes | Remove role from user |
| PUT | /roles/:id/permissions |
Yes | Update role permissions |
| GET | /audit-logs |
Yes | Query audit logs |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /.well-known/jwks.json |
No | JWKS public keys |
| GET | /health |
No | Basic health check |
| GET | /ready |
No | Readiness check |
- RS256 Algorithm: Asymmetric signing prevents token forgery
- JWKS Rotation: Keys should be rotated periodically (24h interval)
- Short Expiration: Tokens expire after 24 hours
- Session Invalidation: Logout removes session from Redis
- bcrypt Hashing: Cost factor 10, salt included
- No Plain Text: Passwords never stored or logged
- Reset Tokens: Single-use, time-limited (1 hour)
- Required for Sensitive Operations: Check
is_verifiedbefore password changes - Token Expiration: Verification tokens expire after 24 hours
- One-Time Use: Tokens invalidated after verification
- System Roles: Cannot be modified or deleted via API
- Audit Logging: All changes tracked with actor information
- Max Users Constraint: Prevents unlimited role assignments
- Permission Checks: Middleware validates permissions on protected routes
- Gin Binding: Request body validation with struct tags
- Email Format: Validated before user creation
- UUID Parsing: Prevents invalid ID attacks
- SQL Injection: Ent ORM provides parameterized queries
- Redis-based rate limiting per IP/user
- Prevents brute force attacks
- Configurable limits per endpoint
- Configurable allowed origins
- Credentials support for cookies
- Preflight request handling
- Go 1.24+: Required for building the binary
- PostgreSQL 13+: Primary database
- Redis 6+: Caching and session storage
- SMTP Server: Mailhog (dev) or AWS SES (prod)
- Environment Variables: See
.env.sample
# Database
DB_URL=localhost
DB_PORT=5432
DB_USER=admin
DB_PASS=admin
DB_NAME=auth
# Redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
# JWT
SECRET_KEY_ID=your-key-id
SECRET_PRIVATE_KEY=your-private-key
# API
API_PORT=42069
# Email (Production)
EMAIL_PROVIDER=ses
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...- Start PostgreSQL and Redis
- Run
go-auth init --config ./configs/rbac-config.yaml - Run
go-auth admin create-superuser(once) - Start JWKS refresh job:
go-auth jobs jwks-refresh --interval 24h(background) - Start API server:
go-auth server --port 42069
- Liveness:
GET /health(always returns 200) - Readiness:
GET /ready(checks database connectivity)
- Structured JSON logging to stdout
- Request ID tracking in all logs
- Audit logs for compliance
- Email delivery logs for debugging
Go-Auth provides a complete authentication and authorization solution with:
- Modular Design: Clear separation between auth, RBAC, and email
- Scalability: Stateless JWT tokens, Redis caching
- Security: RS256 signing, bcrypt hashing, audit logging
- Flexibility: YAML-based RBAC configuration, wildcard permissions
- Developer Experience: Cobra CLI, comprehensive documentation, Docker support
For detailed API examples and usage, see the main README.md.