authenticateOrRejectWithChallenge

Signature

type AuthenticationResult[+T] = Either[HttpChallenge, T]
def authenticateOrRejectWithChallenge[T]( authenticator: Option[HttpCredentials] 
def authenticateOrRejectWithChallenge[C <: HttpCredentials: ClassTag, T]( authenticator: Option[C] 

Description

Lifts an authenticator function into a directive.

This directive allows implementing the low level challenge-response type of authentication that some services may require.

More details about challenge-response authentication are available in the RFC 2617, RFC 7616 and RFC 7617.

Example

Scala
sourceval challenge = HttpChallenge("MyAuth", Some("MyRealm"))

// your custom authentication logic:
def auth(creds: HttpCredentials): Boolean = true

def myUserPassAuthenticator(credentials: Option[HttpCredentials]): Future[AuthenticationResult[String]] =
  Future {
    credentials match {
      case Some(creds) if auth(creds) => Right("some-user-name-from-creds")
      case _                          => Left(challenge)
    }
  }

val route =
  Route.seal {
    path("secured") {
      authenticateOrRejectWithChallenge(myUserPassAuthenticator _) { userName =>
        complete("Authenticated!")
      }
    }
  }

// tests:
Get("/secured") ~> route ~> check {
  status shouldEqual StatusCodes.Unauthorized
  responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request"
  header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("MyAuth", Some("MyRealm"))
}

val validCredentials = BasicHttpCredentials("John", "p4ssw0rd")
Get("/secured") ~> addCredentials(validCredentials) ~> // adds Authorization header
route ~> check {
  status shouldEqual StatusCodes.OK
  responseAs[String] shouldEqual "Authenticated!"
}
Java
sourceimport org.apache.pekko.http.javadsl.model.headers.HttpChallenge;
import org.apache.pekko.http.javadsl.model.headers.HttpCredentials;

import static org.apache.pekko.http.javadsl.server.Directives.authenticateOrRejectWithChallenge;
import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.path;

final HttpChallenge challenge = HttpChallenge.create("MyAuth", new Option.Some<>("MyRealm"));

// your custom authentication logic:
final Function<HttpCredentials, Boolean> auth = credentials -> true;

final Function<Optional<HttpCredentials>, CompletionStage<Either<HttpChallenge, String>>>
    myUserPassAuthenticator =
        opt -> {
          if (opt.isPresent() && auth.apply(opt.get())) {
            return CompletableFuture.completedFuture(Right.apply("some-user-name-from-creds"));
          } else {
            return CompletableFuture.completedFuture(Left.apply(challenge));
          }
        };

final Route route =
    path(
            "secured",
            () ->
                authenticateOrRejectWithChallenge(
                    myUserPassAuthenticator, userName -> complete("Authenticated!")))
        .seal();

// tests:
testRoute(route)
    .run(HttpRequest.GET("/secured"))
    .assertStatusCode(StatusCodes.UNAUTHORIZED)
    .assertEntity(
        "The resource requires authentication, which was not supplied with the request")
    .assertHeaderExists("WWW-Authenticate", "MyAuth realm=\"MyRealm\"");

final HttpCredentials validCredentials =
    BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
testRoute(route)
    .run(HttpRequest.GET("/secured").addCredentials(validCredentials))
    .assertStatusCode(StatusCodes.OK)
    .assertEntity("Authenticated!");