SKIP TO CONTENT
HOME/LEARN/BACKEND
JWTRBACSECURITY

AUTHENTICATION & AUTHORIZATION

JWT-based auth, RBAC implementation, permission matrices, and security best practices.

JWT — Do It Right

Store access tokens in memory (not localStorage — XSS vulnerable). Use short expiry with refresh tokens in httpOnly cookies. Never store sensitive data in JWT payload.

go
// Go — JWT generation with short-lived access + refresh tokens
func (s *AuthService) GenerateTokens(user *User) (*TokenPair, error) {
    // Access token — 15 min, stored in memory on client
    accessClaims := jwt.MapClaims{
        "sub":  user.ID,
        "role": user.Role,
        "org":  user.OrgID,
        "exp":  time.Now().Add(15 * time.Minute).Unix(),
    }
    accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims)
    access, err := accessToken.SignedString(s.accessSecret)
    if err != nil { return nil, err }

    // Refresh token — 7 days, httpOnly cookie
    refreshClaims := jwt.MapClaims{
        "sub": user.ID,
        "exp": time.Now().Add(7 * 24 * time.Hour).Unix(),
    }
    refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims)
    refresh, err := refreshToken.SignedString(s.refreshSecret)
    if err != nil { return nil, err }

    return &TokenPair{Access: access, Refresh: refresh}, nil
}

RBAC Middleware

Roles map to permission sets. Check permissions in middleware, not in business logic — this lets you change access rules without touching code.

go
// Permission check middleware
func (a *Auth) HasPermission(permission string) fiber.Handler {
    return func(c *fiber.Ctx) error {
        userID := c.Locals("userID").(string)
        orgID := c.Locals("orgID").(string)

        // Fetch from cache first, then DB
        perms, err := a.permCache.Get(userID + ":" + orgID)
        if err != nil {
            perms, err = a.repo.GetUserPermissions(userID, orgID)
            if err != nil { return c.Status(500).JSON(errInternal) }
            a.permCache.Set(userID+":"+orgID, perms, 5*time.Minute)
        }

        if !perms.Has(permission) {
            return c.Status(403).JSON(fiber.Map{
                "error": "insufficient permissions",
                "required": permission,
            })
        }
        return c.Next()
    }
}

// Usage in routes
tickets.Post("/", auth.HasPermission("tickets:create"), h.CreateTicket)
tickets.Delete("/:id", auth.HasPermission("tickets:delete"), h.DeleteTicket)