authenticateBasicPF

Signature

type AuthenticatorPF[T] = PartialFunction[Credentials, T]
def authenticateBasicPF[T](realm: String, authenticator: AuthenticatorPF[T]): AuthenticationDirective[T]

Description

Wraps the inner route with Http Basic authentication support using a given AuthenticatorPF[T]AuthenticatorPF<T>.

Provides support for handling HTTP Basic Authentication.

Refer to authenticateBasic for a detailed description of this directive.

Its semantics are equivalent to authenticateBasicPF ’s, where not handling a case in the Partial Function (PF) leaves the request to be rejected with a AuthenticationFailedRejectionAuthenticationFailedRejection rejection.

Longer-running authentication tasks (like looking up credentials in a database) should use authenticateBasicAsync or authenticateBasicPFAsync if you prefer to use the PartialFunction syntax.

See Credentials and password timing attacks for details about verifying the secret.

Warning

Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext.

Example

Scala
val myUserPassAuthenticator: AuthenticatorPF[String] = {
  case p @ Credentials.Provided(id) if p.verify("p4ssw0rd")         => id
  case p @ Credentials.Provided(id) if p.verify("p4ssw0rd-special") => s"$id-admin"
}

val route =
  Route.seal {
    path("secured") {
      authenticateBasicPF(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 validAdminCredentials = BasicHttpCredentials("John", "p4ssw0rd-special")
Get("/secured") ~> addCredentials(validAdminCredentials) ~> // adds Authorization header
  route ~> check {
    responseAs[String] shouldEqual "The user is 'John-admin'"
  }

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
import akka.http.javadsl.server.directives.SecurityDirectives.ProvidedCredentials;

import static akka.http.javadsl.server.Directives.authenticateBasicPF;
import static akka.http.javadsl.server.Directives.complete;
import static akka.http.javadsl.server.Directives.path;

final PartialFunction<Optional<ProvidedCredentials>, String> myUserPassAuthenticator =
  new JavaPartialFunction<Optional<ProvidedCredentials>, String>() {
    @Override
    public String apply(Optional<ProvidedCredentials> opt, boolean isCheck) throws Exception {
      if (opt.filter(c -> (c != null) && c.verify("p4ssw0rd")).isPresent()) {
        if (isCheck) return null;
        else return opt.get().identifier();
      } else if (opt.filter(c -> (c != null) && c.verify("p4ssw0rd-special")).isPresent()) {
        if (isCheck) return null;
        else return opt.get().identifier() + "-admin";
      } else {
        throw noMatch();
      }
    }
  };

final Route route = path("secured", () ->
  authenticateBasicPF("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 validAdminCredentials =
  BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd-special");
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validAdminCredentials))
  .assertEntity("The user is 'John-admin'");

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");
Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.