Nette Documentation Preview

syntax
Felhasználói bejelentkezés (Authentikáció)
******************************************

<div class=perex>

Szinte egyetlen webalkalmazás sem nélkülözheti a felhasználói bejelentkezési mechanizmust és a felhasználói jogosultságok ellenőrzését. Ebben a fejezetben a következőkről lesz szó:

- felhasználók be- és kijelentkeztetése
- saját authentikátorok

</div>

→ [Telepítés és követelmények |@home#Telepítés]

A példákban a [api:Nette\Security\User] osztály objektumát fogjuk használni, amely az aktuális felhasználót képviseli, és amelyhez úgy juthat hozzá, hogy [dependency injection |dependency-injection:passing-dependencies] segítségével kéri át. A presenterekben elegendő csak a `$user = $this->getUser()` hívása.


Authentikáció
=============

Az authentikáció **felhasználói bejelentkezést** jelent, tehát azt a folyamatot, amely során ellenőrizzük, hogy a felhasználó valóban az-e, akinek kiadja magát. Általában felhasználónévvel és jelszóval igazolja magát. Az ellenőrzést az ún. [#authentikátor] végzi. Ha a bejelentkezés sikertelen, `Nette\Security\AuthenticationException` kivétel dobódik.

```php
try {
	$user->login($username, $password);
} catch (Nette\Security\AuthenticationException $e) {
	$this->flashMessage('A felhasználónév vagy jelszó helytelen');
}
```

Így jelentkezteti ki a felhasználót:

```php
$user->logout();
```

És annak megállapítása, hogy be van-e jelentkezve:

```php
echo $user->isLoggedIn() ? 'igen' : 'nem';
```

Nagyon egyszerű, ugye? És minden biztonsági szempontot a Nette kezel Ön helyett.

A presenterekben ellenőrizheti a bejelentkezést a `startup()` metódusban, és a be nem jelentkezett felhasználót átirányíthatja a bejelentkezési oldalra.

```php
protected function startup()
{
	parent::startup();
	if (!$this->getUser()->isLoggedIn()) {
		$this->redirect('Sign:in');
	}
}
```


Lejárat
=======

A felhasználó bejelentkezése a [tároló lejáratával |#A bejelentkezett felhasználó tárolója] együtt jár le, amely általában a session (lásd a [session lejárata |http:configuration#Session] beállítását). De beállítható rövidebb időintervallum is, amelynek lejárta után a felhasználó kijelentkezik. Erre szolgál a `setExpiration()` metódus, amelyet a `login()` előtt kell meghívni. Paraméterként adjon meg egy relatív időt tartalmazó stringet:

```php
// a bejelentkezés 30 perc inaktivitás után lejár
$user->setExpiration('30 minutes');

// a beállított lejárat törlése
$user->setExpiration(null);
```

Azt, hogy a felhasználó az időintervallum lejárta miatt jelentkezett-e ki, a `$user->getLogoutReason()` metódus árulja el, amely vagy a `Nette\Security\UserStorage::LogoutInactivity` konstanst (lejárt az időkorlát) vagy a `UserStorage::LogoutManual` konstanst (a `logout()` metódussal jelentkeztették ki) adja vissza.


Authentikátor
=============

Ez egy objektum, amely ellenőrzi a bejelentkezési adatokat, tehát általában a nevet és a jelszót. Triviális formája a [api:Nette\Security\SimpleAuthenticator] osztály, amelyet a [konfigurációban|configuration] definiálhatunk:

```neon
security:
	users:
		# név: jelszó
		frantisek: tajneheslo
		katka: jestetajnejsiheslo
```

Ez a megoldás inkább tesztelési célokra alkalmas. Megmutatjuk, hogyan hozzunk létre egy authentikátort, amely egy adatbázis tábla alapján ellenőrzi a bejelentkezési adatokat.

Az authentikátor egy objektum, amely implementálja a [api:Nette\Security\Authenticator] interfészt a `authenticate()` metódussal. Feladata vagy az ún. [identitást |#Identitás] visszaadni, vagy `Nette\Security\AuthenticationException` kivételt dobni. Lehetőség lenne még hibakódot is megadni a helyzet finomabb megkülönböztetésére: `Authenticator::IdentityNotFound` és `Authenticator::InvalidCredential`.

```php
use Nette;
use Nette\Security\SimpleIdentity;

class MyAuthenticator implements Nette\Security\Authenticator
{
	public function __construct(
		private Nette\Database\Explorer $database,
		private Nette\Security\Passwords $passwords,
	) {
	}

	public function authenticate(string $username, string $password): SimpleIdentity
	{
		$row = $this->database->table('users')
			->where('username', $username)
			->fetch();

		if (!$row) {
			throw new Nette\Security\AuthenticationException('Felhasználó nem található.');
		}

		if (!$this->passwords->verify($password, $row->password)) {
			throw new Nette\Security\AuthenticationException('Érvénytelen jelszó.');
		}

		return new SimpleIdentity(
			$row->id,
			$row->role, // vagy több szerepkör tömbje
			['name' => $row->username],
		);
	}
}
```

A MyAuthenticator osztály a [Nette Database Explorer|database:explorer] segítségével kommunikál az adatbázissal, és a `users` táblával dolgozik, ahol a `username` oszlopban a felhasználó bejelentkezési neve, a `password` oszlopban pedig a [jelszó lenyomatát|passwords] tárolja. A név és jelszó ellenőrzése után visszaadja az identitást, amely tartalmazza a felhasználó azonosítóját (ID), szerepkörét (a tábla `role` oszlopa), amelyről [később |authorization#Szerepkörök] többet mondunk, és egy tömböt további adatokkal (esetünkben a felhasználónévvel).

Az authentikátort még hozzáadjuk a DI konténer konfigurációjához [szolgáltatásként|dependency-injection:services]:

```neon
services:
	- MyAuthenticator
```


$onLoggedIn, $onLoggedOut események
-----------------------------------

A `Nette\Security\User` objektumnak vannak [események |nette:glossary#Eventek események] `$onLoggedIn` és `$onLoggedOut`, tehát hozzáadhat callbackeket, amelyek a sikeres bejelentkezés után, illetve a felhasználó kijelentkezése után hívódnak meg.


```php
$user->onLoggedIn[] = function () {
	// a felhasználó éppen bejelentkezett
};
```


Identitás
=========

Az identitás a felhasználóról szóló információk összessége, amelyet az authentikátor ad vissza, és amely ezt követően a sessionben tárolódik, és a `$user->getIdentity()` segítségével érhető el. Tehát lekérhetjük az azonosítót, a szerepköröket és egyéb felhasználói adatokat, ahogyan azokat az authentikátorban átadtuk:

```php
$user->getIdentity()->getId();
// működik a $user->getId() rövidítés is;

$user->getIdentity()->getRoles();

// a felhasználói adatok property-ként érhetők el
// a név, amelyet a MyAuthenticatorban adtunk át
$user->getIdentity()->name;
```

Ami fontos, az az, hogy a `$user->logout()` segítségével történő kijelentkezéskor **az identitás nem törlődik**, és továbbra is rendelkezésre áll. Tehát bár a felhasználónak van identitása, nem feltétlenül van bejelentkezve. Ha explicit módon szeretnénk törölni az identitást, a `logout(true)` hívásával jelentkeztetjük ki a felhasználót.

Ennek köszönhetően továbbra is feltételezheti, hogy melyik felhasználó van a számítógépnél, és például személyre szabott ajánlatokat jeleníthet meg neki az e-shopban, de a személyes adatait csak a bejelentkezés után jelenítheti meg.

Az identitás egy objektum, amely implementálja a [api:Nette\Security\IIdentity] interfészt, az alapértelmezett implementáció a [api:Nette\Security\SimpleIdentity]. És ahogy említettük, a sessionben tárolódik, tehát ha például megváltoztatjuk valamelyik bejelentkezett felhasználó szerepkörét, a régi adatok az identitásában maradnak egészen az újbóli bejelentkezéséig.


A bejelentkezett felhasználó tárolója
=====================================

A felhasználóról szóló két alapvető információ, tehát hogy be van-e jelentkezve és az ő [identitása |#Identitás], általában a sessionben kerül átvitelre. Ez megváltoztatható. Ezen információk tárolásáért egy objektum felel, amely implementálja a `Nette\Security\UserStorage` interfészt. Két standard implementáció áll rendelkezésre, az első a sessionben, a második a cookie-ban továbbítja az adatokat. Ezek a `Nette\Bridges\SecurityHttp\SessionStorage` és a `CookieStorage` osztályok. A tárolót kiválaszthatja és konfigurálhatja nagyon kényelmesen a [security › authentication |configuration#Tároló] konfigurációban.

Továbbá befolyásolhatja, hogy pontosan hogyan történjen az identitás tárolása (*sleep*) és visszaállítása (*wakeup*). Elegendő, ha az authenticator implementálja a `Nette\Security\IdentityHandler` interfészt. Ennek két metódusa van: a `sleepIdentity()` az identitás tárolóba írása előtt hívódik meg, a `wakeupIdentity()` pedig annak kiolvasása után. A metódusok módosíthatják az identitás tartalmát, vagy helyettesíthetik egy új objektummal, amelyet visszaadnak. A `wakeupIdentity()` metódus akár `null`-t is visszaadhat, ezzel kijelentkeztetve a felhasználót.

Példaként megmutatjuk a gyakori kérdés megoldását, hogyan frissítsük a szerepköröket az identitásban rögtön a sessionből való betöltés után. A `wakeupIdentity()` metódusban átadjuk az identitásba az aktuális szerepköröket, pl. az adatbázisból:

```php
final class Authenticator implements
	Nette\Security\Authenticator, Nette\Security\IdentityHandler
{
	public function sleepIdentity(IIdentity $identity): IIdentity
	{
		// itt lehet módosítani az identitást a tárolóba írás előtt a bejelentkezés után,
		// de erre most nincs szükségünk
		return $identity;
	}

	public function wakeupIdentity(IIdentity $identity): ?IIdentity
	{
		// szerepkörök frissítése az identitásban
		$userId = $identity->getId();
		$identity->setRoles($this->facade->getUserRoles($userId));
		return $identity;
	}
```

És most visszatérünk a cookie alapú tárolóhoz. Lehetővé teszi egy olyan weboldal létrehozását, ahol a felhasználók bejelentkezhetnek, és ehhez nincs szükség sessionökre. Tehát nincs szükség a lemezre írásra. Egyébként így működik az a weboldal is, amelyet éppen olvas, beleértve a fórumot is. Ebben az esetben az `IdentityHandler` implementálása elengedhetetlen. A cookie-ba ugyanis csak egy véletlenszerű tokent fogunk tárolni, amely a bejelentkezett felhasználót reprezentálja.

Először tehát a konfigurációban beállítjuk a kívánt tárolót a `security › authentication › storage: cookie` segítségével.

Az adatbázisban létrehozunk egy `authtoken` oszlopot, amelyben minden felhasználónak egy [teljesen véletlenszerű, egyedi és kitalálhatatlan|utils:random] stringje lesz, megfelelő hosszúságú (legalább 13 karakter). A `CookieStorage` tároló a cookie-ban csak az `$identity->getId()` értékét továbbítja, tehát a `sleepIdentity()`-ben az eredeti identitást egy helyettesítő identitásra cseréljük, amelynek azonosítójában az `authtoken` van, míg a `wakeupIdentity()` metódusban az authtoken alapján kiolvassuk a teljes identitást az adatbázisból:

```php
final class Authenticator implements
	Nette\Security\Authenticator, Nette\Security\IdentityHandler
{
	public function authenticate(string $username, string $password): SimpleIdentity
	{
		$row = $this->db->fetch('SELECT * FROM user WHERE username = ?', $username);
		// ellenőrizzük a jelszót
		...
		// visszaadjuk az identitást az adatbázisból származó összes adattal
		return new SimpleIdentity($row->id, null, (array) $row);
	}

	public function sleepIdentity(IIdentity $identity): SimpleIdentity
	{
		// visszaadjuk a helyettesítő identitást, ahol az ID-ben az authtoken lesz
		return new SimpleIdentity($identity->authtoken);
	}

	public function wakeupIdentity(IIdentity $identity): ?SimpleIdentity
	{
		// a helyettesítő identitást lecseréljük a teljes identitásra, mint az authenticate()-ban
		$row = $this->db->fetch('SELECT * FROM user WHERE authtoken = ?', $identity->getId());
		return $row
			? new SimpleIdentity($row->id, null, (array) $row)
			: null;
	}
}
```


Több független bejelentkezés
============================

Egy weboldalon és egy sessionön belül párhuzamosan több független bejelentkező felhasználó is lehet. Ha például egy weboldalon külön authentikációt szeretnénk az adminisztrációhoz és a nyilvános részhez, elegendő mindegyiknek saját nevet beállítani:

```php
$user->getStorage()->setNamespace('backend');
```

Fontos megjegyezni, hogy a névteret mindig az adott részhez tartozó összes helyen be kell állítani. Ha presentereket használunk, a névteret az adott rész közös ősében állítjuk be - általában a BasePresenterben. Ezt a [checkRequirements() |api:Nette\Application\UI\Presenter::checkRequirements()] metódus kiterjesztésével tesszük meg:

```php
public function checkRequirements($element): void
{
	$this->getUser()->getStorage()->setNamespace('backend');
	parent::checkRequirements($element);
}
```


Több authentikátor
------------------

Az alkalmazás független bejelentkezéssel rendelkező részekre való felosztása általában különböző authentikátorokat is igényel. Azonban, ha a szolgáltatások konfigurációjában két, az Authenticator interfészt implementáló osztályt regisztrálnánk, a Nette nem tudná, melyiket rendelje automatikusan a `Nette\Security\User` objektumhoz, és hibát jelezne. Ezért az authentikátorokhoz az [autowiring |dependency-injection:autowiring]-ot úgy kell korlátoznunk, hogy csak akkor működjön, ha valaki egy konkrét osztályt kér, pl. FrontAuthenticator, amit az `autowired: self` opcióval érünk el:

```neon
services:
	-
		create: FrontAuthenticator
		autowired: self
```

```php
class SignPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private FrontAuthenticator $authenticator,
	) {
	}
}
```

A User objektum authentikátorát a [login() |api:Nette\Security\User::login()] metódus hívása előtt állítjuk be, tehát általában annak az űrlapnak a kódjában, amely bejelentkezteti:

```php
$form->onSuccess[] = function (Form $form, \stdClass $data) {
	$user = $this->getUser();
	$user->setAuthenticator($this->authenticator);
	$user->login($data->username, $data->password);
	// ...
};
```

Felhasználói bejelentkezés (Authentikáció)

Szinte egyetlen webalkalmazás sem nélkülözheti a felhasználói bejelentkezési mechanizmust és a felhasználói jogosultságok ellenőrzését. Ebben a fejezetben a következőkről lesz szó:

  • felhasználók be- és kijelentkeztetése
  • saját authentikátorok

Telepítés és követelmények

A példákban a Nette\Security\User osztály objektumát fogjuk használni, amely az aktuális felhasználót képviseli, és amelyhez úgy juthat hozzá, hogy dependency injection segítségével kéri át. A presenterekben elegendő csak a $user = $this->getUser() hívása.

Authentikáció

Az authentikáció felhasználói bejelentkezést jelent, tehát azt a folyamatot, amely során ellenőrizzük, hogy a felhasználó valóban az-e, akinek kiadja magát. Általában felhasználónévvel és jelszóval igazolja magát. Az ellenőrzést az ún. authentikátor végzi. Ha a bejelentkezés sikertelen, Nette\Security\AuthenticationException kivétel dobódik.

try {
	$user->login($username, $password);
} catch (Nette\Security\AuthenticationException $e) {
	$this->flashMessage('A felhasználónév vagy jelszó helytelen');
}

Így jelentkezteti ki a felhasználót:

$user->logout();

És annak megállapítása, hogy be van-e jelentkezve:

echo $user->isLoggedIn() ? 'igen' : 'nem';

Nagyon egyszerű, ugye? És minden biztonsági szempontot a Nette kezel Ön helyett.

A presenterekben ellenőrizheti a bejelentkezést a startup() metódusban, és a be nem jelentkezett felhasználót átirányíthatja a bejelentkezési oldalra.

protected function startup()
{
	parent::startup();
	if (!$this->getUser()->isLoggedIn()) {
		$this->redirect('Sign:in');
	}
}

Lejárat

A felhasználó bejelentkezése a tároló lejáratával együtt jár le, amely általában a session (lásd a session lejárata beállítását). De beállítható rövidebb időintervallum is, amelynek lejárta után a felhasználó kijelentkezik. Erre szolgál a setExpiration() metódus, amelyet a login() előtt kell meghívni. Paraméterként adjon meg egy relatív időt tartalmazó stringet:

// a bejelentkezés 30 perc inaktivitás után lejár
$user->setExpiration('30 minutes');

// a beállított lejárat törlése
$user->setExpiration(null);

Azt, hogy a felhasználó az időintervallum lejárta miatt jelentkezett-e ki, a $user->getLogoutReason() metódus árulja el, amely vagy a Nette\Security\UserStorage::LogoutInactivity konstanst (lejárt az időkorlát) vagy a UserStorage::LogoutManual konstanst (a logout() metódussal jelentkeztették ki) adja vissza.

Authentikátor

Ez egy objektum, amely ellenőrzi a bejelentkezési adatokat, tehát általában a nevet és a jelszót. Triviális formája a Nette\Security\SimpleAuthenticator osztály, amelyet a konfigurációban definiálhatunk:

security:
	users:
		# név: jelszó
		frantisek: tajneheslo
		katka: jestetajnejsiheslo

Ez a megoldás inkább tesztelési célokra alkalmas. Megmutatjuk, hogyan hozzunk létre egy authentikátort, amely egy adatbázis tábla alapján ellenőrzi a bejelentkezési adatokat.

Az authentikátor egy objektum, amely implementálja a Nette\Security\Authenticator interfészt a authenticate() metódussal. Feladata vagy az ún. identitást visszaadni, vagy Nette\Security\AuthenticationException kivételt dobni. Lehetőség lenne még hibakódot is megadni a helyzet finomabb megkülönböztetésére: Authenticator::IdentityNotFound és Authenticator::InvalidCredential.

use Nette;
use Nette\Security\SimpleIdentity;

class MyAuthenticator implements Nette\Security\Authenticator
{
	public function __construct(
		private Nette\Database\Explorer $database,
		private Nette\Security\Passwords $passwords,
	) {
	}

	public function authenticate(string $username, string $password): SimpleIdentity
	{
		$row = $this->database->table('users')
			->where('username', $username)
			->fetch();

		if (!$row) {
			throw new Nette\Security\AuthenticationException('Felhasználó nem található.');
		}

		if (!$this->passwords->verify($password, $row->password)) {
			throw new Nette\Security\AuthenticationException('Érvénytelen jelszó.');
		}

		return new SimpleIdentity(
			$row->id,
			$row->role, // vagy több szerepkör tömbje
			['name' => $row->username],
		);
	}
}

A MyAuthenticator osztály a Nette Database Explorer segítségével kommunikál az adatbázissal, és a users táblával dolgozik, ahol a username oszlopban a felhasználó bejelentkezési neve, a password oszlopban pedig a jelszó lenyomatát tárolja. A név és jelszó ellenőrzése után visszaadja az identitást, amely tartalmazza a felhasználó azonosítóját (ID), szerepkörét (a tábla role oszlopa), amelyről később többet mondunk, és egy tömböt további adatokkal (esetünkben a felhasználónévvel).

Az authentikátort még hozzáadjuk a DI konténer konfigurációjához szolgáltatásként:

services:
	- MyAuthenticator

$onLoggedIn, $onLoggedOut események

A Nette\Security\User objektumnak vannak események $onLoggedIn és $onLoggedOut, tehát hozzáadhat callbackeket, amelyek a sikeres bejelentkezés után, illetve a felhasználó kijelentkezése után hívódnak meg.

$user->onLoggedIn[] = function () {
	// a felhasználó éppen bejelentkezett
};

Identitás

Az identitás a felhasználóról szóló információk összessége, amelyet az authentikátor ad vissza, és amely ezt követően a sessionben tárolódik, és a $user->getIdentity() segítségével érhető el. Tehát lekérhetjük az azonosítót, a szerepköröket és egyéb felhasználói adatokat, ahogyan azokat az authentikátorban átadtuk:

$user->getIdentity()->getId();
// működik a $user->getId() rövidítés is;

$user->getIdentity()->getRoles();

// a felhasználói adatok property-ként érhetők el
// a név, amelyet a MyAuthenticatorban adtunk át
$user->getIdentity()->name;

Ami fontos, az az, hogy a $user->logout() segítségével történő kijelentkezéskor az identitás nem törlődik, és továbbra is rendelkezésre áll. Tehát bár a felhasználónak van identitása, nem feltétlenül van bejelentkezve. Ha explicit módon szeretnénk törölni az identitást, a logout(true) hívásával jelentkeztetjük ki a felhasználót.

Ennek köszönhetően továbbra is feltételezheti, hogy melyik felhasználó van a számítógépnél, és például személyre szabott ajánlatokat jeleníthet meg neki az e-shopban, de a személyes adatait csak a bejelentkezés után jelenítheti meg.

Az identitás egy objektum, amely implementálja a Nette\Security\IIdentity interfészt, az alapértelmezett implementáció a Nette\Security\SimpleIdentity. És ahogy említettük, a sessionben tárolódik, tehát ha például megváltoztatjuk valamelyik bejelentkezett felhasználó szerepkörét, a régi adatok az identitásában maradnak egészen az újbóli bejelentkezéséig.

A bejelentkezett felhasználó tárolója

A felhasználóról szóló két alapvető információ, tehát hogy be van-e jelentkezve és az ő identitása, általában a sessionben kerül átvitelre. Ez megváltoztatható. Ezen információk tárolásáért egy objektum felel, amely implementálja a Nette\Security\UserStorage interfészt. Két standard implementáció áll rendelkezésre, az első a sessionben, a második a cookie-ban továbbítja az adatokat. Ezek a Nette\Bridges\SecurityHttp\SessionStorage és a CookieStorage osztályok. A tárolót kiválaszthatja és konfigurálhatja nagyon kényelmesen a security › authentication konfigurációban.

Továbbá befolyásolhatja, hogy pontosan hogyan történjen az identitás tárolása (sleep) és visszaállítása (wakeup). Elegendő, ha az authenticator implementálja a Nette\Security\IdentityHandler interfészt. Ennek két metódusa van: a sleepIdentity() az identitás tárolóba írása előtt hívódik meg, a wakeupIdentity() pedig annak kiolvasása után. A metódusok módosíthatják az identitás tartalmát, vagy helyettesíthetik egy új objektummal, amelyet visszaadnak. A wakeupIdentity() metódus akár null-t is visszaadhat, ezzel kijelentkeztetve a felhasználót.

Példaként megmutatjuk a gyakori kérdés megoldását, hogyan frissítsük a szerepköröket az identitásban rögtön a sessionből való betöltés után. A wakeupIdentity() metódusban átadjuk az identitásba az aktuális szerepköröket, pl. az adatbázisból:

final class Authenticator implements
	Nette\Security\Authenticator, Nette\Security\IdentityHandler
{
	public function sleepIdentity(IIdentity $identity): IIdentity
	{
		// itt lehet módosítani az identitást a tárolóba írás előtt a bejelentkezés után,
		// de erre most nincs szükségünk
		return $identity;
	}

	public function wakeupIdentity(IIdentity $identity): ?IIdentity
	{
		// szerepkörök frissítése az identitásban
		$userId = $identity->getId();
		$identity->setRoles($this->facade->getUserRoles($userId));
		return $identity;
	}

És most visszatérünk a cookie alapú tárolóhoz. Lehetővé teszi egy olyan weboldal létrehozását, ahol a felhasználók bejelentkezhetnek, és ehhez nincs szükség sessionökre. Tehát nincs szükség a lemezre írásra. Egyébként így működik az a weboldal is, amelyet éppen olvas, beleértve a fórumot is. Ebben az esetben az IdentityHandler implementálása elengedhetetlen. A cookie-ba ugyanis csak egy véletlenszerű tokent fogunk tárolni, amely a bejelentkezett felhasználót reprezentálja.

Először tehát a konfigurációban beállítjuk a kívánt tárolót a security › authentication › storage: cookie segítségével.

Az adatbázisban létrehozunk egy authtoken oszlopot, amelyben minden felhasználónak egy teljesen véletlenszerű, egyedi és kitalálhatatlan stringje lesz, megfelelő hosszúságú (legalább 13 karakter). A CookieStorage tároló a cookie-ban csak az $identity->getId() értékét továbbítja, tehát a sleepIdentity()-ben az eredeti identitást egy helyettesítő identitásra cseréljük, amelynek azonosítójában az authtoken van, míg a wakeupIdentity() metódusban az authtoken alapján kiolvassuk a teljes identitást az adatbázisból:

final class Authenticator implements
	Nette\Security\Authenticator, Nette\Security\IdentityHandler
{
	public function authenticate(string $username, string $password): SimpleIdentity
	{
		$row = $this->db->fetch('SELECT * FROM user WHERE username = ?', $username);
		// ellenőrizzük a jelszót
		...
		// visszaadjuk az identitást az adatbázisból származó összes adattal
		return new SimpleIdentity($row->id, null, (array) $row);
	}

	public function sleepIdentity(IIdentity $identity): SimpleIdentity
	{
		// visszaadjuk a helyettesítő identitást, ahol az ID-ben az authtoken lesz
		return new SimpleIdentity($identity->authtoken);
	}

	public function wakeupIdentity(IIdentity $identity): ?SimpleIdentity
	{
		// a helyettesítő identitást lecseréljük a teljes identitásra, mint az authenticate()-ban
		$row = $this->db->fetch('SELECT * FROM user WHERE authtoken = ?', $identity->getId());
		return $row
			? new SimpleIdentity($row->id, null, (array) $row)
			: null;
	}
}

Több független bejelentkezés

Egy weboldalon és egy sessionön belül párhuzamosan több független bejelentkező felhasználó is lehet. Ha például egy weboldalon külön authentikációt szeretnénk az adminisztrációhoz és a nyilvános részhez, elegendő mindegyiknek saját nevet beállítani:

$user->getStorage()->setNamespace('backend');

Fontos megjegyezni, hogy a névteret mindig az adott részhez tartozó összes helyen be kell állítani. Ha presentereket használunk, a névteret az adott rész közös ősében állítjuk be – általában a BasePresenterben. Ezt a checkRequirements() metódus kiterjesztésével tesszük meg:

public function checkRequirements($element): void
{
	$this->getUser()->getStorage()->setNamespace('backend');
	parent::checkRequirements($element);
}

Több authentikátor

Az alkalmazás független bejelentkezéssel rendelkező részekre való felosztása általában különböző authentikátorokat is igényel. Azonban, ha a szolgáltatások konfigurációjában két, az Authenticator interfészt implementáló osztályt regisztrálnánk, a Nette nem tudná, melyiket rendelje automatikusan a Nette\Security\User objektumhoz, és hibát jelezne. Ezért az authentikátorokhoz az autowiring-ot úgy kell korlátoznunk, hogy csak akkor működjön, ha valaki egy konkrét osztályt kér, pl. FrontAuthenticator, amit az autowired: self opcióval érünk el:

services:
	-
		create: FrontAuthenticator
		autowired: self
class SignPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private FrontAuthenticator $authenticator,
	) {
	}
}

A User objektum authentikátorát a login() metódus hívása előtt állítjuk be, tehát általában annak az űrlapnak a kódjában, amely bejelentkezteti:

$form->onSuccess[] = function (Form $form, \stdClass $data) {
	$user = $this->getUser();
	$user->setAuthenticator($this->authenticator);
	$user->login($data->username, $data->password);
	// ...
};