# Timonix Auth SDK (`timonix/auth-sdk`)

Oficjalna, bezpieczna, wewnętrzna biblioteka SDK do weryfikacji i autoryzacji tokenów (SSO, OAuth2, API Keys, M2M) dla ekosystemu aplikacji **Timonix** (m.in. Tconsole, Tkonto, Tdom, Aquay, mikroserwisy i API).

---

## 🚀 Główne Możliwości

- **Wielopoziomowa weryfikacja tokenów**:
  - **Lokalna weryfikacja kryptograficzna (JWT)**: Natychmiastowe sprawdzanie podpisu bez opóźnień sieciowych (0ms).
  - **Wsparcie dla HS256 (Symetryczny sekret)**: Domyślny, rekomendowany na start standard współdzielonego klucza między Tkonto a serwisami.
  - **Wsparcie dla RS256 (Asymetryczny klucz publiczny RSA)**: Bezpieczna weryfikacja kluczem publicznym OpenSSL.
  - **Zdalna introspekcja (RFC 7662)**: Weryfikacja w czasie rzeczywistym tokenów nieprzezroczystych (opaque) oraz natychmiastowe sprawdzanie unieważnień (revocation).
  - **Wsparcie dla Kluczy API (M2M)**: Obsługa nagłówków `X-Api-Key`, `X-Timonix-Bearer` oraz `X-Timonix-Internal-Key`.
- **Zaawansowany Silnik Uprawnień i Ról (RBAC & ABAC)**:
  - Sprawdzanie uprawnień granularnych: `$user->can('projects.create')`.
  - Uprawnienia ze znakami wieloznacznymi (**Wildcards**): np. `projects.*` daje dostęp do wszystkich akcji w module `projects`.
  - Sprawdzanie ról: `$user->hasRole('admin')`, `$user->hasAnyRole(['editor', 'admin'])`.
  - Sprawdzanie zakresów OAuth2: `$user->hasScope('openid')`.
  - Wymuszanie autoryzacji: `$sdk->authorize('projects.delete')` — automatycznie rzuca `ForbiddenException` w przypadku braku dostępu.
- **Bezpieczeństwo klasy Enterprise**:
  - **Timing-Attack Safe**: Wszelkie porównania realizowane w stałym czasie za pomocą `hash_equals()`.
  - **Ochrona przed Algorithm Confusion**: Twarda blokada ataku `"alg": "none"` oraz podstawiania klucza asymetrycznego pod symetryczny.
  - **Automatyczne maskowanie wrażliwych danych**: Tokeny i sekrety nie wyciekają do logów, `var_dump()` ani raportów błędów.
  - **Tolerancja na Clock Drift (Leeway)**: 60-sekundowe okno czasowe na różnice zegarów serwerów.
- **Pełna integracja z Frameworkiem Atom**:
  - `TimonixAuthMiddleware`: Rejestrowany w kontrolerach Atom, zabezpiecza akcje i zwraca automatyczne odpowiedzi JSON 401/403.
  - `TimonixAtomGuard`: Statyczny guard dostępny w całej aplikacji Atom (`TimonixAtomGuard::user()`, `TimonixAtomGuard::can(...)`).

---

## 📦 Instalacja w Projekcie

W pliku `composer.json` aplikacji dodaj lokalne repozytorium path lub zainstaluj bibliotekę:

```json
{
    "repositories": [
        {
            "type": "path",
            "url": "WEWNĘCZNA BIBLIOTEKA SDK"
        }
    ],
    "require": {
        "timonix/auth-sdk": "*"
    }
}
```

---

## ⚡ Szybki Start

### 1. Wariant Symetryczny: HS256 (Współdzielony Sekret - Wybór Projektu)

```php
use Timonix\Sdk\Auth\Client\TimonixAuthClient;
use Timonix\Sdk\Auth\Exceptions\ForbiddenException;
use Timonix\Sdk\Auth\Exceptions\TimonixAuthException;

// 1. Inicjalizacja z konfiguracji lub .env
$sdk = TimonixAuthClient::builder()
    ->withProviderUrl('https://account.timonix.pl')
    ->withSymmetricSecret(getenv('TIMONIX_AUTH_SECRET')) // HS256
    ->withExpectedIssuer('account.timonix.pl')
    ->withCacheTtl(300) // 5 minut cache
    ->build();

$client = new TimonixAuthClient($sdk);

try {
    // 2. Automatyczne pobranie i weryfikacja tokenu z żądania HTTP
    $user = $client->authenticate();

    echo "Zalogowano: " . $user->getEmail();
    echo "ID Użytkownika: " . $user->getId();

    // 3. Weryfikacja uprawnień (RBAC i Wildcards)
    if ($user->can('projects.create')) {
        // Użytkownik ma bezpośrednie uprawnienie lub 'projects.*' lub rolę 'admin'
    }

    if ($user->hasRole('developer')) {
        // Użytkownik posiada rolę developer
    }

    // 4. Wymuszenie autoryzacji (rzuca ForbiddenException jeśli brak uprawnień)
    $client->authorize('domains.delete');

} catch (ForbiddenException $e) {
    // 403 Forbidden - Użytkownik jest zalogowany, ale nie ma uprawnień
    http_response_code(403);
    echo json_encode(['error' => $e->getMessage(), 'required' => $e->getRequiredPermission()]);
} catch (TimonixAuthException $e) {
    // 401 Unauthorized - Brak tokenu, token wygasł lub zły podpis
    http_response_code(401);
    echo json_encode(['error' => $e->getMessage()]);
}
```

---

### 2. Wariant Asymetryczny: RS256 (Klucz Publiczny RSA)

```php
$sdk = TimonixAuthClient::builder()
    ->withProviderUrl('https://account.timonix.pl')
    ->withPublicKey(file_get_contents('/sciezka/do/public_key.pem')) // RS256
    ->withExpectedIssuer('account.timonix.pl')
    ->build();

$client = new TimonixAuthClient($sdk);
```

---

## 🏛️ Integracja z Frameworkiem Atom

### 1. Zabezpieczenie Kontrolera za pomocą `TimonixAuthMiddleware`

W kontrolerze dziedziczącym po `Atom\Controller`:

```php
namespace App\Controllers;

use Atom\Controller;
use Atom\HttpFoundation\Request;
use Atom\HttpFoundation\Response;
use Timonix\Sdk\Auth\Integrations\Atom\TimonixAuthMiddleware;
use Timonix\Sdk\Auth\Integrations\Atom\TimonixAtomGuard;

class ProjectApiController extends Controller
{
    public function __construct()
    {
        // Rejestracja middleware:
        // Wymaga aktywnego tokenu oraz uprawnienia 'projects.manage'
        // z wyłączeniem akcji 'publicOverview'
        $this->registerMiddleware(new TimonixAuthMiddleware(
            requiredPermission: 'projects.manage',
            exceptActions: ['publicOverview'],
            jsonResponse: true // automatyczna odpowiedź JSON 401/403 przy błędzie
        ));
    }

    public function list(Request $request, Response $response): string
    {
        // Bezpośredni dostęp do tożsamości zalogowanego użytkownika:
        $userId = TimonixAtomGuard::id();
        $user = TimonixAtomGuard::user();

        // Dodatkowe sprawdzenia uprawnień wewnątrz akcji:
        if (TimonixAtomGuard::can('projects.export')) {
            // eksport danych
        }

        return json_encode([
            'status' => 'success',
            'user' => $user->getEmail(),
            'roles' => $user->getRoles()
        ]);
    }
}
```

### 2. Użycie `TimonixAtomGuard` w dowolnym miejscu aplikacji

```php
use Timonix\Sdk\Auth\Integrations\Atom\TimonixAtomGuard;

if (TimonixAtomGuard::check()) {
    $currentUser = TimonixAtomGuard::user();
    $userId = TimonixAtomGuard::id();
    
    if (TimonixAtomGuard::can('admin.access')) {
        // Dostęp do panelu
    }
}
```

---

## 🔍 Źródła Ekstrakcji Tokenów

SDK automatycznie wykrywa i pobiera tokeny z żądania według priorytetu:
1. `Authorization: Bearer <token>`
2. `X-Timonix-Bearer: <token>`
3. `X-Bearer-Token: <token>`
4. `X-Api-Key: <key>` (klucze API `tk_...`)
5. Parametr query `access_token` lub `api_key` (konfigurowalny)
6. Ciasteczko `timonix_token` lub `console_access_token` (konfigurowalne)

---

## 🛡️ Hierarchia Wyjątków i Kody HTTP

Wszystkie wyjątki dziedziczą po `Timonix\Sdk\Auth\Exceptions\TimonixAuthException`:

| Klasa Wyjątku | Kod HTTP | Opis |
|---------------|----------|------|
| `MissingTokenException` | `401` | Brak nagłówka autoryzacyjnego w żądaniu |
| `InvalidTokenException` | `401` | Błędna struktura tokenu lub uszkodzone dane JSON |
| `TokenExpiredException` | `401` | Upłynął czas ważności tokenu (`exp`) |
| `TokenSignatureException`| `401` | Niepoprawny podpis lub próba fałszowania (`alg: none`) |
| `TokenRevokedException`  | `401` | Token został cofnięty w centralnym Tkonto |
| `UnauthorizedException`  | `401` | Ogólny brak autoryzacji do zasobu |
| `ForbiddenException`     | `403` | Brak wymaganego uprawnienia lub roli |
| `ProviderUnavailableException`| `503` | Serwer Tkonto niedostępny (błąd sieci, timeout) |
| `ConfigurationException` | `500` | Błędna konfiguracja SDK (np. brak klucza) |

---

## 🧪 Mockowanie w Testach Aplikacji Klienckich

W testach jednostkowych Twojej aplikacji (PHPUnit) nie musisz uruchamiać serwera Tkonto ani generować skomplikowanych podpisów kryptograficznych:

```php
use Timonix\Sdk\Auth\Client\TimonixAuthClient;
use Timonix\Sdk\Auth\Testing\MockTokenVerifier;

// 1. Stworzenie mocka reprezentującego zalogowanego programistę
$mock = MockTokenVerifier::asUser('usr_test_1', ['developer'], ['projects.create', 'projects.view']);

// LUB superadministratora z uprawnieniem '*'
$mockAdmin = MockTokenVerifier::asAdmin('usr_admin_1');

// 2. Wstrzyknięcie do klienta SDK
$sdk = new TimonixAuthClient(TimonixAuthClient::builder()->build(), $mock);

$user = $sdk->verifyToken('dowolny_ciag_testowy');
assert($user->can('projects.create') === true);
```

Do generowania prawdziwych tokenów testowych HS256/RS256 służy klasa `TestTokenFactory`:
```php
use Timonix\Sdk\Auth\Testing\TestTokenFactory;

$validToken = TestTokenFactory::createHs256Token($secret, ['roles' => ['admin']]);
$expiredToken = TestTokenFactory::createExpiredToken($secret);
$tamperedToken = TestTokenFactory::createTamperedToken($secret);
```

---

## ⚙️ Zmienne Środowiskowe (.env)

Możesz zainicjalizować SDK za pomocą `TimonixAuthClient::fromEnv()`, definiując w `.env`:

```env
# URL centralnego systemu logowania Tkonto
OAUTH_PROVIDER_URL=https://account.timonix.pl

# Sekret symetryczny HS256 współdzielony z Tkonto (min. 32 znaki)
TIMONIX_AUTH_SECRET=twoj_bezpieczny_sekret_hs256_min_32_znaki!

# Algorytm (HS256 lub RS256)
TIMONIX_AUTH_ALGO=HS256

# Klucz wewnętrzny dla komunikacji Maszyna-Maszyna (M2M)
TIMONIX_INTERNAL_KEY=twoj_klucz_wewnetrzny_m2m
```

---

## 📄 Licencja

Biblioteka wewnętrzna ekosystemu **Timonix**. Wszelkie prawa zastrzeżone.
