My API clients will pass the session token in the header or query string like:
Http Header with key/value like MyApp-Token abc123
Url:
  https://api.example.com/v1/board?authToken=abc123
 val secureRoutes =
        authenticateToken() { authenticatedContext =>
           path("board") {
             get {
               complete(s"board#index route ${authenticatedContext.user.username}")
             }
           }
       }
Is there a built-in directive that can do this for me or do I have to somehow create my own custom directive that will first look in the HTTP headers, and if not there then look to see if there is a query string value at the key authToken ?
If I have to create a custom directive, any examples I can follow or learn from?
I know I can get a HTTP header using the built-in directive:
          headerValueByName("x-authToken") { authToken =>
            get {
              complete(s"board#index route 2.1 $authToken")
            }
          }
And there is also a directive to get the value from a query string:
  parameter("authToken") { authToken => 
  ...
  }
How could I combine both of them, and then internally I would want to make a database call, and then instead of returning authToken I want to return a custom case class that will contain data I just loaded from the database like:
case class AuthenticatedContext(authToken: String, user: User, ...)
We can always construct a custom directive or compose the directives as shown below:
case class AuthenticatedContext(authToken: String)
  def validateTokenAndGetAuthContext(token: String): Future[AuthenticatedContext] = {
    Future.successful(AuthenticatedContext(token))
  }
  val authToken: Directive1[AuthenticatedContext] = optionalHeaderValueByName("authToken").flatMap {
    case Some(token) =>
      onComplete(validateTokenAndGetAuthContext(token)).flatMap {
        case Failure(_) => reject(AuthorizationFailedRejection)
        case Success(value) => provide(value)
      }
    case None =>
      parameter("authToken".optional).flatMap {
        case Some(token) =>
          onComplete(validateTokenAndGetAuthContext(token)).flatMap {
            case Failure(_) => reject(AuthorizationFailedRejection)
            case Success(value) => provide(value)
          }
        case None => reject(AuthorizationFailedRejection)
      }
  }
It's constructed using HeaderDirectives, ParameterDirectives and FuturesDirectives.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With