You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
3.4 KiB
Go
146 lines
3.4 KiB
Go
package auth
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
)
|
|
|
|
// UserContext holds user information extracted from JWT
|
|
type UserContext struct {
|
|
UserID string `json:"user_id"`
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
Roles []string `json:"roles"`
|
|
}
|
|
|
|
// GetUserFromContext extracts user information from fiber context
|
|
func GetUserFromContext(c *fiber.Ctx) (*UserContext, error) {
|
|
userID := c.Locals(UserIDKey)
|
|
if userID == nil {
|
|
return nil, errors.New(ErrUserNotAuthenticated)
|
|
}
|
|
|
|
username, _ := c.Locals(UsernameKey).(string)
|
|
email, _ := c.Locals(EmailKey).(string)
|
|
roles, _ := c.Locals(RolesKey).([]string)
|
|
|
|
return &UserContext{
|
|
UserID: userID.(string),
|
|
Username: username,
|
|
Email: email,
|
|
Roles: roles,
|
|
}, nil
|
|
}
|
|
|
|
// IsAuthenticated checks if user is authenticated
|
|
func IsAuthenticated(c *fiber.Ctx) bool {
|
|
return c.Locals(UserIDKey) != nil
|
|
}
|
|
|
|
// HasRole checks if user has a specific role
|
|
func HasRole(c *fiber.Ctx, role string) bool {
|
|
return hasRole(c, role)
|
|
}
|
|
|
|
// hasRole is the internal implementation for role checking
|
|
func hasRole(c *fiber.Ctx, role string) bool {
|
|
roles, ok := c.Locals(RolesKey).([]string)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
for _, r := range roles {
|
|
if r == role {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// hasAnyRole is the internal implementation for checking multiple roles
|
|
func HasAnyRole(c *fiber.Ctx, roles ...string) bool {
|
|
userRoles, ok := c.Locals(RolesKey).([]string)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
for _, userRole := range userRoles {
|
|
for _, role := range roles {
|
|
if userRole == role {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsAdmin checks if user has admin role
|
|
func IsAdmin(c *fiber.Ctx) bool {
|
|
return HasRole(c, AdminRole)
|
|
}
|
|
|
|
// Token processing helper functions
|
|
|
|
// extractToken extracts Bearer token from Authorization header
|
|
func extractToken(c *fiber.Ctx) (string, error) {
|
|
authHeader := c.Get(AuthorizationHeader)
|
|
if authHeader == "" {
|
|
return "", errors.New(ErrAuthHeaderRequired)
|
|
}
|
|
|
|
tokenString := strings.TrimPrefix(authHeader, BearerPrefix)
|
|
if tokenString == authHeader {
|
|
return "", errors.New(ErrInvalidAuthHeaderFormat)
|
|
}
|
|
|
|
return tokenString, nil
|
|
}
|
|
|
|
// setUserContext sets user information in fiber context from claims
|
|
func setUserContext(c *fiber.Ctx, claims *Claims) {
|
|
c.Locals(UserIDKey, claims.UserID)
|
|
c.Locals(UsernameKey, claims.Username)
|
|
c.Locals(EmailKey, claims.Email)
|
|
c.Locals(RolesKey, claims.Roles)
|
|
}
|
|
|
|
// validateAndSetContext validates token and sets user context
|
|
func validateAndSetContext(c *fiber.Ctx, authService *MockAuthService, tokenString string) error {
|
|
claims, err := authService.ValidateToken(c.Context(), tokenString)
|
|
if err != nil {
|
|
return fmt.Errorf("%s: %w", ErrInvalidToken, err)
|
|
}
|
|
|
|
setUserContext(c, claims)
|
|
return nil
|
|
}
|
|
|
|
// Error response helper functions
|
|
|
|
// unauthorizedError creates a standardized unauthorized error response
|
|
func unauthorizedError(c *fiber.Ctx, message string, details ...string) error {
|
|
response := fiber.Map{
|
|
"error": message,
|
|
}
|
|
if len(details) > 0 {
|
|
response["details"] = details[0]
|
|
}
|
|
return c.Status(StatusUnauthorized).JSON(response)
|
|
}
|
|
|
|
// forbiddenError creates a standardized forbidden error response
|
|
func forbiddenError(c *fiber.Ctx, message string, extraFields ...fiber.Map) error {
|
|
response := fiber.Map{
|
|
"error": message,
|
|
}
|
|
for _, extra := range extraFields {
|
|
for k, v := range extra {
|
|
response[k] = v
|
|
}
|
|
}
|
|
return c.Status(StatusForbidden).JSON(response)
|
|
}
|