Akka HTTP interop
Akka gRPC is built on top of Akka HTTP. This means it is possible to leverage the Akka HTTP API’s to create more complicated services, for example serving non-gRPC endpoints next to gRPC endpoints or adding additional behavior around your gRPC routes.
Example: authentication/authorization
One use case could be adding cross-cutting concerns such as authentication/authorization. Suppose you have an API that you want to secure using a token. You already have a regular HTTP API that users can use to obtain a token, and want to secure your gRPC routes to only accept calls that include this token.
Akka HTTP authentication route
This route could be any arbitrary Akka HTTP route. For this example we just provide a hint in the response body:
- Scala
-
source
// A Route to authenticate with val authenticationRoute: Route = path("login") { get { complete("Psst, please use token XYZ!") } }
- Java
-
source
// A Route to authenticate with Route authentication = path("login", () -> get(() -> complete("Psst, please use token XYZ!") ) );
Akka gRPC route
We create the Akka gRPC service implementation, and convert it to a Route
Route
as well:
- Scala
-
source
// Create service handlers val handler: HttpRequest => Future[HttpResponse] = GreeterServiceHandler(new GreeterServiceImpl()) // As a Route val handlerRoute: Route = { ctx => handler(ctx.request).map(RouteResult.Complete) }
- Java
-
source
// Instantiate implementation GreeterService impl = new GreeterServiceImpl(mat); Function<HttpRequest, CompletionStage<HttpResponse>> handler = GreeterServiceHandlerFactory.create(impl, sys); // As a Route Route handlerRoute = handle(handler);
Securing the Akka gRPC route
We can wrap the gRPC route just like any Route
Route
, applying the authorization:
- Scala
-
source
// A directive to authorize calls val authorizationDirective: Directive0 = headerValueByName("token").flatMap { token => if (token == "XYZ") pass else reject }
- Java
-
source
// Protect the handler route Route protectedHandler = headerValueByName("token", token -> { if ("XYZ".equals(token)) { return handlerRoute; } else { return complete(StatusCodes.UNAUTHORIZED); } });
Tying it all together
Finally we can combine the routes and serve them. Remember we need to use bindAndHandleAsync
to enable HTTP/2 support:
- Scala
-
source
val route = concat( authenticationRoute, authorizationDirective { handlerRoute }) // Bind service handler servers to localhost:8082 val binding = Http().newServerAt("127.0.0.1", 8082).bind(route)
- Java
-
source
Route finalRoute = concat( authentication, protectedHandler ); return Http.get(sys) .newServerAt("127.0.0.1", 8090) .bind(finalRoute);
Future work
For more in-depth integration you might want to pass information from the Akka HTTP route into your gRPC service implementation.
Currently, you could achieve this by adding the required information to your service implementation constructor, and constructing a new Handler for each request.
In the future we plan to provide a nicer API for this, for example we could pass the Akka HTTP attributes (introduced in 10.2.0) as Metadata when using the PowerApi.