123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- <?php
- namespace Tqdev\PhpCrudApi\Middleware;
-
- use Psr\Http\Message\ResponseInterface;
- use Psr\Http\Message\ServerRequestInterface;
- use Tqdev\PhpCrudApi\Controller\Responder;
- use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
- use Tqdev\PhpCrudApi\Record\ErrorCode;
- use Tqdev\PhpCrudApi\RequestUtils;
-
- class JwtAuthMiddleware extends Middleware
- {
- private function getVerifiedClaims(string $token, int $time, int $leeway, int $ttl, string $secret, array $requirements): array
- {
- $algorithms = array(
- 'HS256' => 'sha256',
- 'HS384' => 'sha384',
- 'HS512' => 'sha512',
- 'RS256' => 'sha256',
- 'RS384' => 'sha384',
- 'RS512' => 'sha512',
- );
- $token = explode('.', $token);
- if (count($token) < 3) {
- return array();
- }
- $header = json_decode(base64_decode(strtr($token[0], '-_', '+/')), true);
- if (!$secret) {
- return array();
- }
- if ($header['typ'] != 'JWT') {
- return array();
- }
- $algorithm = $header['alg'];
- if (!isset($algorithms[$algorithm])) {
- return array();
- }
- if (!empty($requirements['alg']) && !in_array($algorithm, $requirements['alg'])) {
- return array();
- }
- $hmac = $algorithms[$algorithm];
- $signature = base64_decode(strtr($token[2], '-_', '+/'));
- $data = "$token[0].$token[1]";
- switch ($algorithm[0]) {
- case 'H':
- $hash = hash_hmac($hmac, $data, $secret, true);
- if (function_exists('hash_equals')) {
- $equals = hash_equals($signature, $hash);
- } else {
- $equals = $signature == $hash;
- }
- if (!$equals) {
- return array();
- }
- break;
- case 'R':
- $equals = openssl_verify($data, $signature, $secret, $hmac) == 1;
- if (!$equals) {
- return array();
- }
- break;
- }
- $claims = json_decode(base64_decode(strtr($token[1], '-_', '+/')), true);
- if (!$claims) {
- return array();
- }
- foreach ($requirements as $field => $values) {
- if (!empty($values)) {
- if ($field != 'alg') {
- if (!isset($claims[$field]) || !in_array($claims[$field], $values)) {
- return array();
- }
- }
- }
- }
- if (isset($claims['nbf']) && $time + $leeway < $claims['nbf']) {
- return array();
- }
- if (isset($claims['iat']) && $time + $leeway < $claims['iat']) {
- return array();
- }
- if (isset($claims['exp']) && $time - $leeway > $claims['exp']) {
- return array();
- }
- if (isset($claims['iat']) && !isset($claims['exp'])) {
- if ($time - $leeway > $claims['iat'] + $ttl) {
- return array();
- }
- }
- return $claims;
- }
-
- private function getClaims(string $token): array
- {
- $time = (int) $this->getProperty('time', time());
- $leeway = (int) $this->getProperty('leeway', '5');
- $ttl = (int) $this->getProperty('ttl', '30');
- $secret = $this->getProperty('secret', '');
- $requirements = array(
- 'alg' => $this->getArrayProperty('algorithms', ''),
- 'aud' => $this->getArrayProperty('audiences', ''),
- 'iss' => $this->getArrayProperty('issuers', ''),
- );
- if (!$secret) {
- return array();
- }
- return $this->getVerifiedClaims($token, $time, $leeway, $ttl, $secret, $requirements);
- }
-
- private function getAuthorizationToken(ServerRequestInterface $request): string
- {
- $headerName = $this->getProperty('header', 'X-Authorization');
- $headerValue = RequestUtils::getHeader($request, $headerName);
- $parts = explode(' ', trim($headerValue), 2);
- if (count($parts) != 2) {
- return '';
- }
- if ($parts[0] != 'Bearer') {
- return '';
- }
- return $parts[1];
- }
-
- public function handle(ServerRequestInterface $request): ResponseInterface
- {
- if (session_status() == PHP_SESSION_NONE) {
- session_start();
- }
- $token = $this->getAuthorizationToken($request);
- if ($token) {
- $claims = $this->getClaims($token);
- $_SESSION['claims'] = $claims;
- if (empty($claims)) {
- return $this->responder->error(ErrorCode::AUTHENTICATION_FAILED, 'JWT');
- }
- if (!headers_sent()) {
- session_regenerate_id();
- }
- }
- if (empty($_SESSION['claims'])) {
- $authenticationMode = $this->getProperty('mode', 'required');
- if ($authenticationMode == 'required') {
- return $this->responder->error(ErrorCode::AUTHENTICATION_REQUIRED, '');
- }
- }
- return $this->next->handle($request);
- }
- }
|