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]
AsyncAuthenticator<T>
- function from Optional<ProvidedCredentials>
to CompletionStage<Optional<T>>
.
This variant of the authenticateBasic directive returns a Future[Option[T]]
CompletionStage<Optional<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
an empty Optional
the request is rejected with a AuthenticationFailedRejection
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
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
- Scala
-
source
def 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")) }
- Java
-
source
import 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");