Authentication & Authorization
ss-keel-core provides a Guard interface for authentication middleware, and a type-safe way to access the authenticated user inside handlers.
The Guard Interface
Section titled “The Guard Interface”type Guard interface { Middleware() fiber.Handler}A Guard wraps any authentication logic into a Fiber middleware. When authentication fails, the guard should call ctx.Next() or return an error.
Implementing a Guard
Section titled “Implementing a Guard”package auth
import ( "github.com/gofiber/fiber/v2" "github.com/slice-soft/ss-keel-core/core")
type JWTGuard struct { secret string}
func NewJWTGuard(secret string) *JWTGuard { return &JWTGuard{secret: secret}}
func (g *JWTGuard) Middleware() fiber.Handler { return func(c *fiber.Ctx) error { token := c.Get("Authorization") if token == "" { return core.Unauthorized("missing authorization header") }
user, err := parseJWT(token, g.secret) if err != nil { return core.Unauthorized("invalid token") }
// Store the user in context for downstream handlers ctx := &core.Ctx{Ctx: c} ctx.SetUser(user)
return c.Next() }}Applying a Guard
Section titled “Applying a Guard”Per-Route
Section titled “Per-Route”Attach a guard to individual routes with .Use(). The secret comes from your config.Config, not from os.Getenv directly:
// cfg is config.Config loaded in main.goguard := auth.NewJWTGuard(cfg.JWTSecret)
core.GET("/profile", profileHandler). Use(guard.Middleware()). WithSecured("bearerAuth")Per-Group
Section titled “Per-Group”Apply a guard to all routes in a group:
protected := app.Group("/api", guard.Middleware())protected.Use(&users.Module{})Global (all routes)
Section titled “Global (all routes)”You can apply middleware at the Fiber level via app.Fiber():
app.Fiber().Use(guard.Middleware())Accessing the Authenticated User
Section titled “Accessing the Authenticated User”Setting the User
Section titled “Setting the User”Inside your guard middleware, call SetUser on the Ctx:
ctx.SetUser(user) // user can be any typeReading the User
Section titled “Reading the User”Use the generic UserAs[T] helper inside your handler:
func (c *UserController) profile(ctx *core.Ctx) error { user, ok := core.UserAs[*User](ctx) if !ok { return core.Unauthorized("not authenticated") }
return ctx.OK(user)}UserAs[T] performs a type-safe extraction — it returns (T, bool), where ok is false if the user was not set or the type doesn’t match.
OpenAPI Security Schemes
Section titled “OpenAPI Security Schemes”Mark a route as secured and declare the scheme in your docs config:
core.DELETE("/users/:id", deleteHandler). WithSecured("bearerAuth")The security scheme is declared in DocsConfig (applied globally):
core.KConfig{ Docs: core.DocsConfig{ // Bearer auth is automatically recognized by "bearerAuth" name },}Full Example
Section titled “Full Example”type JWTGuard struct{ secret string }
func (g *JWTGuard) Middleware() fiber.Handler { return func(c *fiber.Ctx) error { raw := c.Get("Authorization") // "Bearer <token>" token := strings.TrimPrefix(raw, "Bearer ")
claims, err := verifyJWT(token, g.secret) if err != nil { return core.Unauthorized("invalid or expired token") }
ctx := &core.Ctx{Ctx: c} ctx.SetUser(&AuthUser{ID: claims.Subject, Role: claims.Role}) return c.Next() }}
// auth/module.gopackage auth
import ( "myapp/config" "github.com/slice-soft/ss-keel-core/core")
type Module struct { cfg config.Config guard *JWTGuard}
func New(cfg config.Config) *Module { return &Module{cfg: cfg}}
func (m *Module) Register(app *core.App) { // JWTSecret comes from config, not os.Getenv m.guard = NewJWTGuard(m.cfg.JWTSecret) app.RegisterController(NewAuthController())}
func (m *Module) Guard() *JWTGuard { return m.guard}
// main.gocfg := config.Load()
authModule := auth.New(cfg)app.Use(authModule)
protected := app.Group("/api/v1", authModule.Guard().Middleware())protected.Use(users.New(cfg))Role-Based Authorization
Section titled “Role-Based Authorization”Build authorization on top of UserAs:
func RequireRole(role string) fiber.Handler { return func(c *fiber.Ctx) error { ctx := &core.Ctx{Ctx: c} user, ok := core.UserAs[*AuthUser](ctx) if !ok || user.Role != role { return core.Forbidden("insufficient permissions") } return c.Next() }}
// Usagecore.DELETE("/users/:id", deleteHandler). Use(RequireRole("admin"))