Olympus Docs
IntegrateBackends

Symfony (PHP) integration

Validate Olympus tokens in a Symfony app

Symfony's Security component supports custom authenticators. We'll add one that validates Olympus OAuth2 access tokens.

Composer

composer require lcobucci/jwt firebase/php-jwt symfony/cache

JWKS fetcher with cache

// src/Security/JwksFetcher.php
namespace App\Security;

use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;

class JwksFetcher {
  public function __construct(private CacheInterface $cache) {}

  public function getKeys(): array {
    return $this->cache->get('olympus_jwks', function (ItemInterface $item) {
      $item->expiresAfter(3600);
      $json = file_get_contents($_ENV['HYDRA_PUBLIC_URL'] . '/.well-known/jwks.json');
      return json_decode($json, true);
    });
  }
}

Authenticator

// src/Security/OlympusAuthenticator.php
namespace App\Security;

use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;

class OlympusAuthenticator extends AbstractAuthenticator {
  public function __construct(private JwksFetcher $jwks) {}

  public function supports(Request $request): bool {
    return $request->headers->has('Authorization');
  }

  public function authenticate(Request $request): SelfValidatingPassport {
    $token = substr($request->headers->get('Authorization'), 7);
    $jwks = JWK::parseKeySet($this->jwks->getKeys());
    $claims = JWT::decode($token, $jwks);
    // Verify issuer
    if ($claims->iss !== $_ENV['HYDRA_PUBLIC_URL']) {
      throw new \Exception('invalid_issuer');
    }
    return new SelfValidatingPassport(new UserBadge($claims->sub));
  }
}

Wire into security.yaml

security:
  firewalls:
    api:
      pattern: ^/api
      stateless: true
      custom_authenticators:
        - App\Security\OlympusAuthenticator

Scope checks

In controllers:

#[Route('/api/orders/{id}', methods: ['DELETE'])]
public function delete(string $id, Security $security) {
  $token = $security->getToken();
  $scopes = explode(' ', $token->getAttribute('scope') ?? '');
  if (!in_array('orders:write', $scopes)) {
    throw new AccessDeniedException();
  }
  // ...
}

On this page