RBAC at System Level ( Architectural Diagram )

Client
  |
  |  Authorization: Bearer <JWT (user_id, role)>
  v
Gin Engine
  |
  |-- RateLimitMiddleware
  |
  |-- AuthMiddleware
  |     └── Extracts user_id + role from JWT
  |     └── Stores in context
  |
  |-- RequireRole Middleware
  |     └── Checks if role ∈ allowed roles
  |
  |-- Handler
        |
        |-- Service
              |
              |-- Repository
                    |
                    |-- Database

Folder Structure

api-go
	|
	|-------> cmd/server
	|             |------> main.go
	|
	|
	|-------> internal 
	|             |
	|             |------> app
	|             |         |-----> app.go
	|             |    
	|             |------> auth
	|             |         |-----> jwt.go 
	|             |
	|             |
	|							|------> db
	|							|         |-----> postgres.go
	|							|
	|							|------> domain 
	|             |         |-----> errors.go
	|             |         |
	|             |         |-----> user.go
	|             |         |
	|							|         |-----> website.go
	|							|
	|							|------> dto
	|							|					|-----> websites.go
	|             |         |
	|             |         |-----> auth.go      
	|             |
	|							|
	|							|------> handlers
	|							|					|-----> websites.go
	|             |         |
	|             |         |-----> users.go     
	|             |
	|             |
	|             |------> http
	|             |         |-----> apperror
	|							|         |          |-----------> mapper.go
	|             |         |
	|             |         |-----> context
	|             |         |          |-----------> context.go
	|             |         |
	|             |         |-----> middleware
	|             |         |          |-----------> auth.go
	|             |         |          |
	|             |         |          |-----------> rateLimit.go
	|             |         |          |
	|             |         |          |-----------> rbac.go // added
	|             |         |
	|             |         |     
	|             |         |
	|							|         |-----> response
	|							|                    |-----------> response.go 		
	|             |
	|							|
	|							|------> repository
	|							|         |-----> website_repository_pg.go
	|							|         |-----> website_repository.go
	|             |         |-----> user_repository.go     
	|             |         |-----> user_repository_pg.go  
	|							|
	|							|
	|							|------> routes
	|							|					|-----> websites.go
	|             |         |
	|             |         |-----> health.go
	|             |         |
	|             |         |-----> router.go
	|             |         |
	|             |         |-----> user.go   
	|             |         |
	|             |         |-----> admin.go    // added
	|             |            
	|             |
	|							|
	|							|------> service
	|							|					|-----> websites.go	
	|             |         |
	|             |         |-----> users.go 
	|             |         
	|             |
	|             |------> validation
	|                       |-----> password.go	
	|
	|------> migrations
	|							|------> 20251226095007_create_websites_table.down.sql		
	|							|------> 20251226095007_create_websites_table.up.sql	
	|
	|------> .env	

Authorisation

What is this user allowed to do ?

RBAC = Role → Permissions → Actions

User ──has──▶ Role ──allows──▶ Actions

STEP 0 — Decide RBAC Scope

USER   → can create websites, view own data
ADMIN  → can manage all websites, users

STEP 1 : Add Role to User ( Domain Level )

internal/domain/user.go


type Role string

const (
	RoleUser  Role = "USER"
	RoleAdmin Role = "ADMIN"
)

type User struct {
	ID        string    `db:"id"`
	Email     string    `db:"email"`
	Password  string    `db:"password"`
	CreatedAt time.Time `db:"created_at"`
	Role      Role      `db:"role"`
}

Step 2 : Persist Role in Database

2.1 Create new migration files

migrate create -ext sql -dir migrations alter_users_table 

2.2 Write migrations

migrations/20260102100925_alter_users_table.up.sql

ALTER TABLE users
ADD COLUMN role TEXT NOT NULL DEFAULT 'USER';

migrations/20260102100925_alter_users_table.down.sql