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)
BasicAuthenticationhlavičky Authorization: Basic (v 3.x URL požiadavky, pozrite nižšie)(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; it cannot be forged with headers
	- Tomaj\NetteApi\Misc\IpDetector
	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%)
	# 3 requests per minute per IP, counted in the Nette cache (%tempDir%/cache)
	rateLimit:
		factory: Tomaj\NetteApi\RateLimit\FixedWindowLimiter(perIp, 3, 60, Tomaj\NetteApi\RateLimit\Key\IpKey(), Tomaj\NetteApi\RateLimit\Storage\NetteCacheStorage())

	- 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úť. IpDetector z 3.x uprednostňuje hlavičky Client-IP a X-Forwarded-For, ktoré môže poslať ktorýkoľvek klient. Od 4.0 berie adresu z Nette\Http\IRequest::getRemoteAddress(), ktorá verí preposlaným hlavičkám len od proxy v http: proxy:, takže za reverse proxy ju tam uveďte, inak má každý klient adresu proxy. Token obmedzený na adresu proxy (127.0.0.1 za lokálnym nginx, rozsah vášho load balancera) potom prejde každému klientovi, ktorý token má. Pozrite si návod na upgrade.
  • 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.
  • BasicAuthentication číta meno z URL požiadavky, ktorú nette/http 3.2 a novší nechá prázdnu, takže v 3.x tam nikdy neprejde. Napíšte triedu, ktorá číta $request->getBasicCredentials(). 4.0 číta hlavičku Authorization: Basic.
  • Čí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.

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';
    }
}

Pre obmedzenie s key: identity implementujte aj AuthorizationIdentityInterface: getIdentity() vráti, kto volá (id používateľa, token), alebo null pred úspešným authorized(). Vstavané tokenové autorizácie vracajú token a BasicAuthentication meno používateľa. Potomok, ktorý prepíše ich authorized() bez volania rodiča, musí prepísať aj getIdentity(). Pozrite si Obmedzenie požiadaviek.

Č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.