891 lines
31 KiB
Markdown
891 lines
31 KiB
Markdown
# 🔐 Teljes Regisztrációs Folyamat Audit — 2026-06-07
|
|
|
|
## 📋 Összefoglaló
|
|
|
|
A Service Finder regisztrációs rendszere **4 fázisból** áll, amelyek a Lite regisztrációtól a teljes KYC-n át a felhasználói fiók aktiválásáig vezetnek. A folyamat **több rétegű** (frontend → backend → adatbázis → e-mail), és **Shadow Identity** kezeléssel, **Device Fingerprinting**-gel, valamint **dinamikus konfigurációval** bővül.
|
|
|
|
---
|
|
|
|
## 🧭 1. Regisztrációs Folyamat Áttekintése (4 Fázis)
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
A[Lite Regisztráció<br/>1. Fázis] -->|Email + Jelszó| B[Email Verifikáció<br/>Magic Link<br/>2. Fázis]
|
|
B -->|Auto-login JWT| C[KYC 1. lépés<br/>Cím - Soft KYC<br/>3. Fázis]
|
|
C --> D[KYC 2. lépés<br/>Személyes adatok<br/>4. Fázis]
|
|
D --> E[Fiók Aktiválva<br/>Szervezet + Pénztárca]
|
|
```
|
|
|
|
---
|
|
|
|
## 📦 2. Fázis 1: Lite Regisztráció
|
|
|
|
### 2.1 Frontend: LoginModal.vue
|
|
|
|
- **Belépési pont:** A `LoginModal.vue` 3D kártya-forgató (CSS rotateY/rotateX) a `register` oldallapon.
|
|
- **Gyűjtött mezők:** `lastName`, `firstName`, `regEmail`, `regPassword` (min. 8 karakter)
|
|
- **FingerprintJS:** A böngésző eszköz-ujjlenyomatát a `FingerprintJS` komponens generálja (`deviceFingerprint`).
|
|
- **Regisztráció utáni állapot:** Az email mentésre kerül `localStorage`-ba (`pending_verification_email` kulccsal), és a felhasználó átirányításra kerül a `/verify` oldalra.
|
|
- **60 másodperces cooldown:** Az újraküldés gomb 60 másodpercig inaktív (időzítés a localStorage-ban).
|
|
|
|
### 2.2 API Végpont: POST /auth/register
|
|
|
|
**Fájl:** `backend/app/api/v1/endpoints/auth.py:21`
|
|
|
|
```python
|
|
@router.post("/register", status_code=status.HTTP_201_CREATED)
|
|
async def register(user_in: UserLiteRegister, db: AsyncSession = Depends(get_db)):
|
|
```
|
|
|
|
**Bemenő adatok (Pydantic UserLiteRegister):**
|
|
|
|
| Mező | Típus | Kötelező | Megjegyzés |
|
|
|------|-------|----------|------------|
|
|
| `email` | `EmailStr` | Igen | Unique constraint |
|
|
| `password` | `str` | Igen | Min 8 karakter (dinamikus) |
|
|
| `first_name` | `str` | Igen | |
|
|
| `last_name` | `str` | Igen | |
|
|
| `region_code` | `str` | Nem | Példa: "HU", "AT" |
|
|
| `lang` | `str` | Nem | Nyelvi preferencia |
|
|
| `timezone` | `str` | Nem | |
|
|
| `referred_by_code` | `str` | Nem | P2P referral kód |
|
|
|
|
**Válasz:** `{"status": "success", "user_id": int, "email": str}`
|
|
|
|
### 2.3 Backend Szolgáltatás: AuthService.register_lite()
|
|
|
|
**Fájl:** `backend/app/services/auth_service.py:62`
|
|
|
|
**Folyamat:**
|
|
|
|
1. **Dinamikus konfiguráció lekérése** a `system.system_parameters` táblából:
|
|
- `auth_min_password_length` → alapértelmezett `8`
|
|
- `auth_default_role` → alapértelmezett `"user"`
|
|
- `auth_registration_hours` → alapértelmezett `48`
|
|
|
|
2. **Jelszó komplexitás ellenőrzése** (`_validate_password_complexity`):
|
|
- Minimális hossz (dinamikus)
|
|
- Nagybetű, kisbetű, szám követelmény?
|
|
- Regionális beállítások (pl. speciális karakterek)
|
|
|
|
3. **Email egyediség ellenőrzése** — `UniqueViolation` kezelés Sentinel audit loggal
|
|
|
|
4. **Person rekord létrehozása** (`identity.persons`):
|
|
```python
|
|
person = Person(
|
|
first_name=user_in.first_name,
|
|
last_name=user_in.last_name,
|
|
is_ghost=False, # Valódi személy
|
|
)
|
|
```
|
|
|
|
5. **User rekord létrehozása** (`identity.users`):
|
|
```python
|
|
user = User(
|
|
email=user_in.email,
|
|
hashed_password=pwd_context.hash(password),
|
|
role=default_role,
|
|
person=person, # Kapcsolat
|
|
is_active=False, # Inaktív amíg nem verifikálta az emailt
|
|
preferred_language=user_in.lang,
|
|
region_code=user_in.region_code,
|
|
referral_code=generate_secure_slug(),
|
|
)
|
|
```
|
|
|
|
6. **VerificationToken létrehozása** (`identity.verification_tokens`):
|
|
```python
|
|
token = VerificationToken(
|
|
user_id=user.id,
|
|
token_type="registration", # vagy "email_verification"
|
|
expires_at=utcnow() + timedelta(hours=reg_token_hours), # 48h
|
|
)
|
|
```
|
|
|
|
7. **Token slug generálása** (`security.generate_secure_slug()`)
|
|
|
|
8. **Email küldés** (`EmailManager.send_email()`):
|
|
- `template_key`: `"registration_verification"`
|
|
- Címzett: `user.email`
|
|
- Változók: `verification_url`, `user_name`, `expiry_hours`
|
|
|
|
9. **Sentinel Audit log:** `EVENT.REGISTRATION_INITIATED`
|
|
|
|
### 2.4 Adatbázis Írások (Fázis 1)
|
|
|
|
| Tábla | Séma | Művelet | Státusz |
|
|
|-------|------|---------|---------|
|
|
| `persons` | `identity` | `INSERT` | `is_active=False`, `is_ghost=False` |
|
|
| `users` | `identity` | `INSERT` | `is_active=False`, `role='user'` |
|
|
| `verification_tokens` | `identity` | `INSERT` | `token_type='registration'`, `expires_at=now+48h` |
|
|
|
|
---
|
|
|
|
## 📧 3. Fázis 2: Email Verifikáció (Magic Link)
|
|
|
|
### 3.1 Frontend: VerifyEmailView.vue
|
|
|
|
**Három állapot:**
|
|
|
|
| Állapot | Leírás |
|
|
|---------|--------|
|
|
| `isVerifying` | Spinner + "Email verifikálása..." szöveg, amíg a backend válaszol |
|
|
| `verifySuccess` | Zöld pipa animáció + 3 másodperces visszaszámlálás, majd átirányítás `/complete-kyc` |
|
|
| `default` | Email cím megadása + újraküldés gomb (cooldown-nal) |
|
|
|
|
**Magic Link működése:**
|
|
- A link token-t tartalmaz URL paraméterként (`?token=...`)
|
|
- A `VerifyEmailView` a token-t kiolvassa a query paraméterből
|
|
- Meghívja az `authStore.verifyAccount(token)` metódust
|
|
- Automatikusan generál egy új `deviceFingerprint`-et a FingerprintJS-el
|
|
|
|
### 3.2 Auth Store Metódus: verifyAccount()
|
|
|
|
```typescript
|
|
async function verifyAccount(verificationToken: string, deviceFingerprint?: string) {
|
|
const response = await api.post('/auth/verify-email', {
|
|
token: verificationToken,
|
|
device_fingerprint: deviceFingerprint
|
|
});
|
|
// Ha a válasz JWT token-t tartalmaz -> auto-login
|
|
if (response.data.access_token) {
|
|
// Token-ek elmentése localStorage-be
|
|
token.value = response.data.access_token;
|
|
refreshToken.value = response.data.refresh_token;
|
|
// Felhasználói profil betöltése
|
|
await fetchUser();
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3.3 API Végpont: POST /auth/verify-email
|
|
|
|
**Fájl:** `backend/app/api/v1/endpoints/auth.py:142`
|
|
|
|
**Válasz:** `Token` séma (`access_token`, `refresh_token`, `token_type`, `is_active`)
|
|
|
|
### 3.4 Backend Szolgáltatás: AuthService.verify_email()
|
|
|
|
**Fájl:** `backend/app/services/auth_service.py:319`
|
|
|
|
1. **Token validálás:**
|
|
- UUID formátum ellenőrzése
|
|
- Létezik-e a token a `verification_tokens` táblában
|
|
- `is_used=False` — még nem használták fel
|
|
- `expires_at > now()` — nem járt le
|
|
|
|
2. **User aktiválás:**
|
|
```python
|
|
user.is_active = True
|
|
person.is_active = True
|
|
token.is_used = True
|
|
```
|
|
|
|
3. **Device Fingerprint rögzítés:**
|
|
```python
|
|
# Ha device_fingerprint érkezett:
|
|
device = await get_or_create_device(db, device_fingerprint)
|
|
await create_user_device_link(db, user.id, device.id)
|
|
```
|
|
|
|
4. **JWT Token generálás** (auto-login):
|
|
```python
|
|
access_token, refresh_token = create_tokens(
|
|
data={"sub": str(user.id), "role": user.role.value},
|
|
remember_me=False # Magic Link esetén nincs remember_me
|
|
)
|
|
```
|
|
|
|
5. **Sentinel Audit log:** `EVENT.EMAIL_VERIFIED`
|
|
|
|
### 3.5 Adatbázis Írások (Fázis 2)
|
|
|
|
| Tábla | Séma | Művelet | Változás |
|
|
|-------|------|---------|----------|
|
|
| `users` | `identity` | `UPDATE` | `is_active=False → True` |
|
|
| `persons` | `identity` | `UPDATE` | `is_active=False → True` |
|
|
| `verification_tokens` | `identity` | `UPDATE` | `is_used=False → True` |
|
|
| `devices` | `identity` | `INSERT/SELECT` | Ha új eszköz → létrehozás |
|
|
| `user_device_links` | `identity` | `INSERT` | User-Device kapcsolat |
|
|
|
|
---
|
|
|
|
## 🏠 4. Fázis 3: KYC 1. Lépés — Cím (Soft KYC)
|
|
|
|
### 4.1 Frontend: CompleteKycView.vue — 1. Lépés
|
|
|
|
**Gyűjtött mezők:**
|
|
|
|
| Mező | Típus | Kötelező |
|
|
|------|-------|----------|
|
|
| `country` | Select | Igen (HU/AT/SK/DE/RO/CZ) |
|
|
| `zip_code` | Input | Igen |
|
|
| `city` | Input | Igen (auto-fill zip lookup-ból) |
|
|
| `street_name` | Input | Nem |
|
|
| `street_type` | Select | Nem (utca, tér, stb.) |
|
|
| `street_number` | Input | Nem |
|
|
| `building` | Input | Nem |
|
|
| `staircase` | Input | Nem |
|
|
| `floor` | Input | Nem |
|
|
| `door` | Input | Nem |
|
|
|
|
**Speciális funkciók:**
|
|
- **Irányítószám keresés:** `/api/v1/system/zip-lookup` — automatikus város kitöltés
|
|
- **Skip gomb:** Csak irányítószám + város megadása kötelező, a többi opcionális
|
|
- **Cím adatbázis:** A `GeoService` a `data.zip_codes`, `data.streets`, `data.street_types`, `data.addresses` táblákat használja
|
|
|
|
### 4.2 API Végpont: POST /auth/complete-kyc
|
|
|
|
**Fájl:** `backend/app/api/v1/endpoints/auth.py:289`
|
|
|
|
```python
|
|
@router.post("/complete-kyc")
|
|
async def complete_kyc(
|
|
kyc_in: UserKYCComplete,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
```
|
|
|
|
### 4.3 Backend Szolgáltatás: AuthService.complete_kyc()
|
|
|
|
**Fájl:** `backend/app/services/auth_service.py:177`
|
|
|
|
**Bemenő adatok (Pydantic UserKYCComplete):**
|
|
|
|
| Mező | Típus | Kötelező |
|
|
|------|-------|----------|
|
|
| `phone` | `str` | Igen |
|
|
| `birth_place` | `str` | Igen |
|
|
| `birth_date` | `date` | Igen |
|
|
| `mothers_name` | `str` | Igen |
|
|
| `address` | nested | Igen (zip_code + city minimum) |
|
|
| `identity_docs` | `list[dict]` | Nem (ID card + driver's license) |
|
|
| `ice_contact` | `ICEContact` | Nem |
|
|
| `preferred_language` | `str` | Nem |
|
|
| `preferred_currency` | `str` | Nem |
|
|
| `device_fingerprint` | `str` | Nem |
|
|
|
|
**Alapértelmezett mezők a UserKYCComplete-ben:**
|
|
```python
|
|
phone: Optional[str] = None
|
|
birth_place: Optional[str] = None
|
|
birth_date: Optional[date] = None
|
|
mothers_name: Optional[str] = None
|
|
identity_docs: Optional[List[dict]] = None
|
|
ice_contact: Optional[ICEContact] = None
|
|
preferred_language: Optional[str] = None
|
|
preferred_currency: Optional[str] = None
|
|
```
|
|
|
|
---
|
|
|
|
## 🏢 5. Fázis 4: KYC 2. Lépés — Teljes Aktiválás
|
|
|
|
### 5.1 Frontend: CompleteKycView.vue — 2. Lépés
|
|
|
|
**Gyűjtött mezők:**
|
|
- `phone` — Telefonszám
|
|
- `identity_docs` — Dokumentumok:
|
|
- `id_card_number` + `id_card_expiry`
|
|
- `driver_license_number` + `driver_license_expiry`
|
|
- `preferred_language` / `preferred_currency` — Nyelv és pénznem preferencia
|
|
|
|
### 5.2 Auth Store Metódus: completeKyc()
|
|
|
|
```typescript
|
|
async function completeKyc(kycData: Record<string, any>): Promise<boolean> {
|
|
try {
|
|
await api.post('/auth/complete-kyc', kycData);
|
|
await fetchUser(); // Profil frissítése
|
|
return true;
|
|
} catch (err) {
|
|
// Hibakezelés
|
|
return false;
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5.3 Backend Szolgáltatás: AuthService.complete_kyc() — Teljes logika
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[KYC adatok érkeznek] --> B[Shadow Identity keresés]
|
|
B --> C{Van duplicate Person?}
|
|
C -->|Igen| D[Merge Person rekordok]
|
|
C -->|Nem| E[Folytatás jelenlegi Person-nal]
|
|
D --> E
|
|
E --> F[Cím létrehozása GeoService]
|
|
F --> G[Personal Organization létrehozása]
|
|
G --> H[Wallet létrehozása]
|
|
H --> I[Branch létrehozása]
|
|
I --> J[OrganizationMember létrehozása]
|
|
J --> K[Gamification XP award]
|
|
K --> L[P2P referral XP (ha van)]
|
|
L --> M[Sikeres válasz]
|
|
```
|
|
|
|
#### Részletes lépések:
|
|
|
|
1. **Shadow Identity keresés és merge:**
|
|
```python
|
|
# Ellenőrzi, hogy a person rekordhoz tartozik-e másik User
|
|
# Ha igen, összefésüli az adatokat (identity_hash alapján)
|
|
existing_person = await check_shadow_person(db, kyc_in)
|
|
if existing_person and existing_person.id != current_user.person_id:
|
|
# Merge logika: a régebbi person adatai átkerülnek az újra
|
|
await merge_persons(db, source_id=existing_person.id, target_id=current_user.person_id)
|
|
```
|
|
|
|
2. **Cím létrehozása** (GeoService.get_or_create_full_address()):
|
|
```python
|
|
address = await GeoService.get_or_create_full_address(
|
|
db, kyc_in.address.dict(), user_id=current_user.id
|
|
)
|
|
current_user.person.address_id = address.id
|
|
```
|
|
|
|
3. **Personal Organization létrehozása:**
|
|
```python
|
|
org = Organization(
|
|
name=f"{person.first_name} {person.last_name} Személyes",
|
|
org_type="personal",
|
|
owner_id=current_user.id,
|
|
is_verified=True,
|
|
)
|
|
```
|
|
|
|
4. **Wallet létrehozása** (`finance.wallets`):
|
|
```python
|
|
wallet = Wallet(
|
|
user_id=current_user.id,
|
|
earned_credits=0,
|
|
purchased_credits=0,
|
|
service_coins=0,
|
|
currency=kyc_in.preferred_currency or "HUF",
|
|
)
|
|
```
|
|
|
|
5. **Branch létrehozása** (`data.branches`):
|
|
```python
|
|
branch = Branch(
|
|
organization_id=org.id,
|
|
address_id=address.id,
|
|
name="Központi telephely",
|
|
branch_type="headquarters",
|
|
)
|
|
```
|
|
|
|
6. **OrganizationMember létrehozása** (`data.organization_members`):
|
|
```python
|
|
member = OrganizationMember(
|
|
organization_id=org.id,
|
|
user_id=current_user.id,
|
|
role="owner",
|
|
)
|
|
```
|
|
|
|
7. **Gamification XP award:**
|
|
- `ProfileCompleted` esemény → +50 XP
|
|
- `EmailVerified` + `ProfileCompleted` kombó → +100 XP (first_login bonus)
|
|
|
|
8. **P2P referral XP:**
|
|
- Ha `referred_by_id` be van állítva a User-en:
|
|
- A referrer +100 XP-t kap (`ReferralUsed` esemény)
|
|
|
|
9. **Sentinel Audit log:** `EVENT.KYC_COMPLETED`
|
|
|
|
### 5.4 Adatbázis Írások (Fázis 4)
|
|
|
|
| Tábla | Séma | Művelet |
|
|
|-------|------|---------|
|
|
| `persons` | `identity` | `UPDATE` (phone, birth_data, address_id, identity_docs, ice_contact) |
|
|
| `addresses` | `data` | `INSERT` |
|
|
| `zip_codes` | `data` | `INSERT/SELECT` (auto-learning) |
|
|
| `streets` | `data` | `INSERT/SELECT` (auto-learning) |
|
|
| `organizations` | `data` | `INSERT` (personal org) |
|
|
| `wallets` | `finance` | `INSERT` |
|
|
| `branches` | `data` | `INSERT` (headquarters) |
|
|
| `organization_members` | `data` | `INSERT` (owner role) |
|
|
| `gamification_events` | `gamification` | `INSERT` (XP award) |
|
|
|
|
---
|
|
|
|
## 🔑 6. Token Élettartamok és Kezelés
|
|
|
|
### 6.1 JWT Token-ek
|
|
|
|
**Forrás:** `backend/app/core/security.py:17`
|
|
|
|
```python
|
|
def create_tokens(data: Dict[str, Any], remember_me: bool = False) -> Tuple[str, str]:
|
|
# Access Token
|
|
access_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) # 30 perc
|
|
access_token = create_access_token(data=data, expires_delta=access_expires)
|
|
|
|
# Refresh Token
|
|
refresh_days = settings.REFRESH_TOKEN_EXPIRE_DAYS # 7 nap (alap)
|
|
if remember_me:
|
|
refresh_days = 30 # 30 nap ha "Emlékezz rám"
|
|
refresh_expires = timedelta(days=refresh_days)
|
|
refresh_token = create_refresh_token(data=data, expires_delta=refresh_expires)
|
|
|
|
return access_token, refresh_token
|
|
```
|
|
|
|
| Token | Élettartam | Beállítási Forrás |
|
|
|-------|-----------|-------------------|
|
|
| **Access Token** | **30 perc** | `settings.ACCESS_TOKEN_EXPIRE_MINUTES` (static) |
|
|
| **Refresh Token** (alap) | **1 nap** | `settings.REFRESH_TOKEN_EXPIRE_DAYS=7` → runtime override: 1 nap |
|
|
| **Refresh Token** (remember_me) | **30 nap** | Runtime override `remember_me=True` |
|
|
| **Verification Token** (regisztráció) | **48 óra** | `system_parameters.auth_registration_hours` (dinamikus) |
|
|
| **Verification Token** (jelszó reset) | **2 óra** | `system_parameters.auth_password_reset_hours` (dinamikus) |
|
|
|
|
### 6.2 Token Használati Minta
|
|
|
|
```
|
|
Regisztráció → Nincs token (csak user_id + email vissza)
|
|
Email verifikáció → Access (30p) + Refresh (1d/30d) → Auto-login
|
|
Bejelentkezés → Access (30p) + Refresh (1d/30d)
|
|
Jelszó reset → Nincs token (csak sikeres üzenet)
|
|
```
|
|
|
|
### 6.3 Dekódolás és Validáció
|
|
|
|
**Forrás:** `backend/app/core/security.py:43`
|
|
|
|
```python
|
|
def decode_token(token: str) -> Optional[Dict[str, Any]]:
|
|
try:
|
|
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[ALGORITHM])
|
|
return payload
|
|
except JWTError:
|
|
return None
|
|
```
|
|
|
|
### 6.4 Dependency Injection: get_current_user()
|
|
|
|
**Fájl:** `backend/app/api/deps.py:55`
|
|
|
|
```python
|
|
async def get_current_user(
|
|
token: str = Depends(oauth2_scheme), # Authorization: Bearer <token>
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> User:
|
|
payload = decode_token(token)
|
|
if payload is None:
|
|
raise HTTPException(status_code=401, detail="INVALID_TOKEN")
|
|
user = await db.get(User, int(payload.get("sub")))
|
|
if user is None:
|
|
raise HTTPException(status_code=401, detail="USER_NOT_FOUND")
|
|
return user
|
|
```
|
|
|
|
---
|
|
|
|
## 🖥️ 7. Frontend Felületek Részletes Leírása
|
|
|
|
### 7.1 LoginModal.vue — 3D Kártya-forgató
|
|
|
|
**Technológia:** CSS 3D transzformációk (`rotateY`, `rotateX`)
|
|
**Négy oldal:**
|
|
|
|
| Oldal | CSS Osztály | Trigger |
|
|
|-------|-------------|---------|
|
|
| **Login** | `.card-face-front` | Alapértelmezett |
|
|
| **Register** | `.card-face-back` | "Regisztráció" gomb |
|
|
| **Forgot Password** | `.card-face-left` | "Elfelejtett jelszó" link |
|
|
| **Resend Verification** | `.card-face-top` | "Újraküldés" link |
|
|
|
|
**Login folyamat:**
|
|
1. `email` + `password` → `authStore.login()`
|
|
2. Ha `isKycComplete` = false → `/complete-kyc` redirect
|
|
3. Ha `isKycComplete` = true → `/dashboard` redirect
|
|
|
|
**Remember Me:** localStorage-ban tárolva (`login_remember`)
|
|
|
|
### 7.2 VerifyEmailView.vue — Verifikációs Oldal
|
|
|
|
**URL:** `/verify?token=...`
|
|
|
|
**3 állapot:**
|
|
|
|
1. **Verifying** (`isVerifying = true`):
|
|
- Spinner komponens
|
|
- "Email cím ellenőrzése..." szöveg
|
|
- Token kiolvasása a URL-ből
|
|
|
|
2. **Success** (`verifySuccess = true`):
|
|
- Zöld pipa animáció (CSS)
|
|
- "Email cím sikeresen megerősítve!"
|
|
- 3 másodperces countdown → `/complete-kyc`
|
|
|
|
3. **Default** (nincs token a URL-ben):
|
|
- Email input mező
|
|
- "Újraküldés" gomb
|
|
- 60 másodperes cooldown
|
|
|
|
### 7.3 CompleteKycView.vue — 2-lépéses KYC
|
|
|
|
**Védett route:** Csak hitelesített felhasználók számára (`requiresAuth` guard).
|
|
|
|
**Step 1 — Cím adatok:**
|
|
- Ország választó (HU/AT/SK/DE/RO/CZ flag-ekkel)
|
|
- Irányítószám + automatikus város kitöltés
|
|
- Utca, házszám, épület, lépcsőház, emelet, ajtó (opcionális)
|
|
- "Kihagyás" gomb (ha csak zip+city van megadva)
|
|
|
|
**Step 2 — Személyes adatok:**
|
|
- Telefonszám
|
|
- Születési hely, dátum
|
|
- Anyja neve
|
|
- Dokumentumok (személyi + jogosítvány)
|
|
- ICE kapcsolattartó
|
|
- Nyelv és pénznem preferencia
|
|
|
|
### 7.4 Router Guardok
|
|
|
|
**Fájl:** `frontend/src/router/index.ts:49`
|
|
|
|
```typescript
|
|
router.beforeEach(async (to, _from, next) => {
|
|
const authStore = useAuthStore();
|
|
await authStore.init(); // Token-ek betöltése localStorage-ból
|
|
|
|
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
|
|
next('/'); // Vissza a Landing-re
|
|
} else if (to.path === '/' && authStore.isAuthenticated) {
|
|
next('/dashboard'); // Már bejelentkezett → dashboard
|
|
} else {
|
|
next();
|
|
}
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 🗄️ 8. Adatbázis Séma Részletek
|
|
|
|
### 8.1 Person — `identity.persons`
|
|
|
|
| Mező | Típus | Default | Megjegyzés |
|
|
|------|-------|---------|------------|
|
|
| `id` | `BigInt` PK | auto | |
|
|
| `uuid` | `UUID` | `uuid4()` | Publikus azonosító |
|
|
| `address_id` | `Int FK` | `NULL` | → `data.addresses.id` |
|
|
| `first_name` | `String(100)` | | |
|
|
| `last_name` | `String(100)` | | |
|
|
| `middle_name` | `String(100)` | `NULL` | |
|
|
| `phone` | `String(20)` | `NULL` | |
|
|
| `birth_place` | `String(200)` | `NULL` | |
|
|
| `birth_date` | `Date` | `NULL` | |
|
|
| `mothers_name` | `String(200)` | `NULL` | |
|
|
| `identity_hash` | `String(64)` | `NULL` | SHA-256 hash a deduplikációhoz |
|
|
| `identity_docs` | `JSON` | `NULL` | Dokumentumok listája |
|
|
| `ice_contact` | `JSON` | `NULL` | ICE kapcsolattartó |
|
|
| `avatar_url` | `String(500)` | `NULL` | |
|
|
| `lifetime_xp` | `Integer` | `0` | Gamification |
|
|
| `penalty_points` | `Integer` | `0` | |
|
|
| `social_reputation` | `Float` | `0.0` | |
|
|
| `is_sales_agent` | `Boolean` | `False` | |
|
|
| `is_active` | `Boolean` | `False` | Aktiválás után `True` |
|
|
| `is_ghost` | `Boolean` | `False` | Ideiglenes rekord |
|
|
| `merged_into_id` | `Int FK` | `NULL` | Shadow Identity merge cél |
|
|
| `created_at` | `DateTime` | `utcnow()` | |
|
|
| `updated_at` | `DateTime` | `utcnow()` | |
|
|
|
|
**Relációk:**
|
|
- `users: List[User]` — Egy személyhez több User tartozhat
|
|
- `active_user_account: User` — Az aktív User
|
|
- `address: Address` — Lakcím
|
|
- `owned_business_entities: List[Organization]` — Tulajdonolt szervezetek
|
|
|
|
### 8.2 User — `identity.users`
|
|
|
|
| Mező | Típus | Default | Megjegyzés |
|
|
|------|-------|---------|------------|
|
|
| `id` | `Int` PK | auto | |
|
|
| `email` | `String(255)` | | **UNIQUE**, login azonosító |
|
|
| `hashed_password` | `String(255)` | | bcrypt hash |
|
|
| `role` | `Enum(UserRole)` | `user` | `user`, `admin`, `superadmin`, `manager`, `agent`, `api` |
|
|
| `person_id` | `BigInt FK` | `NULL` | → `identity.persons.id` |
|
|
| `subscription_plan` | `String(50)` | `NULL` | |
|
|
| `is_vip` | `Boolean` | `False` | |
|
|
| `referral_code` | `String(20)` | `NULL` | **UNIQUE**, P2P meghívó |
|
|
| `referred_by_id` | `Int FK` | `NULL` | → `identity.users.id` |
|
|
| `is_active` | `Boolean` | `False` | Verifikáció után `True` |
|
|
| `is_deleted` | `Boolean` | `False` | Soft delete (GDPR) |
|
|
| `folder_slug` | `String(64)` | `NULL` | |
|
|
| `preferred_language` | `String(10)` | `NULL` | |
|
|
| `region_code` | `String(10)` | `NULL` | |
|
|
| `preferred_currency` | `String(3)` | `NULL` | |
|
|
| `ui_mode` | `String(20)` | `NULL` | |
|
|
| `scope_level` | `Enum` | `NULL` | RBAC scope |
|
|
| `scope_id` | `Int` | `NULL` | |
|
|
| `custom_permissions` | `JSON` | `NULL` | |
|
|
| `alternative_emails` | `JSON` | `NULL` | |
|
|
| `email_history` | `JSON` | `NULL` | |
|
|
| `created_at` | `DateTime` | `utcnow()` | |
|
|
| `updated_at` | `DateTime` | `utcnow()` | |
|
|
|
|
**Relációk:**
|
|
- `person: Person` — A személy, akihez a fiók tartozik
|
|
- `referrer: User` — Aki meghívta
|
|
- `sales_agent: User` — Értékesítési ügynök
|
|
|
|
### 8.3 VerificationToken — `identity.verification_tokens`
|
|
|
|
| Mező | Típus | Default | Megjegyzés |
|
|
|------|-------|---------|------------|
|
|
| `token` | `UUID` PK | `uuid4()` | |
|
|
| `user_id` | `Int FK` | | → `identity.users.id` |
|
|
| `token_type` | `String(50)` | | `registration`, `password_reset`, `email_change` |
|
|
| `extra_data` | `JSON` | `NULL` | |
|
|
| `expires_at` | `DateTime` | | |
|
|
| `is_used` | `Boolean` | `False` | |
|
|
| `created_at` | `DateTime` | `utcnow()` | |
|
|
|
|
### 8.4 Wallet — `finance.wallets`
|
|
|
|
| Mező | Típus | Default | Megjegyzés |
|
|
|------|-------|---------|------------|
|
|
| `id` | `Int` PK | auto | |
|
|
| `user_id` | `Int FK` | | → `identity.users.id` |
|
|
| `earned_credits` | `Integer` | `0` | |
|
|
| `purchased_credits` | `Integer` | `0` | |
|
|
| `service_coins` | `Integer` | `0` | |
|
|
| `currency` | `String(3)` | `HUF` | |
|
|
|
|
### 8.5 Device — `identity.devices`
|
|
|
|
| Mező | Típus | Megjegyzés |
|
|
|------|-------|------------|
|
|
| `id` | `Int` PK | |
|
|
| `fingerprint_hash` | `String(255)` | FingerprintJS hash |
|
|
| `user_agent` | `Text` | |
|
|
| `ip_address` | `String(45)` | |
|
|
| `created_at` | `DateTime` | |
|
|
|
|
### 8.6 UserDeviceLink — `identity.user_device_links`
|
|
|
|
| Mező | Típus | Megjegyzés |
|
|
|------|-------|------------|
|
|
| `id` | `UUID` PK | |
|
|
| `user_id` | `Int FK` | → `identity.users.id` |
|
|
| `device_hash` | `String(255)` | |
|
|
| `first_seen_at` | `DateTime` | |
|
|
| `last_seen_at` | `DateTime` | |
|
|
|
|
---
|
|
|
|
## 🎭 9. Shadow Identity (Személy Összefésülés)
|
|
|
|
**Mechanizmus:** Amikor a KYC befejeződik, a rendszer ellenőrzi, hogy a megadott adatok (pl. `identity_hash` alapján) egyeznek-e egy másik `Person` rekorddal. Ha igen, a két rekord összefésülésre kerül:
|
|
|
|
```python
|
|
async def merge_persons(db: AsyncSession, source_id: int, target_id: int):
|
|
source = await db.get(Person, source_id)
|
|
target = await db.get(Person, target_id)
|
|
|
|
# Source User-ek átirányítása a Target Person-hoz
|
|
for user in source.users:
|
|
user.person_id = target.id
|
|
|
|
# Source megjelölése merge-eltként
|
|
source.merged_into_id = target.id
|
|
source.is_active = False
|
|
|
|
await db.commit()
|
|
```
|
|
|
|
**Trigger:** `complete_kyc()` függvényben, az adatok validálása után.
|
|
|
|
---
|
|
|
|
## ⚙️ 10. Dinamikus Konfiguráció
|
|
|
|
**Forrás:** `system.system_parameters` tábla → `Settings.get_db_setting()`
|
|
|
|
**Fájl:** `backend/app/core/config.py:140`
|
|
|
|
| Paraméter | Alapértelmezett | Hatás |
|
|
|-----------|----------------|-------|
|
|
| `auth_min_password_length` | `8` | Jelszó minimális hossza |
|
|
| `auth_max_password_length` | `128` | Jelszó maximális hossza |
|
|
| `auth_default_role` | `user` | Alapértelmezett szerepkör |
|
|
| `auth_registration_hours` | `48` | Verifikációs token élettartam (óra) |
|
|
| `auth_password_reset_hours` | `2` | Jelszó reset token élettartam (óra) |
|
|
| `auth_max_login_attempts` | `5` | Maximális bejelentkezési kísérlet |
|
|
|
|
---
|
|
|
|
## 📧 11. Email Rendszer
|
|
|
|
**Forrás:** `backend/app/services/email_manager.py:18`
|
|
|
|
| Szolgáltató | Prioritás | Módszer |
|
|
|-------------|-----------|---------|
|
|
| **Brevo API** | 1. | REST API kulcsos hitelesítés |
|
|
| **Brevo SMTP** | 2. | `smtp-relay.brevo.com:587` |
|
|
| **Fallback SMTP** | 3. | Általános SMTP (SSL/TLS) |
|
|
|
|
**Email sablonok:**
|
|
| Sablon kulcs | Használat |
|
|
|-------------|-----------|
|
|
| `registration_verification` | Regisztráció utáni verifikációs email |
|
|
| `password_reset` | Jelszó visszaállítás |
|
|
| `welcome` | Sikeres verifikáció utáni üdvözlő email |
|
|
|
|
**Sablonok:** A `locale_manager` segítségével i18n támogatással, nyelv-specifikus HTML template-ek.
|
|
|
|
---
|
|
|
|
## 🛡️ 12. Sentinel Audit Log Események
|
|
|
|
| Esemény | Fázis | Naplózott adatok |
|
|
|---------|-------|------------------|
|
|
| `EVENT.REGISTRATION_INITIATED` | 1 | email, ip, user_agent |
|
|
| `EVENT.EMAIL_VERIFIED` | 2 | user_id, token_id |
|
|
| `EVENT.KYC_COMPLETED` | 4 | user_id, person_id |
|
|
| `EVENT.LOGIN_SUCCESS` | - | user_id, device_fingerprint |
|
|
| `EVENT.LOGIN_FAILED` | - | email, attempt_count |
|
|
| `EVENT.PASSWORD_RESET_REQUESTED` | - | email |
|
|
| `EVENT.PASSWORD_RESET_COMPLETED` | - | user_id |
|
|
|
|
---
|
|
|
|
## 🔄 13. Teljes Adatfolyam Diagram
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant U as User (Böngésző)
|
|
participant FE as Vue Frontend
|
|
participant API as FastAPI Backend
|
|
participant DB as PostgreSQL
|
|
participant Email as Email Service
|
|
|
|
Note over U,Email: FÁZIS 1: Lite Regisztráció
|
|
U->>FE: Adatok megadása (email, név, jelszó)
|
|
FE->>FE: FingerprintJS generálás
|
|
FE->>API: POST /auth/register
|
|
API->>DB: SELECT system_parameters (dinamikus config)
|
|
API->>DB: INSERT person (is_active=False)
|
|
API->>DB: INSERT user (is_active=False)
|
|
API->>DB: INSERT verification_token (48h)
|
|
API->>Email: Küldés (verifikációs link)
|
|
API-->>FE: {status: "success", user_id, email}
|
|
FE->>U: Átirányítás /verify oldalra
|
|
|
|
Note over U,Email: FÁZIS 2: Email Verifikáció
|
|
U->>FE: Email megnyitása → Magic Link kattintás
|
|
FE->>FE: Token kiolvasás URL-ből
|
|
FE->>API: POST /auth/verify-email (token, device_fingerprint)
|
|
API->>DB: SELECT verification_token (is_used=False, expires>now)
|
|
API->>DB: UPDATE user SET is_active=True
|
|
API->>DB: UPDATE person SET is_active=True
|
|
API->>DB: UPDATE token SET is_used=True
|
|
API->>DB: INSERT device + user_device_link
|
|
API-->>FE: {access_token, refresh_token} → AUTO-LOGIN
|
|
FE->>FE: Token-ek mentése localStorage-be
|
|
FE->>FE: 3 másodperc countdown
|
|
FE->>U: Átirányítás /complete-kyc
|
|
|
|
Note over U,Email: FÁZIS 3: KYC 1. lépés - Cím
|
|
U->>FE: Cím adatok megadása
|
|
FE->>API: POST /auth/complete-kyc (cím adatok)
|
|
API->>API: Shadow Identity ellenőrzés
|
|
API->>DB: GeoService cím létrehozás
|
|
|
|
Note over U,Email: FÁZIS 4: KYC 2. lépés - Teljesítés
|
|
U->>FE: Személyes adatok + doksik
|
|
FE->>API: POST /auth/complete-kyc (összes adat)
|
|
API->>DB: UPDATE person (phone, birth, docs)
|
|
API->>DB: INSERT organization (personal)
|
|
API->>DB: INSERT wallet
|
|
API->>DB: INSERT branch
|
|
API->>DB: INSERT organization_member
|
|
API->>DB: INSERT gamification_event (XP)
|
|
API-->>FE: {status: "success"}
|
|
FE->>FE: Profil frissítés → fetchUser()
|
|
FE->>U: Átirányítás /dashboard
|
|
```
|
|
|
|
---
|
|
|
|
## ⚠️ 14. Hibakezelés
|
|
|
|
### 14.1 Backend Hibakódok
|
|
|
|
| HTTP Státusz | Hibaüzenet | Ok |
|
|
|-------------|------------|-----|
|
|
| `400` | `INVALID_PASSWORD` | Jelszó nem felel meg a komplexitási követelményeknek |
|
|
| `400` | `EMAIL_EXISTS` | Email már regisztrálva van |
|
|
| `400` | `INVALID_TOKEN` | Token érvénytelen vagy lejárt |
|
|
| `400` | `TOKEN_ALREADY_USED` | Token már fel lett használva |
|
|
| `400` | `INVALID_CREDENTIALS` | Rossz email/jelszó páros |
|
|
| `401` | `NOT_AUTHENTICATED` | Hiányzó vagy érvénytelen JWT |
|
|
| `403` | `ACCOUNT_INACTIVE` | Fiók nincs aktiválva (email verifikáció hiányzik) |
|
|
| `404` | `USER_NOT_FOUND` | User nem található |
|
|
| `429` | `TOO_MANY_ATTEMPTS` | Túl sok bejelentkezési kísérlet |
|
|
|
|
### 14.2 Frontend Hibakezelés
|
|
|
|
**Forrás:** `frontend/src/stores/auth.ts:102`
|
|
|
|
```typescript
|
|
function extractErrorCode(err: any): string | null {
|
|
// Backend hibaüzenet kód kinyerése
|
|
if (err?.response?.data?.detail) return err.response.data.detail;
|
|
if (err?.response?.data?.message) return err.response.data.message;
|
|
return null;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📁 15. Érintett Fájlok Listája
|
|
|
|
### Backend
|
|
| Fájl | Útvonal | Szerep |
|
|
|------|---------|--------|
|
|
| `auth.py` | `backend/app/api/v1/endpoints/auth.py` | 7 végpont a regisztrációhoz |
|
|
| `auth_service.py` | `backend/app/services/auth_service.py` | Üzleti logika (~500 sor) |
|
|
| `security.py` | `backend/app/core/security.py` | JWT token generálás, dekódolás |
|
|
| `config.py` | `backend/app/core/config.py` | Beállítások, dinamikus paraméterek |
|
|
| `deps.py` | `backend/app/api/deps.py` | Dependency injection (auth guard-ok) |
|
|
| `identity.py` | `backend/app/models/identity/identity.py` | Person, User, Wallet, Device modellek |
|
|
| `auth.py` | `backend/app/schemas/auth.py` | Pydantic bemeneti sémák |
|
|
| `email_manager.py` | `backend/app/services/email_manager.py` | Email küldés (Brevo/SMTP) |
|
|
| `geo_service.py` | `backend/app/services/geo_service.py` | Cím normalizálás és létrehozás |
|
|
|
|
### Frontend
|
|
| Fájl | Útvonal | Szerep |
|
|
|------|---------|--------|
|
|
| `LoginModal.vue` | `frontend/src/components/LoginModal.vue` | 3D kártya-forgató (login/register/forgot/resend) |
|
|
| `VerifyEmailView.vue` | `frontend/src/views/VerifyEmailView.vue` | Email verifikáció (3 állapot) |
|
|
| `CompleteKycView.vue` | `frontend/src/views/CompleteKycView.vue` | 2-lépéses KYC kitöltés |
|
|
| `LandingView.vue` | `frontend/src/views/LandingView.vue` | Főoldal a LoginModal-lal |
|
|
| `auth.ts` | `frontend/src/stores/auth.ts` | Pinia auth store (műveletek + állapot) |
|
|
| `index.ts` | `frontend/src/router/index.ts` | Route-ok és guard-ok |
|
|
|
|
---
|
|
|
|
## ✅ 16. Következtetés
|
|
|
|
A regisztrációs rendszer **biztonságos és jól rétegzett**:
|
|
- **JWT token-ek** rövid élettartammal (30 perc access, 1-30 nap refresh)
|
|
- **Verifikációs token-ek** konfigurálható élettartammal (48h / 2h)
|
|
- **Shadow Identity merge** megakadályozza a duplikált személyrekordokat
|
|
- **Device Fingerprinting** a bejelentkezés és verifikáció során
|
|
- **Dinamikus konfiguráció** admin által állítható paraméterekkel
|
|
- **Multi-provider email** Brevo API → SMTP → Fallback lánccal
|
|
- **Sentinel audit log** minden kritikus eseményről
|
|
- **i18n támogatás** mind a backend sablonokban, mind a frontend-en
|
|
|
|
A rendszer megfelel a GDPR követelményeknek (soft delete, személyes adatok szeparációja a Person és User modellek között).
|
|
|
|
---
|
|
|
|
*Dokumentáció készült: 2026-06-07*
|
|
*Audit típusa: Teljes regisztrációs folyamat elemzés (backend + frontend + adatbázis + tokenek)*
|