Akka http custom directive - async

Hello there,

I am trying to write a custom directive in akka http for something like authorize (with a bit of difference from what’s available OOTB).

object AuthenticationUtils {
  def generateUserRequest(implicit system: ActorSystem, executionContext: ExecutionContext): Directive1[UserRequest] = {
    optionalHeaderValueByName("Authorization").flatMap {
      case Some(tokenValue) if Token.isValidTokenHeader(tokenValue) =>
        val token = Token.fromHeader(tokenValue).get
        val selection = system.actorSelection("myauthenticator-actor-selection-here")
        implicit val timeout: Timeout = Timeout(2 minutes) //may be implicit timeout?
        val future = (selection ? RedisTokenActor.GenerateUserRequest(token)).mapTo[GenerateUserRequestResponse]
            .map { r => r.optUserRequest}
        /*onSuccess(future) { //this does not work.
          case Some(ur) => provide(ur)
          case _ => reject(AuthorizationFailedRejection)
        }*/
        val res = Await.result(future, timeout.duration) //todo: make this async.
        res match {
          case Some(ur) => provide(ur)
          case _ => reject(AuthorizationFailedRejection)
        }

      case _ =>  reject(AuthorizationFailedRejection)
    }
  }
}

My question is that, how do I async the execution of the future within a directive? When I use onSuccess, the return objects are not compatible.
Please advice on how do I resolve this. When I use it synchronously, the above code works as desired.

Thanks
Muthu

Found the answer to my question :). Thank you for looking and helping me to answer :).

  def generateUserRequest(implicit system: ActorSystem, executionContext: ExecutionContext): Directive1[UserRequest] = {
    optionalHeaderValueByName("Authorization").flatMap {
      case Some(tokenValue) if Token.isValidTokenHeader(tokenValue) =>
        val token = Token.fromHeader(tokenValue).get
        val selection = system.actorSelection("myauthenticator-actor-selection-here")
        implicit val timeout: Timeout = Timeout(2 minutes) //may be implicit timeout?
        val future = (selection ? RedisTokenActor.GenerateUserRequest(token)).mapTo[GenerateUserRequestResponse]
            .map { r => r.optUserRequest}
        future.onSuccess(future).flatMap { 
          case Some(ur) => provide(ur)
          case _ => reject(AuthorizationFailedRejection)
        }*/
    
      case _ =>  reject(AuthorizationFailedRejection)
    }
  }
}

missed out flatMap for onSuccess().

There must still be something wrong, because: future.onSuccess(future) does not make sense. And there is a trailing */ in there :)

There must still be something wrong, because: future.onSuccess(future) does not make sense.

I have some actor that does some looking up of data source and generates some sort of UserRequest that can be used by some downstream directive (which is sent as part of provide(…)).
The reject directive would fail fast.
This is almost like authorize directive (part of SecurityDirective) with an addition of transforming the token into some sort of business object.
Perhaps your question is more of suggesting a better way of doing this?

And there is a trailing */ in there :slight_smile:

Ah… yes. Minus the typo from copying the code, it does work :). Thanks Johanandren!

Another question… how do I have a child directive pass some values back to parent directive?
For example, say I use DebuggingDirectives where I can intercept HttpRequest & Complete/Reject. How could I have some downstream directive like Path to return back some “business context” that can be used by this directive?

for example,

get {
 myCustomLogReqResp {
   path("some_useful_path") {
     //provide some context to logger so that when I send log the req+resp I can also log this context.
     complete("some useful response")
   }
 }
}

Please advice,
Muthu

Did you see this section in the Akka HTTP docs on creating custom directives? https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/custom-directives.html

Hello Jonandren,

Thanks for the tip. I am trying to put up a custom directive for my step of “myCustomLogReqResp” (based on logRequestResult from https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/debugging-directives/logRequestResult.html).
But, I would only have handle to RequestContext (for HttpRequest) & RouteResult (for HttpResponse / reject).
I did want to add logging on the relative URI used.
For example, say

myCustomLogReqResp {
   path("p1" / IntValue) { i =>
     complete("some useful $i")
   }
}

the URI from the HttpRequest would be the complete path. In my case, I am looking to intercept the PathMatcher from path directive and get “p1/{IntValue}”.
I was thinking of “writing some mutable object if I cannot directly access the PathMatcher value from myCustomLogReqResp directive and wrap the path directive to intercept the PathMatcher.”… I suppose I m missing something simple :).
But even if I get handle to PathMatcher, is there is an generic extractor that would provide me something like “p1/{IntValue}” so that I may be able to use this logging for any new path directive added?

Please advice,
Muthu

Not sure I understand what the question is.

Is it that you want the directive to extract how much of the path was matched? In the RequestContext you have a unmatchedPath field, that should let you figure that out. I think your directive would have to go inside the path though.