authenticateBasicAsync
Signature¶
type AsyncAuthenticator[T] = Credentials => Future[Option[T]]
def authenticateBasicAsync[T](realm: String, authenticator: AsyncAuthenticator[T]): AuthenticationDirective[T]
Description¶
Wraps the inner route with Http Basic authentication support using a given AsyncAuthenticator[T]
.
This variant of the authenticateBasic directive returns a Future[Option[T]]
which allows freeing up the routing layer of Apache Pekko HTTP, freeing it for other requests. It should be used whenever an authentication is expected to take a longer amount of time (e.g. looking up the user in a database).
In case the returned option is None
the request is rejected with a AuthenticationFailedRejection
, which by default is mapped to an 401 Unauthorized
response.
Standard HTTP-based authentication which uses the WWW-Authenticate
header containing challenge data and Authorization
header for receiving credentials is implemented in authenticateOrRejectWithChallenge
.
See Credentials and password timing attacks for details about verifying the secret.
Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext.
Example¶
sourcedef myUserPassAuthenticator(credentials: Credentials): Future[Option[String]] =
credentials match {
case p @ Credentials.Provided(id) =>
Future {
// potentially
if (p.verify("p4ssw0rd")) Some(id)
else None
}
case _ => Future.successful(None)
}
val route =
Route.seal {
path("secured") {
authenticateBasicAsync(realm = "secure site", myUserPassAuthenticator) { userName =>
complete(s"The user is '$userName'")
}
}
}
// 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("Basic", Some("secure site"),
Map("charset" -> "UTF-8"))
}
val validCredentials = BasicHttpCredentials("John", "p4ssw0rd")
Get("/secured") ~> addCredentials(validCredentials) ~> // adds Authorization header
route ~> check {
responseAs[String] shouldEqual "The user is 'John'"
}
val invalidCredentials = BasicHttpCredentials("Peter", "pan")
Get("/secured") ~>
addCredentials(invalidCredentials) ~> // adds Authorization header
route ~> check {
status shouldEqual StatusCodes.Unauthorized
responseAs[String] shouldEqual "The supplied authentication is invalid"
header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", Some("secure site"),
Map("charset" -> "UTF-8"))
}
sourceimport org.apache.pekko.http.javadsl.server.directives.SecurityDirectives.ProvidedCredentials;
import static org.apache.pekko.http.javadsl.server.Directives.authenticateBasicAsync;
import static org.apache.pekko.http.javadsl.server.Directives.complete;
import static org.apache.pekko.http.javadsl.server.Directives.path;
final Function<Optional<ProvidedCredentials>, CompletionStage<Optional<String>>>
myUserPassAuthenticator =
opt -> {
if (opt.filter(c -> (c != null) && c.verify("p4ssw0rd")).isPresent()) {
return CompletableFuture.completedFuture(Optional.of(opt.get().identifier()));
} else {
return CompletableFuture.completedFuture(Optional.empty());
}
};
final Route route =
path(
"secured",
() ->
authenticateBasicAsync(
"secure site",
myUserPassAuthenticator,
userName -> complete("The user is '" + userName + "'")))
.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", "Basic realm=\"secure site\",charset=UTF-8");
final HttpCredentials validCredentials =
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
testRoute(route)
.run(HttpRequest.GET("/secured").addCredentials(validCredentials))
.assertEntity("The user is 'John'");
final HttpCredentials invalidCredentials =
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
testRoute(route)
.run(HttpRequest.GET("/secured").addCredentials(invalidCredentials))
.assertStatusCode(StatusCodes.UNAUTHORIZED)
.assertEntity("The supplied authentication is invalid")
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\",charset=UTF-8");