|
@@ -0,0 +1,536 @@
|
|
|
|
|
+import play.api.test.Helpers._
|
|
|
|
|
+import play.api.test.{FakeRequest,WithApplication}
|
|
|
|
|
+import play.api.mvc.{Results,Headers}
|
|
|
|
|
+import play.api.libs.json.{JsObject,Json}
|
|
|
|
|
+import org.scalatest.BeforeAndAfterAll
|
|
|
|
|
+import org.scalatest.tagobjects.Slow
|
|
|
|
|
+import org.scalatestplus.play._
|
|
|
|
|
+import org.scalatestplus.play.guice.GuiceOneServerPerSuite
|
|
|
|
|
+import org.sample.user.shared.models.{User,UserAuthorization}
|
|
|
|
|
+import scala.concurrent.duration._
|
|
|
|
|
+import java.util.Base64
|
|
|
|
|
+
|
|
|
|
|
+class OAuthSpec extends PlaySpec
|
|
|
|
|
+ with BeforeAndAfterAll
|
|
|
|
|
+ with Results
|
|
|
|
|
+ with GuiceOneServerPerSuite {
|
|
|
|
|
+
|
|
|
|
|
+ val email = "tuser@sample.org"
|
|
|
|
|
+ val password = "password"
|
|
|
|
|
+ val fname = "test"
|
|
|
|
|
+ val lname = "user"
|
|
|
|
|
+
|
|
|
|
|
+ val users = Seq(
|
|
|
|
|
+ (User(0, "test", "user", "tuser@sample.org"), "password"),
|
|
|
|
|
+ (User(0, "another", "user", "usert@sample.org"), "password")
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ implicit class CSRFWrapper[T](requ: FakeRequest[T]) {
|
|
|
|
|
+ def withCSRFToken() = requ.withHeaders(
|
|
|
|
|
+ requ.headers.add(("Csrf-Token", "nocheck"))
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ def makeAuthHeader(username: String, password: String) =
|
|
|
|
|
+ Headers(("Authorization" -> s"Basic ${new String(Base64.getEncoder.encode(s"$username:$password".getBytes))}"))
|
|
|
|
|
+
|
|
|
|
|
+ def makeAuthHeader(auth: UserAuthorization) =
|
|
|
|
|
+ Headers(("Authorization" -> s"${auth.tokenType} ${auth.accessToken}"))
|
|
|
|
|
+
|
|
|
|
|
+ override def beforeAll = {
|
|
|
|
|
+ for ((User(_, fname, lname, email), password) <- users) {
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(PUT, "/user/").withJsonBody(
|
|
|
|
|
+ Json.parse(s"""|{
|
|
|
|
|
+ | "fname": "$fname",
|
|
|
|
|
+ | "lname": "$lname",
|
|
|
|
|
+ | "email": "$email",
|
|
|
|
|
+ | "password": "$password"
|
|
|
|
|
+ |}""".stripMargin)
|
|
|
|
|
+ ).withCSRFToken)
|
|
|
|
|
+ status(resp) mustEqual OK
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "the token endpoint" should {
|
|
|
|
|
+ "reject an empty request" in {
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual BAD_REQUEST
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_request\", "+
|
|
|
|
|
+ "error_description=\"required parameter: grant_type\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject an improperly formatted request" in {
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withBody("""{"grant_type" "password"}""")
|
|
|
|
|
+ .withHeaders(
|
|
|
|
|
+ makeAuthHeader(email, password).add(("Content-Type", "application/json"))
|
|
|
|
|
+ ).withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual BAD_REQUEST
|
|
|
|
|
+ // Won't get an OAuth response header. Will fail before processing.
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject a missing grant_type" in {
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("{}"))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual BAD_REQUEST
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_request\", "+
|
|
|
|
|
+ "error_description=\"required parameter: grant_type\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "the token endpoint for password grants" should {
|
|
|
|
|
+ "accept a valid login" in {
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual OK
|
|
|
|
|
+ contentAsJson(resp).as[UserAuthorization] must matchPattern {
|
|
|
|
|
+ case UserAuthorization(_, _, _, _) =>
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "issue unique access tokens for successive logins" in {
|
|
|
|
|
+ val requ = FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ val Some(resp1) = route(app, requ)
|
|
|
|
|
+ val Some(resp2) = route(app, requ)
|
|
|
|
|
+ status(resp2) mustEqual OK
|
|
|
|
|
+ val resp1Auth = contentAsJson(resp1).as[UserAuthorization]
|
|
|
|
|
+ val resp2Auth = contentAsJson(resp2).as[UserAuthorization]
|
|
|
|
|
+ resp1Auth must matchPattern {
|
|
|
|
|
+ case UserAuthorization(_, _, _, _) =>
|
|
|
|
|
+ }
|
|
|
|
|
+ resp2Auth must matchPattern {
|
|
|
|
|
+ case UserAuthorization(_, _, _, _) =>
|
|
|
|
|
+ }
|
|
|
|
|
+ resp1Auth.accessToken mustNot equal(resp2Auth.accessToken)
|
|
|
|
|
+ resp1Auth.refreshToken mustNot equal(resp2Auth.refreshToken)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject an unregistered user" in {
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(
|
|
|
|
|
+ "_"+users(0)._1.email,
|
|
|
|
|
+ "_"+users(0)._2
|
|
|
|
|
+ ))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_client\", "+
|
|
|
|
|
+ "error_description=\"Invalid client or client is not authorized\""))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject an incorrect password" in {
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, "_"+users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_client\", "+
|
|
|
|
|
+ "error_description=\"Invalid client or client is not authorized\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject an incorrect username" in {
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader("_"+users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_client\", "+
|
|
|
|
|
+ "error_description=\"Invalid client or client is not authorized\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject an requests without Authorization headers" in {
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""{"grant_type": "password"}""")
|
|
|
|
|
+ ).withCSRFToken)
|
|
|
|
|
+ status(resp) mustEqual BAD_REQUEST
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_request\", "+
|
|
|
|
|
+ "error_description=\"Client credential is not found\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "the token endpoint for refresh grants" should {
|
|
|
|
|
+ "accept a given refresh token" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual OK
|
|
|
|
|
+ val newAuth = contentAsJson(resp).as[UserAuthorization]
|
|
|
|
|
+ newAuth must matchPattern {
|
|
|
|
|
+ case UserAuthorization(_, _, _, _) =>
|
|
|
|
|
+ }
|
|
|
|
|
+ newAuth.accessToken mustNot equal(auth.accessToken)
|
|
|
|
|
+ newAuth.refreshToken mustNot equal(auth.refreshToken)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject a given refresh token on the second attempt" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+
|
|
|
|
|
+ val Some(resp1) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp1) mustEqual OK
|
|
|
|
|
+ contentAsJson(resp1).as[UserAuthorization] must matchPattern {
|
|
|
|
|
+ case UserAuthorization(_, _, _, _) =>
|
|
|
|
|
+ }
|
|
|
|
|
+ val Some(resp2) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp2) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp2) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_client\", "+
|
|
|
|
|
+ "error_description=\"Invalid client or client is not authorized\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject a revoked refresh token" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+ val Some(resp1) = route(app, FakeRequest(DELETE, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(auth))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp1) mustEqual OK
|
|
|
|
|
+ val Some(resp2) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp2) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp2) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_client\", "+
|
|
|
|
|
+ "error_description=\"Invalid client or client is not authorized\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject a given refresh token with an incorrect username" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "_${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_client\", "+
|
|
|
|
|
+ "error_description=\"Invalid client or client is not authorized\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject an unprovided refresh token" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+ val fakeToken = (auth.refreshToken.charAt(0)+1) +
|
|
|
|
|
+ auth.refreshToken.substring(1);
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "$fakeToken"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_client\", "+
|
|
|
|
|
+ "error_description=\"Invalid client or client is not authorized\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "the secured endpoint" should {
|
|
|
|
|
+ "accept an authorized request" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(email, password))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(GET, "/user/self/name/")
|
|
|
|
|
+ .withHeaders(makeAuthHeader(auth))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual OK
|
|
|
|
|
+ contentAsString(resp) must
|
|
|
|
|
+ equal(s"${users(0)._1.fname} ${users(0)._1.lname}")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject an unauthorized request" in {
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(GET, "/user/self/name/")
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual BAD_REQUEST
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_request\", "+
|
|
|
|
|
+ "error_description=\"Access token is not found\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject an forged authorized request" in {
|
|
|
|
|
+ val fakeToken = "7pKNy790TV5lKVjQw3k/pwJmMS8XBhHaLTVaI6ftd5M="
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(GET, "/user/self/name/")
|
|
|
|
|
+ .withHeaders(("Authorization" -> s"Bearer $fakeToken"))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_token\", "+
|
|
|
|
|
+ "error_description=\"The access token is not found\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject a refreshed access token" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+ val Some(resp1) = route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp1) mustEqual OK
|
|
|
|
|
+ val Some(resp2) = route(app, FakeRequest(GET, "/user/self/name/")
|
|
|
|
|
+ .withHeaders(makeAuthHeader(auth))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp2) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp2) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_token\", "+
|
|
|
|
|
+ "error_description=\"The access token is invalid\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject a revoked access token" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+ val Some(resp1) = route(app, FakeRequest(DELETE, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(auth))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp1) mustEqual OK
|
|
|
|
|
+ val Some(resp2) = route(app, FakeRequest(GET, "/user/self/name/")
|
|
|
|
|
+ .withHeaders(makeAuthHeader(auth))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp2) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp2) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_token\", "+
|
|
|
|
|
+ "error_description=\"The access token is invalid\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /*
|
|
|
|
|
+ "reject an expired access token" taggedAs(Slow) in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(email, password))
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+ Thread.sleep((1 hour).toMillis)
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(GET, "/user/self/name/")
|
|
|
|
|
+ .withHeaders(makeAuthHeader(auth)))
|
|
|
|
|
+ status(resp) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_token\", "+
|
|
|
|
|
+ "error_description=\"The access token is invalid\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "the token endpoint for revokation requests" should {
|
|
|
|
|
+ "accept a valid request" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(DELETE, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(auth))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual OK
|
|
|
|
|
+ contentAsString(resp) mustEqual("")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject a request with an invalid authorization header" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+ val fakeToken = (auth.accessToken.charAt(0)+1) +
|
|
|
|
|
+ auth.accessToken.substring(1);
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(DELETE, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withHeaders("Authorization" -> s"Bearer $fakeToken")
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_token\", "+
|
|
|
|
|
+ "error_description=\"The access token is not found\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject a request without an authorization header" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(DELETE, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual BAD_REQUEST
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_request\", "+
|
|
|
|
|
+ "error_description=\"Access token is not found\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject a request with an unissued refresh token" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+ val fakeToken = (auth.refreshToken.charAt(0)+1) +
|
|
|
|
|
+ auth.refreshToken.substring(1);
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(DELETE, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${fakeToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(auth))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_token\", "+
|
|
|
|
|
+ "error_description=\"The access token is invalid\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ "reject a request with an incorrect username" in {
|
|
|
|
|
+ val auth = contentAsJson(route(app, FakeRequest(POST, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse("""{"grant_type": "password"}"""))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(users(0)._1.email, users(0)._2))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ ).get).as[UserAuthorization]
|
|
|
|
|
+ val Some(resp) = route(app, FakeRequest(DELETE, "/authorize/")
|
|
|
|
|
+ .withJsonBody(Json.parse(s"""|{
|
|
|
|
|
+ | "grant_type": "refresh_token",
|
|
|
|
|
+ | "client_id": "_${users(0)._1.email}",
|
|
|
|
|
+ | "refresh_token": "${auth.refreshToken}"
|
|
|
|
|
+ |}""".stripMargin))
|
|
|
|
|
+ .withHeaders(makeAuthHeader(auth))
|
|
|
|
|
+ .withCSRFToken
|
|
|
|
|
+ )
|
|
|
|
|
+ status(resp) mustEqual UNAUTHORIZED
|
|
|
|
|
+ headers(resp) must contain ("WWW-Authenticate" ->
|
|
|
|
|
+ ("Bearer error=\"invalid_client\", "+
|
|
|
|
|
+ "error_description=\"Invalid client or client is not authorized\"")
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|