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
// A Route to authenticate with
val authenticationRoute: Route = path("login") {
  get {
    complete("Psst, please use token XYZ!")
  }
}
Java
// 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 RouteRoute as well:

Scala
// 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
// Instantiate implementation
GreeterService impl = new GreeterServiceImpl(mat);
Function<HttpRequest, CompletionStage<HttpResponse>> handler = GreeterServiceHandlerFactory.create(impl, sys);

// As a Route
Route handlerRoute = RouteUtils.fromFunction(handler, sys);

Securing the Akka gRPC route

We can wrap the gRPC route just like any RouteRoute, applying the authorization:

Scala
// A directive to authorize calls
val authorizationDirective: Directive0 =
  headerValueByName("token").flatMap { token =>
    if (token == "XYZ") pass
    else reject
  }
Java
// 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
val route = concat(
  authenticationRoute,
  authorizationDirective {
    handlerRoute
  })

// Bind service handler servers to localhost:8082
val binding = Http2().bindAndHandleAsync(
  Route.asyncHandler(route),
  interface = "127.0.0.1",
  port = 8082,
  connectionContext = HttpConnectionContext())
Java
Route finalRoute = concat(
  authentication,
  protectedHandler
);

return Http.get(sys).bindAndHandleAsync(
  RouteUtils.toFunction(finalRoute, sys),
  ConnectHttp.toHost("127.0.0.1", 8090),
  mat);

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.

Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.