authenticateOrRejectWithChallenge • Akka HTTP (original) (raw)

Signature

type AuthenticationResult[+T] = Either[HttpChallenge, T]
def authenticateOrRejectWithChallenge[T](authenticator: Option[HttpCredentials] => Future[AuthenticationResult[T]]): AuthenticationDirective[T]

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

source`` val 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

source`import akka.http.javadsl.model.headers.HttpChallenge; import akka.http.javadsl.model.headers.HttpCredentials;

import static akka.http.javadsl.server.Directives.authenticateOrRejectWithChallenge; import static akka.http.javadsl.server.Directives.complete; import static akka.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, 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!");`

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.