diff --git a/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala index 1f5e78ca6f..51c8f36209 100644 --- a/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala +++ b/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala @@ -5,12 +5,12 @@ package docs.http.scaladsl.server package directives +import akka.http.scaladsl.model._ +import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.server.Route import akka.http.scaladsl.server.directives.Credentials -import akka.stream.scaladsl.{ FlowGraph, Flow, FlattenStrategy } + import scala.concurrent.Future -import akka.http.scaladsl.model._ -import headers._ class SecurityDirectivesExamplesSpec extends RoutingSpec { @@ -51,6 +51,90 @@ class SecurityDirectivesExamplesSpec extends RoutingSpec { header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", "secure site") } } + "authenticateBasicPF-0" in { + 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'") + } + } + } + + 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", "secure site") + } + + 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", "secure site") + } + } + "authenticateBasicPFAsync-0" in { + case class User(id: String) + def fetchUser(id: String): Future[User] = { + // some fancy logic to obtain a User + Future.successful(User(id)) + } + + val myUserPassAuthenticator: AsyncAuthenticatorPF[User] = { + case p @ Credentials.Provided(id) if p.verify("p4ssw0rd") => + fetchUser(id) + } + + val route = + Route.seal { + path("secured") { + authenticateBasicPFAsync(realm = "secure site", myUserPassAuthenticator) { user => + complete(s"The user is '${user.id}'") + } + } + } + + 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", "secure site") + } + + 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", "secure site") + } + } "authenticateBasicAsync-0" in { def myUserPassAuthenticator(credentials: Credentials): Future[Option[String]] = credentials match { diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/alphabetically.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/alphabetically.rst index 2c9810ec79..4cc016064d 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/directives/alphabetically.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/directives/alphabetically.rst @@ -14,6 +14,14 @@ Directive Description ``AuthenticatorPF[T]`` :ref:`-authenticateBasicPFAsync-` Wraps the inner route with Http Basic authentication support using a given ``AsyncAuthenticatorPF[T]`` +:ref:`-authenticateOAuth2-` Wraps the inner route with OAuth Bearer Token authentication support using + a given ``AuthenticatorPF[T]`` +:ref:`-authenticateOAuth2Async-` Wraps the inner route with OAuth Bearer Token authentication support using + a given ``AsyncAuthenticator[T]`` +:ref:`-authenticateOAuth2PF-` Wraps the inner route with OAuth Bearer Token authentication support using + a given ``AuthenticatorPF[T]`` +:ref:`-authenticateOAuth2AsyncPF-` Wraps the inner route with OAuth Bearer Token authentication support using + a given ``AsyncAuthenticatorPF[T]`` :ref:`-authenticateOrRejectWithChallenge-` Lifts an authenticator function into a directive :ref:`-authorize-` Applies the given authorization check to the request :ref:`-cancelRejection-` Adds a ``TransformationRejection`` cancelling all rejections equal to the diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasic.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasic.rst index 5aeaedf57b..4b5fa385f8 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasic.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasic.rst @@ -25,14 +25,11 @@ which by default is mapped to an ``401 Unauthorized`` response. Longer-running authentication tasks (like looking up credentials in a database) should use the :ref:`-authenticateBasicAsync-` variant of this directive which allows it to run without blocking routing layer of Akka HTTP, freeing it for other requests. -The ``authenticate*`` directives themselfs are not tied to any HTTP-specific -details so that various authentication schemes can be implemented on top of authenticate. - Standard HTTP-based authentication which uses the ``WWW-Authenticate`` header containing challenge data and ``Authorization`` header for receiving credentials is implemented in subclasses of ``HttpAuthenticator``. .. warning:: - Make sure to use basic authentication only over SSL because credentials are transferred in plaintext. + Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. .. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicAsync.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicAsync.rst index 01c2663772..96c48aee0a 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicAsync.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicAsync.rst @@ -23,14 +23,11 @@ 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 :class:`AuthenticationFailedRejection`, which by default is mapped to an ``401 Unauthorized`` response. -The ``authenticate*`` directives themselfs are not tied to any HTTP-specific -details so that various authentication schemes can be implemented on top of authenticate. - Standard HTTP-based authentication which uses the ``WWW-Authenticate`` header containing challenge data and ``Authorization`` header for receiving credentials is implemented in subclasses of ``HttpAuthenticator``. .. warning:: - Make sure to use basic authentication only over SSL because credentials are transferred in plaintext. + Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. .. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPF.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPF.rst index 5253fcf302..9d2331a565 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPF.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPF.rst @@ -18,15 +18,16 @@ Description Provides support for handling `HTTP Basic Authentication`_. -Refer to :ref:`-authenticateBasic-` for a detailed description of this dictive. -It's semantics are equivalent to ``authenticateBasicPF`` 's, where not handling a case in the Partial Function (PF) +Refer to :ref:`-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 :class:`AuthenticationFailedRejection` rejection. Longer-running authentication tasks (like looking up credentials in a database) should use :ref:`authenticateBasicAsync` or :ref:`-authenticateBasicPFAsync-` if you prefer to use the ``PartialFunction`` syntax. .. warning:: - Make sure to use basic authentication only over SSL because credentials are transferred in plaintext. + Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. .. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth @@ -34,4 +35,4 @@ Example ------- .. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: 0authenticateBasicPF + :snippet: authenticateBasicPF-0 diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPFAsync.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPFAsync.rst index e4d5f0f5a3..486d5f5aba 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPFAsync.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPFAsync.rst @@ -18,15 +18,13 @@ Description Provides support for handling `HTTP Basic Authentication`_. -Refer to :ref:`-authenticateBasic-` for a detailed description of this dictive. -It's semantics are equivalent to ``authenticateBasicPF`` 's, where not handling a case in the Partial Function (PF) +Refer to :ref:`-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 :class:`AuthenticationFailedRejection` rejection. -Longer-running authentication tasks (like looking up credentials in a database) should use :ref:`authenticateBasicAsync` -or :ref:`-authenticateBasicPFAsync-` if you prefer to use the ``PartialFunction`` syntax. - .. warning:: - Make sure to use basic authentication only over SSL because credentials are transferred in plaintext. + Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. .. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth @@ -34,4 +32,4 @@ Example ------- .. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: 0authenticateBasicPFAsync + :snippet: authenticateBasicPFAsync-0 diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2.rst index 1fa1327d2b..68f0dfeaff 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2.rst @@ -3,7 +3,7 @@ authenticateOAuth2 ================== -... +Wraps the inner route with OAuth Bearer Token authentication support using a given ``AuthenticatorPF[T]`` Signature --------- @@ -16,10 +16,26 @@ Signature Description ----------- -... +Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, +which is used to initiate an OAuth2 authorization. + +.. warning:: + This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, + by extracting the needed token from the HTTP headers. + +Given a function returning ``Some[T]`` upon successful authentication and ``None`` otherwise, +respectively applies the inner route or rejects the request with a :class:`AuthenticationFailedRejection` rejection, +which by default is mapped to an ``401 Unauthorized`` response. + +Longer-running authentication tasks (like looking up credentials in a database) should use the :ref:`-authenticateOAuth2Async-` +variant of this directive which allows it to run without blocking routing layer of Akka HTTP, freeing it for other requests. + +For more information on how OAuth2 works see `RFC 6750`_. + +.. _RFC 6750: https://tools.ietf.org/html/rfc6750 Example ------- -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: authenticateOAuth2-0 +Usage in code is exactly the same as :ref:`-authenticateBasic-`, +with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2Async.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2Async.rst index 4a50d3f180..b4a2d16f0c 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2Async.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2Async.rst @@ -3,7 +3,7 @@ authenticateOAuth2Async ======================= -... +Wraps the inner route with OAuth Bearer Token authentication support using a given ``AsyncAuthenticator[T]``. Signature --------- @@ -16,10 +16,26 @@ Signature Description ----------- -... +Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, +which is used to initiate an OAuth2 authorization. + +.. warning:: + This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, + by extracting the needed token from the HTTP headers. + +Given a function returning ``Some[T]`` upon successful authentication and ``None`` otherwise, +respectively applies the inner route or rejects the request with a :class:`AuthenticationFailedRejection` rejection, +which by default is mapped to an ``401 Unauthorized`` response. + +See also :ref:`-authenticateOAuth2-` if the authorization operation is rather quick, and does not have to execute asynchronously. + +For more information on how OAuth2 works see `RFC 6750`_. + +.. _RFC 6750: https://tools.ietf.org/html/rfc6750 + Example ------- -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: authenticateOAuth2Async-0 +Usage in code is exactly the same as :ref:`-authenticateBasicAsync-`, +with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2AsyncPF.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2AsyncPF.rst index 60b25dac7a..e554611ad3 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2AsyncPF.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2AsyncPF.rst @@ -3,7 +3,7 @@ authenticateOAuth2AsyncPF ========================= -... +Wraps the inner route with OAuth Bearer Token authentication support using a given ``AsyncAuthenticatorPF[T]``. Signature --------- @@ -16,10 +16,27 @@ Signature Description ----------- -... +Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, +which is used to initiate an OAuth2 authorization. + +.. warning:: + This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, + by extracting the needed token from the HTTP headers. + +Refer to :ref:`-authenticateOAuth2-` for a detailed description of this directive. + +Its semantics are equivalent to ``authenticateOAuth2PF`` 's, where not handling a case in the Partial Function (PF) +leaves the request to be rejected with a :class:`AuthenticationFailedRejection` rejection. + +See also :ref:`-authenticateOAuth2PF-` if the authorization operation is rather quick, and does not have to execute asynchronously. + +For more information on how OAuth2 works see `RFC 6750`_. + +.. _RFC 6750: https://tools.ietf.org/html/rfc6750 + Example ------- -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: authenticateOAuth2AsyncPF-0 +Usage in code is exactly the same as :ref:`-authenticateBasicAsyncPF-`, +with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2PF.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2PF.rst index 9d7f7001bb..392d7e8c91 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2PF.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2PF.rst @@ -3,7 +3,7 @@ authenticateOAuth2PF ==================== -... +Wraps the inner route with OAuth Bearer Token authentication support using a given ``AuthenticatorPF[T]``. Signature --------- @@ -16,10 +16,27 @@ Signature Description ----------- -... +Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, +which is used to initiate an OAuth2 authorization. + +.. warning:: + This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, + by extracting the needed token from the HTTP headers. + +Refer to :ref:`-authenticateOAuth2-` for a detailed description of this directive. + +Its semantics are equivalent to ``authenticateOAuth2PF`` 's, where not handling a case in the Partial Function (PF) +leaves the request to be rejected with a :class:`AuthenticationFailedRejection` rejection. + +Longer-running authentication tasks (like looking up credentials in a database) should use the :ref:`-authenticateOAuth2Async-` +variant of this directive which allows it to run without blocking routing layer of Akka HTTP, freeing it for other requests. + +For more information on how OAuth2 works see `RFC 6750`_. + +.. _RFC 6750: https://tools.ietf.org/html/rfc6750 Example ------- -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: authenticateOAuth2PF-0 +Usage in code is exactly the same as :ref:`-authenticateBasicPF-`, +with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/index.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/index.rst index 1fc1f62049..8bc3a3a7c6 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/index.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/directives/security-directives/index.rst @@ -11,6 +11,11 @@ SecurityDirectives authenticateBasicPF authenticateBasicPFAsync authenticateOrRejectWithChallenge + authenticateOAuth2 + authenticateOAuth2Async + authenticateOAuth2PF + authenticateOAuth2PFAsync + authenticateOrRejectWithChallenge authorize extractCredentials @@ -50,4 +55,12 @@ credentials. You can see the currently registered schemes at http://www.iana.org At this point Akka HTTP only implements the "'Basic' HTTP Authentication Scheme" whose most current specification can be found here: https://datatracker.ietf.org/doc/draft-ietf-httpauth-basicauth-update/. -.. _RFC 7235: http://tools.ietf.org/html/rfc7235 \ No newline at end of file +.. _RFC 7235: http://tools.ietf.org/html/rfc7235 + +Low-level OAuth2 "Bearer Token" directives +------------------------------------------ +The OAuth2 directives currently provided in Akka HTTP are not a full OAuth2 protocol implementation, +they are only a means of extracting the so called ``Bearer Token`` from the ``Authorization`` HTTP Header, +as defined in `RFC 6750`_, and allow users to validate and complete the protocol. + +.. _RFC 6750: https://tools.ietf.org/html/rfc6750