package auth import ( "encoding/json" "fmt" "log" "net/http" "strings" "time" "gitea.local/admin/hspguard/internal/repository" "gitea.local/admin/hspguard/internal/types" "gitea.local/admin/hspguard/internal/util" "gitea.local/admin/hspguard/internal/web" "github.com/google/uuid" ) func (h *AuthHandler) refreshToken(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") if authHeader == "" { web.Error(w, "unauthorized", http.StatusUnauthorized) return } parts := strings.Split(authHeader, "Bearer ") if len(parts) != 2 { web.Error(w, "invalid auth header format", http.StatusUnauthorized) return } tokenStr := parts[1] var userClaims types.UserClaims token, err := util.VerifyToken(tokenStr, h.cfg.Jwt.PublicKey, &userClaims) if err != nil || !token.Valid { http.Error(w, fmt.Sprintf("invalid token: %v", err), http.StatusUnauthorized) return } expire, err := userClaims.GetExpirationTime() if err != nil { web.Error(w, "failed to retrieve enough info from the token", http.StatusInternalServerError) return } if time.Now().After(expire.Time) { web.Error(w, "token is expired", http.StatusUnauthorized) return } userId, err := uuid.Parse(userClaims.Subject) if err != nil { web.Error(w, "failed to parsej user id from token", http.StatusInternalServerError) return } user, err := h.repo.FindUserId(r.Context(), userId) if err != nil { web.Error(w, "user with provided email does not exists", http.StatusBadRequest) return } access, refresh, err := h.signTokens(&user) if err != nil { web.Error(w, "failed to generate tokens", http.StatusInternalServerError) return } jti, err := uuid.Parse(userClaims.ID) if session, err := h.repo.GetUserSessionByRefreshJTI(r.Context(), &jti); err != nil { log.Printf("WARN: No existing user session found for user with '%s' email (jti: '%s'): %v\n", user.Email, userClaims.ID, err) userAgent := r.UserAgent() ipAddr := util.GetClientIP(r) deviceInfo := util.BuildDeviceInfo(userAgent, ipAddr) // Create User Session session, err := h.repo.CreateUserSession(r.Context(), repository.CreateUserSessionParams{ UserID: user.ID, SessionType: "user", ExpiresAt: &refresh.ExpiresAt, LastActive: nil, IpAddress: &ipAddr, UserAgent: &userAgent, AccessTokenID: &access.ID, RefreshTokenID: &refresh.ID, DeviceInfo: deviceInfo, }) if err != nil { log.Printf("ERR: Failed to create user session after logging in: %v\n", err) } log.Printf("INFO: User session created for '%s' with '%s' id\n", user.Email, session.ID.String()) } else { err := h.repo.UpdateSessionTokens(r.Context(), repository.UpdateSessionTokensParams{ ID: session.ID, AccessTokenID: &access.ID, RefreshTokenID: &refresh.ID, ExpiresAt: &refresh.ExpiresAt, }) if err != nil { log.Printf("ERR: Failed to update user session with '%s' id: %v\n", session.ID.String(), err) } } type Response struct { AccessToken string `json:"access"` RefreshToken string `json:"refresh"` } encoder := json.NewEncoder(w) w.Header().Set("Content-Type", "application/json") if err := encoder.Encode(Response{ AccessToken: access.Token, RefreshToken: refresh.Token, }); err != nil { web.Error(w, "failed to encode response", http.StatusInternalServerError) } }