Preskočiť na obsah

Autorizácia ​

Každý endpoint má objekt autorizácie, tretí argument addApi(). Implementuje ApiAuthorizationInterface s dvoma metódami: authorized(): bool a getErrorMessage(): ?string.

Otvoriť diagram v plnej veľkosti

Keď authorized() vráti false, error handler odpovie 401 s {"status":"error","message":"..."} a hláškou z getErrorMessage(). Výnimka vyhodená z authorized() vráti 500 s textom výnimky.

Vstavané triedy ​

TriedaToken číta zKonštruktor
NoAuthorizationnič, prejde vždyžiadny
BearerTokenAuthorizationAuthorization: Bearer <token>(TokenRepositoryInterface, IpDetectorInterface)
HeaderApiKeyAuthenticationhlavičky, napríklad X-API-Key(string $headerName, TokenRepositoryInterface, IpDetectorInterface)
QueryApiKeyAuthenticationquery parametra, napríklad ?api_key=(string $queryParamName, TokenRepositoryInterface, IpDetectorInterface)
CookieApiKeyAuthenticationcookie(string $cookieName, TokenRepositoryInterface, IpDetectorInterface)
BasicAuthenticationmena a hesla v URL požiadavky(array $users, IRequest $httpRequest)

Tokenové triedy zdieľajú TokenAuthorization: token musí byť platný v repozitári a potom IP klienta musí prejsť obmedzením IP tokenu.

Tokeny a obmedzenie IP ​

TokenRepositoryInterface má validToken(string $token): bool a ipRestrictions(string $token): ?string. StaticTokenRepository berie pole token => obmedzenie:

yaml
application:
	mapping:
		Api: Tomaj\NetteApi\Presenters\*Presenter

parameters:
	# token => allowed IPs: '*' for any, one IP, a list, or an IPv4 CIDR range
	apiTokens:
		secret-token: '*'
		office-token: '10.0.0.0/8'
	apiUsers:
		admin: change-me

services:
	router: App\RouterFactory::create
	apiPresenter:
		factory: Tomaj\NetteApi\Presenters\ApiPresenter
	- Tomaj\NetteApi\Output\Configurator\EnvConfigurator
	- Tomaj\NetteApi\Error\DefaultErrorHandler

	# the only IpDetectorInterface service, so it is autowired everywhere
	- App\RemoteAddrIpDetector
	tokens: Tomaj\NetteApi\Misc\StaticTokenRepository(%apiTokens%)

	bearer: Tomaj\NetteApi\Authorization\BearerTokenAuthorization(@tokens)
	apiKey: Tomaj\NetteApi\Authorization\HeaderApiKeyAuthentication('X-API-Key', @tokens)
	basic: App\BasicCredentialsAuthorization(%apiUsers%)
	rateLimit:
		factory: App\FileRateLimit(directory: %tempDir%)
		# otherwise Nette DI passes it as the 4th argument of every addApi() call below
		autowired: false

	- App\Api\WhoAmIHandler

	apiDecider:
		factory: Tomaj\NetteApi\ApiDecider
		setup:
			# Authorization: Bearer secret-token
			- addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'bearer'), App\Api\WhoAmIHandler, @bearer)
			# X-API-Key: secret-token
			- addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'api-key'), App\Api\WhoAmIHandler, @apiKey)
			# curl -u admin:change-me
			- addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'basic'), App\Api\WhoAmIHandler, @basic)
			# no authorization, 3 requests per minute per IP
			- addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'limited'), App\Api\WhoAmIHandler, Tomaj\NetteApi\Authorization\NoAuthorization(), @rateLimit)
ObmedzenieZnamená
* alebo prázdny reťazecľubovoľná IP
1.2.3.4presne táto IP
1.2.3.4, 5.6.7.8ktorákoľvek z uvedených, oddelené čiarkami, medzerami alebo novými riadkami
10.0.0.0/8IPv4 rozsah
nullnikdy, Invalid IP

V reálnej aplikácii implementujte TokenRepositoryInterface nad vašou databázou.

Známe obmedzenia v 3.x

  • Detekciu IP sa dá podvrhnúť. Vstavaný IpDetector uprednostňuje hlavičky Client-IP a X-Forwarded-For, ktoré môže poslať ktorýkoľvek klient. Používajte ho len za proxy, ktorá ich prepisuje, alebo vlastný detektor ako RemoteAddrIpDetector z príkladu autorizácie.
  • Počíta sa len prvý rozsah. V zozname kontrola skončí pri prvej položke s /, takže 10.0.0.0/8, 192.168.0.0/16 sa s druhým rozsahom nikdy nezhoduje. Dajte rozsah na koniec, alebo jeden rozsah na token.
  • Číselné tokeny sa nikdy nezhodujú v StaticTokenRepository: PHP zmení kľúč ako '12345' na celé číslo, ktoré sa s reťazcovým tokenom nikdy nerovná. Používajte kľúče s písmenom.
  • BasicAuthentication číta meno z URL požiadavky, ktorú nette/http 3.2 a novší nechá prázdnu, takže tam nikdy neprejde. Príklad obsahuje BasicCredentialsAuthorization, ktorý číta hlavičku Authorization.

Od verzie 3.6.0 porovnávajú BasicAuthentication a StaticTokenRepository heslá a tokeny cez hash_equals(), v konštantnom čase, takže čas odpovede neprezradí, aká časť tokenu bola správna. Ktoré hodnoty sa zhodujú, je rovnaké ako predtým. Vo vlastnom repozitári tokenov alebo autorizácii porovnávajte tajomstvá tiež cez hash_equals().

Vlastná autorizácia ​

php
<?php

declare(strict_types=1);

namespace App\Api;

use Nette\Security\User;
use Tomaj\NetteApi\Authorization\ApiAuthorizationInterface;

final class AdminOnlyAuthorization implements ApiAuthorizationInterface
{
    public function __construct(private User $user) {}

    public function authorized(): bool
    {
        return $this->user->isInRole('admin');
    }

    public function getErrorMessage(): ?string
    {
        return 'Admins only';
    }
}

Časté otázky ​

Ako vyzerá telo odpovede 401? ​

{"status":"error","message":"Token doesn't exists or isn't active"} alebo iná hláška z getErrorMessage(). Zmeniť ju môžete vo vlastnom error handleri.

Môže jeden endpoint prijať bearer token aj API kľúč? ​

Napíšte triedu, ktorá skúsi oboje: zavolá authorized() na každej a zapamätá si, ktorú hlášku vrátiť. Príklad autorizácie ukazuje každú metódu na vlastnom endpointe.

Pošle konzola môj token? ​

Áno, webová konzola má pole pre bearer token, API kľúč v hlavičke, query aj cookie a meno s heslom pre Basic.

Ako povedať OpenAPI, že endpoint potrebuje token? ​

OpenApiHandler prečíta triedu autorizácie a pridá zodpovedajúcu bezpečnostnú schému. Pozrite si OpenAPI.

Kde obmedzím, ako často môže token volať API? ​

To je obmedzenie požiadaviek, nastavuje sa pre každý endpoint vedľa autorizácie.

Vydané pod licenciou MIT.