Baldinof / roadrunner-bundle

A RoadRunner worker integrated in your Symfony app
MIT License
255 stars 46 forks source link

gRPC interceptor #102

Open p-stoyanov opened 1 year ago

p-stoyanov commented 1 year ago

Hello, I have a grpc service and I want to implement JWT token authentication, for now, I just need to validate that the token is valid. With http this is possible via Symfony authenticator or middleware but with grpc request it doesn't work. As far as I understand, with grpc, this can be achieved through an interceptor, which is an analog of http middleware. Is there a way to implement similar functionality through this bundle?

genhoi commented 1 year ago

@p-stoyanov You can do this through your implementation of the Spiral\RoadRunner\GRPC\InvokerInterface and replace the service with your implementation with the id Spiral\RoadRunner\GRPC\Invoker

For example, like this: services.yaml

    Spiral\RoadRunner\GRPC\Invoker:
        class: App\Infrastructure\Grpc\Roadrunner\Invoker

InvokerInterface implementation

<?php

declare(strict_types=1);

namespace App\Infrastructure\Grpc\Roadrunner;

use Google\Protobuf\Internal\Message;
use Spiral\RoadRunner\GRPC\ContextInterface;
use Spiral\RoadRunner\GRPC\Exception\InvokeException;
use Spiral\RoadRunner\GRPC\InvokerInterface;
use Spiral\RoadRunner\GRPC\Method;
use Spiral\RoadRunner\GRPC\ServiceInterface;
use Spiral\RoadRunner\GRPC\StatusCode;
use Throwable;

class Invoker implements InvokerInterface
{

    /**
     * {@inheritDoc}
     */
    public function invoke(ServiceInterface $service, Method $method, ContextInterface $ctx, ?string $input): string
    {
        /** @var callable $callable */
        $callable = [$service, $method->getName()];

        $arg = $this->makeInput($method, $input);

        // A place to implement the middleware template
        $message = $callable($ctx, $arg);;

        try {
            $message = $message->serializeToString();

            return $message;
        } catch (Throwable $e) {
            throw InvokeException::create($e->getMessage(), StatusCode::INTERNAL, $e);
        }
    }

    /**
     * @throws InvokeException
     */
    private function makeInput(Method $method, ?string $body): Message
    {
        try {
            $class = $method->getInputType();

            /** @psalm-suppress UnsafeInstantiation */
            $in = new $class();

            if (null !== $body) {
                $in->mergeFromString($body);
            }

            return $in;
        } catch (Throwable $e) {
            throw InvokeException::create($e->getMessage(), StatusCode::INTERNAL, $e);
        }
    }
}