---
module: profile.security
title: Security
tab: Architect
order: 30
audience: dev
---

> **Internal · architecture.** Written 2026-07-01 from `hiveops-profile` frontend + `hiveops-auth` source. Verify against code before relying on specifics.

## Services involved

| Piece | Where | Role |
|-------|-------|------|
| `SecuritySettings.svelte` | `hiveops-profile/frontend` (`app: profile`) | Renders the two cards; calls `profileAPI` in `src/lib/api.ts` |
| `hiveops-auth` | Spring Boot, port **8082**, NGINX `/api/auth/` | **Owns** password + preferences. All reads/writes land here |

This feature is entirely **frontend → auth**. No other microservice participates. `mgmt` is only touched elsewhere in the profile app (company name / institution lookups), **not** by the Security screen. Cross-link [platform.service-ownership].

## Request wiring

The SPA builds `authApi` with `baseURL = window.__APP_CONFIG__.apiUrl` (the gateway) and attaches `Authorization: Bearer <localStorage token>` per request. Paths already include the `/auth/api` prefix:

| UI action | Frontend call (`api.ts`) | Controller |
|-----------|--------------------------|------------|
| Load timeout value | `GET /auth/api/users/me/preferences` | `UserProfileController.getPreferences` |
| Save timeout | `PUT /auth/api/users/me/preferences` | `UserProfileController.updatePreferences` |
| Change password | `POST /auth/api/change-password` | `AuthController.changePassword` |

## Data flow — Change Password

```
POST /auth/api/change-password  {newPassword, confirmPassword}
  → AuthController: reject 400 if newPassword != confirmPassword
  → AuthService.changePasswordAuthenticated(email, newPassword)
      → PasswordValidator.validatePassword()  // 12+ chars, upper/lower/digit/special
      → user.setPasswordHash(passwordEncoder.encode(newPassword))  // BCrypt
      → user.setMustChangePassword(false)
      → user.setPasswordChangedAt(Instant.now())
      → userRepository.save(user)             // table: users
      → auditService.logPasswordChanged(user) // table: auth_audit_logs (PASSWORD_CHANGED)
```

Identity comes from the JWT principal (`UserDetails.getUsername()` = email); no user id is passed in the body. No current-password re-check.

## Data flow — Session Timeout

```
PUT /auth/api/users/me/preferences  {sessionTimeoutMinutes}
  → UpdatePreferencesRequest  // @Min(5) @Max(480) sessionTimeoutMinutes
  → UserProfileService.updatePreferences(email, req)
      → load user.getPreferences() (or new UserPreferences() if null)
      → copy only non-null request fields onto the POJO
      → user.setPreferences(prefs); userRepository.save(user)
  → returns full UserPreferences
```

## DB tables

| Table (DB `hiveiq_auth`) | Read/Written | Notes |
|--------------------------|--------------|-------|
| `users` | R/W | `password_hash`, `must_change_password`, `password_changed_at`; **`preferences` is a single `jsonb` column** (`@Column(columnDefinition="jsonb")`) holding the serialized `UserPreferences` POJO — `sessionTimeoutMinutes` lives inside that JSON, **not** a dedicated column or table |
| `auth_audit_logs` | W | one `PASSWORD_CHANGED` row per successful change |

There is **no** separate preferences table and **no** join entity — `UserPreferences` is a plain POJO (not `@Entity`) marshaled into the `users.preferences` jsonb blob. Cross-link [platform.data-architecture].

## Kafka

**None.** `hiveops-auth` has no producers, listeners, `KafkaTemplate`, or `StreamBridge`. Password changes and preference updates are synchronous DB writes only — there is no executor→Kafka→record-store fan-out for this feature. Cross-link [platform.kafka].

## Enforcement boundary

- **Password rules** are enforced server-side in `PasswordValidator` (authoritative). The Svelte client's `length < 8` check is advisory and *weaker* than the server — a client-valid password can still 400 (see internal tab / uncertainties).
- **Session timeout** value is *stored* by auth but idle-logout enforcement is client-side in the consuming SPA. Auth's own JWT access-token TTL is fixed (24h) and independent of this preference.
