package customer import ( "errors" "net/http" "strings" "tm/pkg/audit" "tm/pkg/response" "github.com/labstack/echo/v4" ) // AuthMiddleware validates JWT access tokens and extracts customer information func (h *Handler) AuthMiddleware() echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { // Extract token from Authorization header authHeader := c.Request().Header.Get("Authorization") if authHeader == "" { return response.Unauthorized(c, "Authorization header required") } // Check if it's a Bearer token if !strings.HasPrefix(authHeader, "Bearer ") { return response.Unauthorized(c, "Invalid authorization format. Use 'Bearer '") } // Extract the token tokenString := strings.TrimPrefix(authHeader, "Bearer ") // Validate the access token using authorization service validationResult, err := h.authService.ValidateAccessToken(tokenString) if err != nil { h.logger.Error("Token validation error", map[string]interface{}{ "error": err.Error(), }) return response.Unauthorized(c, "Invalid token") } if !validationResult.Valid { if validationResult.Expired { return response.Unauthorized(c, "Token expired") } return response.Unauthorized(c, validationResult.Error) } // Extract customer information from token customerID := validationResult.Claims.UserID // Store customer information in context for handlers to use c.Set("customer_id", customerID) c.Set("customer_email", validationResult.Claims.Email) c.Set("customer_role", validationResult.Claims.Role) c.Set("company_id", validationResult.Claims.CompanyID) c.Set("access_token", tokenString) audit.EnrichEchoContext(c, audit.ActorTypeCustomer) // Continue to next handler return next(c) } } } // CompanyContextMiddleware resolves company_id from current customer assignments. // JWT company_id can be stale after admin reassigns companies; this keeps tender // recommendations and other company-scoped APIs in sync with the database. func (h *Handler) CompanyContextMiddleware() echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { customerID, err := GetCustomerIDFromContext(c) if err != nil { return response.Unauthorized(c, "User not authenticated") } tokenCompanyID, _ := c.Get("company_id").(string) requestedCompanyID := strings.TrimSpace(c.QueryParam("company_id")) companyID, companyIDs, err := h.service.ResolveCompanyContext( c.Request().Context(), customerID, tokenCompanyID, requestedCompanyID, ) if err != nil { if errors.Is(err, errCompanyNotAssigned) { return response.Forbidden(c, "Company is not assigned to customer") } return response.Unauthorized(c, "User not authenticated") } c.Set("company_id", companyID) c.Set("company_ids", companyIDs) return next(c) } } } // GetCustomerIDFromContext extracts customer ID from Echo context func GetCustomerIDFromContext(c echo.Context) (string, error) { customerID, ok := c.Get("customer_id").(string) if !ok { return "", echo.NewHTTPError(http.StatusUnauthorized, "Customer ID not found in context") } return customerID, nil } // GetAccessTokenFromContext extracts access token from Echo context func GetAccessTokenFromContext(c echo.Context) (string, error) { accessToken, ok := c.Get("access_token").(string) if !ok { return "", echo.NewHTTPError(http.StatusUnauthorized, "Access token not found in context") } return accessToken, nil }