From b34a3b7b2cd712d74609beeb0c783e5569e4415b Mon Sep 17 00:00:00 2001 From: rurik Date: Tue, 22 Mar 2016 12:47:06 +0300 Subject: [PATCH 1/6] Vkontakte provider and example of usage --- .../app/controllers/Application.scala | 55 ++++++++-- social-sample/app/models/User.scala | 40 ++++++- social-sample/app/views/index.scala.html | 21 +++- social-sample/conf/application.conf | 4 + .../migration/default/V1__create_tables.sql | 8 ++ social-sample/conf/routes | 6 +- .../vkontakte/VkontakteAuthenticator.scala | 101 ++++++++++++++++++ .../vkontakte/VkontakteController.scala | 17 +++ .../VkontakteProviderUserSupport.scala | 55 ++++++++++ .../providers/vkontakte/VkontakteUser.scala | 12 +++ 10 files changed, 307 insertions(+), 12 deletions(-) create mode 100644 social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala create mode 100644 social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteController.scala create mode 100644 social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala create mode 100644 social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteUser.scala diff --git a/social-sample/app/controllers/Application.scala b/social-sample/app/controllers/Application.scala index 9a2e7a7..62fda52 100644 --- a/social-sample/app/controllers/Application.scala +++ b/social-sample/app/controllers/Application.scala @@ -1,17 +1,15 @@ package controllers -import models._ -import play.api.mvc.Results._ -import play.api.mvc._ -import scalikejdbc.DB +import controllers.providers.vkontakte.{VkontakteController, VkontakteProviderUserSupport} +import models.{User, VkontakteUser, _} import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.{ ExecutionContext, Future } -import scala.reflect.{ ClassTag, classTag } +import scala.concurrent.{ExecutionContext, Future} +import scala.reflect.{ClassTag, classTag} import jp.t2v.lab.play2.auth._ -import jp.t2v.lab.play2.auth.social.providers.twitter.{TwitterProviderUserSupport, TwitterController} -import jp.t2v.lab.play2.auth.social.providers.facebook.{FacebookProviderUserSupport, FacebookController} -import jp.t2v.lab.play2.auth.social.providers.github.{GitHubProviderUserSupport, GitHubController} +import jp.t2v.lab.play2.auth.social.providers.twitter.{TwitterController, TwitterProviderUserSupport} +import jp.t2v.lab.play2.auth.social.providers.facebook.{FacebookController, FacebookProviderUserSupport} +import jp.t2v.lab.play2.auth.social.providers.github.{GitHubController, GitHubProviderUserSupport} import jp.t2v.lab.play2.auth.social.providers.slack.SlackController object Application extends Controller with OptionalAuthElement with AuthConfigImpl with Logout { @@ -170,3 +168,42 @@ object SlackAuthController extends SlackController } } + + +object VkontakteAuthController extends VkontakteController + with AuthConfigImpl + with VkontakteProviderUserSupport { + + override def onOAuthLinkSucceeded(token: AccessToken, consumerUser: User)(implicit request: RequestHeader, ctx: ExecutionContext): Future[Result] = { + retrieveProviderUser(token).map { providerUser => + DB.localTx { implicit session => + VkontakteUser.save(consumerUser.id, providerUser) + Redirect(routes.Application.index) + } + } + } + + override def onOAuthLoginSucceeded(token: AccessToken)(implicit request: RequestHeader, ctx: ExecutionContext): Future[Result] = { + retrieveProviderUser(token).flatMap { providerUser => + DB.localTx { implicit session => + VkontakteUser.findById(providerUser.id) match { + case None => + val id = User.create(providerUser.first_name, providerUser.coverUrl).id + VkontakteUser.save(id, providerUser) + gotoLoginSucceeded(id) + case Some(fu) => + gotoLoginSucceeded(fu.userId) + } + } + } + } + + + def onAuthSucces(): Unit = { + + } + + +} + + diff --git a/social-sample/app/models/User.scala b/social-sample/app/models/User.scala index 08c88f5..49c721e 100644 --- a/social-sample/app/models/User.scala +++ b/social-sample/app/models/User.scala @@ -1,7 +1,7 @@ package models +import controllers.providers.vkontakte import jp.t2v.lab.play2.auth.social.providers -import scalikejdbc._ import jp.t2v.lab.play2.auth.social.providers.{facebook, twitter} sealed trait Authority @@ -35,6 +35,15 @@ case class TwitterUser( case class SlackAccessToken( userId: Long, accessToken: String) + + +case class VkontakteUser( + userId: Long, + id: String, + name: String, + coverUrl: String, + accessToken: String) + object User { def *(rs: WrappedResultSet) = User( @@ -165,4 +174,33 @@ object SlackAccessToken { SlackAccessToken(userId, accessToken) } +} + +object VkontakteUser { + + def *(rs: WrappedResultSet) = VkontakteUser( + rs.long("user_id"), + rs.string("id"), + rs.string("name"), + rs.string("cover_url"), + rs.string("access_token") + ) + + def findById(id: String)(implicit session: DBSession): Option[VkontakteUser] = { + sql"SELECT * FROM vkontakte_users WHERE id = $id".map(*).single().apply() + } + + def findByUserId(userId: Long)(implicit session: DBSession): Option[VkontakteUser] = { + sql"SELECT * FROM vkontakte_users WHERE user_id = $userId".map(*).single().apply() + } + + def save(userId: Long, vkontakteUser: vkontakte.VkontakteUser)(implicit session: DBSession): VkontakteUser = { + val id = vkontakteUser.id + val first_name = vkontakteUser.first_name + val coverUrl = vkontakteUser.coverUrl + val accessToken = vkontakteUser.accessToken + sql"INSERT INTO vkontakte_users(user_id, id, name, cover_url, access_token) VALUES ($userId, $id, $first_name, $coverUrl, $accessToken)".update.apply() + VkontakteUser(userId, id, first_name, coverUrl, accessToken) + } + } \ No newline at end of file diff --git a/social-sample/app/views/index.scala.html b/social-sample/app/views/index.scala.html index 1a5e2a1..672b916 100644 --- a/social-sample/app/views/index.scala.html +++ b/social-sample/app/views/index.scala.html @@ -2,7 +2,8 @@ gitHubUser: Option[GitHubUser], facebookUser: Option[FacebookUser], twitterUser: Option[TwitterUser], - slackAccessToken: Option[SlackAccessToken]) + slackAccessToken: Option[SlackAccessToken], + vkontakteAccessToken: Option[VkontakteUser]) @@ -98,6 +99,21 @@

Slack

} +
+

Vkontakte

+ + @vkontakteUser.map { u => +

+ @u.name +

+

+ +

+ }.getOrElse { + link Vkontakte + } +
+ }.getOrElse { @@ -110,6 +126,9 @@

Slack

facebook login

+

+ vkontakte login +

} diff --git a/social-sample/conf/application.conf b/social-sample/conf/application.conf index ed2fd53..f04f16c 100644 --- a/social-sample/conf/application.conf +++ b/social-sample/conf/application.conf @@ -16,6 +16,10 @@ slack.clientId = ${SLACK_CLIENT_ID} slack.clientSecret = ${SLACK_CLIENT_SECRET} slack.callbackURL = ${SLACK_CALLBACK_URL} +vkontakte.clientId = ${VKONTAKTE_CLIENT_ID} +vkontakte.clientSecret = ${VKONTAKTE_CLIENT_SECRET} +vkontakte.callbackURL =${VKONTAKTE_CALLBACK_URL} + logger.com.github.tototoshi.play.social = DEBUG db.default.driver=org.h2.Driver diff --git a/social-sample/conf/db/migration/default/V1__create_tables.sql b/social-sample/conf/db/migration/default/V1__create_tables.sql index 49c0ee1..f1f6aad 100644 --- a/social-sample/conf/db/migration/default/V1__create_tables.sql +++ b/social-sample/conf/db/migration/default/V1__create_tables.sql @@ -32,4 +32,12 @@ CREATE TABLE facebook_users ( CREATE TABLE slack_access_token ( user_id INTEGER NOT NULL UNIQUE REFERENCES users(id), access_token VARCHAR(1000) NOT NULL +); + +CREATE TABLE vkontakte_users ( + user_id INTEGER NOT NULL UNIQUE REFERENCES users(id), + id VARCHAR(100) PRIMARY KEY, + name VARCHAR(100) NOT NULL, + cover_url VARCHAR(1000) NOT NULL, + access_token VARCHAR(1000) NOT NULL ); \ No newline at end of file diff --git a/social-sample/conf/routes b/social-sample/conf/routes index 2857fdc..5fbaedd 100644 --- a/social-sample/conf/routes +++ b/social-sample/conf/routes @@ -14,4 +14,8 @@ GET /link/facebook controllers.FacebookAuthController.link(sc GET /authorize/facebook controllers.FacebookAuthController.authorize GET /link/slack controllers.SlackAuthController.link(scope: String) -GET /authorize/slack controllers.SlackAuthController.authorize \ No newline at end of file +GET /authorize/slack controllers.SlackAuthController.authorize + +GET /login/vkontakte controllers.VkontakteAuthController.login(scope: String) +GET /link/vkontakte controllers.VkontakteAuthController.link(scope: String) +GET /authorize/vkontakte controllers.VkontakteAuthController.authorize \ No newline at end of file diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala new file mode 100644 index 0000000..7de2071 --- /dev/null +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala @@ -0,0 +1,101 @@ +package controllers.providers.vkontakte + +/** + * Created by Yuri Rastegaev on 19.03.2016. + */ + +import java.net.URLEncoder + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.scala.DefaultScalaModule +import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper +import jp.t2v.lab.play2.auth.social.core.{AccessTokenRetrievalFailedException, OAuth2Authenticator} +import play.api.Logger +import play.api.Play.current +import play.api.http.{HeaderNames, MimeTypes} +import play.api.libs.ws.{WS, WSResponse} +import play.api.mvc.Results + +import scala.concurrent.{ExecutionContext, Future} +import scala.util.control.NonFatal + + +class VkontakteAuthenticator extends OAuth2Authenticator { + + type AccessToken = String + + val providerName: String = "vkontakte" + + val accessTokenUrl = "https://oauth.vk.com/access_token" + + val authorizationUrl = "https://oauth.vk.com/authorize" + + val display = "page" + + val response_type = "code" + + lazy val clientId = current.configuration.getString("vkontakte.clientId").getOrElse(sys.error("vkontakte.clientId is missing")) + + lazy val clientSecret = current.configuration.getString("vkontakte.clientSecret").getOrElse(sys.error("vkontakte.clientSecret is missing")) + + lazy val callbackUrl = current.configuration.getString("vkontakte.callbackURL").getOrElse(sys.error("vkontakte.callbackURL is missing")) + + def retrieveAccessToken(code: String)(implicit ctx: ExecutionContext): Future[AccessToken] = { + WS.url(accessTokenUrl) + .withQueryString( + "client_id" -> clientId, + "client_secret" -> clientSecret, + "redirect_uri" -> callbackUrl, + "code" -> code) + .withHeaders(HeaderNames.ACCEPT -> MimeTypes.JSON) + .post(Results.EmptyContent()) + .map { response => + Logger(getClass).debug("Retrieving access token from provider API: " + response.body) + parseAccessTokenResponse(response) + } + } + + def getAuthorizationUrl(scope: String, state: String): String = { + val encodedClientId = URLEncoder.encode(clientId, "utf-8") + val encodedRedirectUri = URLEncoder.encode(callbackUrl, "utf-8") + val encodedScope = URLEncoder.encode(scope, "utf-8") + val encodedState = URLEncoder.encode(state, "utf-8") + s"${authorizationUrl}?client_id=${encodedClientId}" + + s"&redirect_uri=${encodedRedirectUri}" + + s"&display=${display}" + + s"&response_type=${response_type}" + + s"&scope=${encodedScope}" + + s"&state=${encodedState}" + } + + def parseAccessTokenResponse(response: WSResponse): String = { + Logger(getClass).debug("Parsing access token response: " + response.body) + try { + val mapper = new ObjectMapper() with ScalaObjectMapper + mapper.registerModule(DefaultScalaModule) + val responseMap = mapper.readValue[Map[String, Object]](response.body) + + var access_token = responseMap.get("access_token") + var email = responseMap.get("email") + var expires_in = responseMap.get("expires_in") + var user_id = responseMap.get("user_id") + + VkontakteAuthenticator.email = email.get.toString + VkontakteAuthenticator.expires_in = expires_in.get.toString + VkontakteAuthenticator.user_id = user_id.get.toString + + access_token.get.toString + } catch { + case NonFatal(e) => + throw new AccessTokenRetrievalFailedException(s"Failed to retrieve access token. ${response.body}", e) + } + } + +} + +object VkontakteAuthenticator { + var email = "" + var expires_in = "" + var user_id = "" + +} diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteController.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteController.scala new file mode 100644 index 0000000..4f87a92 --- /dev/null +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteController.scala @@ -0,0 +1,17 @@ +package controllers.providers.vkontakte + +/** + * Created by Yuri Rastegaev on 19.03.2016. + */ + +import jp.t2v.lab.play2.auth.social.core.OAuth2Controller +import jp.t2v.lab.play2.auth.{AuthConfig, Login, OptionalAuthElement} + +trait VkontakteController extends OAuth2Controller + with AuthConfig + with OptionalAuthElement + with Login { + + val authenticator = new VkontakteAuthenticator + +} diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala new file mode 100644 index 0000000..0c20cea --- /dev/null +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala @@ -0,0 +1,55 @@ +package controllers.providers.vkontakte + +/** + * Created by Yuri Rastegaev on 19.03.2016. + */ + +import jp.t2v.lab.play2.auth.social.core.OAuthProviderUserSupport +import play.api.Logger +import play.api.Play.current +import play.api.libs.json.JsString +import play.api.libs.ws.{WS, WSResponse} + +import scala.concurrent.{ExecutionContext, Future} + +trait VkontakteProviderUserSupport extends OAuthProviderUserSupport { + self: VkontakteController => + + val USERS_GET_URL: String = "https://api.vk.com/method/users.get" + + type ProviderUser = VkontakteUser + + private def readProviderUser(accessToken: String, response: WSResponse): ProviderUser = { + val j = response.json + val resp = j \ "response" + val uid = (resp.get \\ "uid").head.toString() + val first_name = (resp.get \\ "first_name").head.asInstanceOf[JsString].value + val last_name = (resp.get \\ "last_name").head.asInstanceOf[JsString].value + val photo_50 = (resp.get \\ "photo_50").head.asInstanceOf[JsString].value + + VkontakteUser( + uid, + first_name, + VkontakteAuthenticator.email, + photo_50, + accessToken + ) + } + + + def retrieveProviderUser(accessToken: AccessToken, userId: String)(implicit ctx: ExecutionContext): Future[ProviderUser] = { + for { + response <- WS.url(USERS_GET_URL) + .withQueryString("user_id" -> userId.toString, "access_token" -> accessToken, "fields" -> "photo_50") + .get() + } yield { + Logger(getClass).debug("Retrieving user info from provider API: " + response.body) + readProviderUser(accessToken, response) + } + } + + def retrieveProviderUser(accessToken: AccessToken)(implicit ctx: ExecutionContext): Future[ProviderUser] = { + retrieveProviderUser(accessToken, VkontakteAuthenticator.user_id) + } + +} diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteUser.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteUser.scala new file mode 100644 index 0000000..2b0f977 --- /dev/null +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteUser.scala @@ -0,0 +1,12 @@ +package controllers.providers.vkontakte + +/** + * Created by Yuri Rastegaev on 19.03.2016. + */ + +case class VkontakteUser( + id: String, + first_name: String, + email: String, + coverUrl: String, + accessToken: String) From d0181b2a9518b1f1b8d9a9910d20eb25de09cf9b Mon Sep 17 00:00:00 2001 From: rurik Date: Wed, 23 Mar 2016 00:55:53 +0300 Subject: [PATCH 2/6] Vkontakte provider and example of usage --- project/Build.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 114ea7c..76a8a3a 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -142,7 +142,9 @@ object ApplicationBuild extends Build { "org.scalikejdbc" %% "scalikejdbc-test" % "2.2.7" % "test", "org.scalikejdbc" %% "scalikejdbc-play-initializer" % "2.4.0", "org.scalikejdbc" %% "scalikejdbc-play-dbapi-adapter" % "2.4.0", - "org.scalikejdbc" %% "scalikejdbc-play-fixture" % "2.4.0" + "org.scalikejdbc" %% "scalikejdbc-play-fixture" % "2.4.0", + "com.fasterxml.jackson.module" % "jackson-module-scala_2.10" % "2.7.2", + "net.minidev" % "json-smart" % "1.0.9" ), publish := { }, publishArtifact := false, From 42bf5c4c2306c7de11905c3e7c725ccc1e6f2541 Mon Sep 17 00:00:00 2001 From: rurik Date: Wed, 23 Mar 2016 23:55:22 +0300 Subject: [PATCH 3/6] Vkontakte provider and example of usage --- project/Build.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 76a8a3a..b1c0ee8 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -118,6 +118,8 @@ object ApplicationBuild extends Build { name := appName + "-social", libraryDependencies += "com.typesafe.play" %% "play" % playVersion % "provided", libraryDependencies += "com.typesafe.play" %% "play-ws" % playVersion % "provided", + libraryDependencies += "com.fasterxml.jackson.module" % "jackson-module-scala_2.10" % "2.7.2", + libraryDependencies += "net.minidev" % "json-smart" % "1.0.9", publishMavenStyle := appPublishMavenStyle, publishArtifact in Test := appPublishArtifactInTest, pomIncludeRepository := appPomIncludeRepository, @@ -142,9 +144,7 @@ object ApplicationBuild extends Build { "org.scalikejdbc" %% "scalikejdbc-test" % "2.2.7" % "test", "org.scalikejdbc" %% "scalikejdbc-play-initializer" % "2.4.0", "org.scalikejdbc" %% "scalikejdbc-play-dbapi-adapter" % "2.4.0", - "org.scalikejdbc" %% "scalikejdbc-play-fixture" % "2.4.0", - "com.fasterxml.jackson.module" % "jackson-module-scala_2.10" % "2.7.2", - "net.minidev" % "json-smart" % "1.0.9" + "org.scalikejdbc" %% "scalikejdbc-play-fixture" % "2.4.0" ), publish := { }, publishArtifact := false, From 4ecb1f50dd790962d24b79d6e556c126f3bfdf4b Mon Sep 17 00:00:00 2001 From: rurik Date: Thu, 24 Mar 2016 00:53:09 +0300 Subject: [PATCH 4/6] Vkontakte provider and example of usage --- .../app/controllers/Application.scala | 21 ++++++++++++------- social-sample/app/models/User.scala | 4 ++-- social-sample/app/views/index.scala.html | 2 +- .../vkontakte/VkontakteAuthenticator.scala | 2 +- .../vkontakte/VkontakteController.scala | 2 +- .../VkontakteProviderUserSupport.scala | 2 +- .../providers/vkontakte/VkontakteUser.scala | 2 +- 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/social-sample/app/controllers/Application.scala b/social-sample/app/controllers/Application.scala index 62fda52..98f913a 100644 --- a/social-sample/app/controllers/Application.scala +++ b/social-sample/app/controllers/Application.scala @@ -1,16 +1,20 @@ package controllers -import controllers.providers.vkontakte.{VkontakteController, VkontakteProviderUserSupport} -import models.{User, VkontakteUser, _} - -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.{ExecutionContext, Future} -import scala.reflect.{ClassTag, classTag} -import jp.t2v.lab.play2.auth._ import jp.t2v.lab.play2.auth.social.providers.twitter.{TwitterController, TwitterProviderUserSupport} import jp.t2v.lab.play2.auth.social.providers.facebook.{FacebookController, FacebookProviderUserSupport} import jp.t2v.lab.play2.auth.social.providers.github.{GitHubController, GitHubProviderUserSupport} import jp.t2v.lab.play2.auth.social.providers.slack.SlackController +import jp.t2v.lab.play2.auth.social.providers.vkontakte.{VkontakteController, VkontakteProviderUserSupport} +import jp.t2v.lab.play2.auth._ +import models._ +import play.api.mvc.Results._ +import play.api.mvc._ +import scalikejdbc._ + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.{ExecutionContext, Future} +import scala.reflect.{ClassTag, classTag} + object Application extends Controller with OptionalAuthElement with AuthConfigImpl with Logout { @@ -21,7 +25,8 @@ object Application extends Controller with OptionalAuthElement with AuthConfigIm val facebookUser = user.flatMap(u => FacebookUser.findByUserId(u.id)) val twitterUser = user.flatMap(u => TwitterUser.findByUserId(u.id)) val slackAccessToken = user.flatMap(u => SlackAccessToken.findByUserId(u.id)) - Ok(views.html.index(user, gitHubUser, facebookUser, twitterUser, slackAccessToken)) + val vkontakteUser = user.flatMap(u => VkontakteUser.findByUserId(u.id)) + Ok(views.html.index(user, gitHubUser, facebookUser, twitterUser, slackAccessToken,vkontakteUser)) } } diff --git a/social-sample/app/models/User.scala b/social-sample/app/models/User.scala index 49c721e..a8661ac 100644 --- a/social-sample/app/models/User.scala +++ b/social-sample/app/models/User.scala @@ -1,8 +1,8 @@ package models -import controllers.providers.vkontakte import jp.t2v.lab.play2.auth.social.providers -import jp.t2v.lab.play2.auth.social.providers.{facebook, twitter} +import jp.t2v.lab.play2.auth.social.providers.{facebook, twitter,vkontakte} +import scalikejdbc._ sealed trait Authority case object Admin extends Authority diff --git a/social-sample/app/views/index.scala.html b/social-sample/app/views/index.scala.html index 672b916..024ee16 100644 --- a/social-sample/app/views/index.scala.html +++ b/social-sample/app/views/index.scala.html @@ -3,7 +3,7 @@ facebookUser: Option[FacebookUser], twitterUser: Option[TwitterUser], slackAccessToken: Option[SlackAccessToken], - vkontakteAccessToken: Option[VkontakteUser]) + vkontakteUser: Option[VkontakteUser]) diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala index 7de2071..5fbd834 100644 --- a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala @@ -1,4 +1,4 @@ -package controllers.providers.vkontakte +package jp.t2v.lab.play2.auth.social.providers.vkontakte /** * Created by Yuri Rastegaev on 19.03.2016. diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteController.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteController.scala index 4f87a92..8876093 100644 --- a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteController.scala +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteController.scala @@ -1,4 +1,4 @@ -package controllers.providers.vkontakte +package jp.t2v.lab.play2.auth.social.providers.vkontakte /** * Created by Yuri Rastegaev on 19.03.2016. diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala index 0c20cea..d20eae5 100644 --- a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala @@ -1,4 +1,4 @@ -package controllers.providers.vkontakte +package jp.t2v.lab.play2.auth.social.providers.vkontakte /** * Created by Yuri Rastegaev on 19.03.2016. diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteUser.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteUser.scala index 2b0f977..8d28996 100644 --- a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteUser.scala +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteUser.scala @@ -1,4 +1,4 @@ -package controllers.providers.vkontakte +package jp.t2v.lab.play2.auth.social.providers.vkontakte /** * Created by Yuri Rastegaev on 19.03.2016. From c838b8528ab986659eff13e619c109c906f52a14 Mon Sep 17 00:00:00 2001 From: rurik Date: Mon, 28 Mar 2016 22:47:37 +0300 Subject: [PATCH 5/6] pull request fix --- project/Build.scala | 10 ++--- .../social/core/OAuth2Authenticator.scala | 2 +- .../facebook/FacebookAuthenticator.scala | 2 +- .../github/GitHubAuthenticator.scala | 2 +- .../vkontakte/VkontakteAuthenticator.scala | 42 +++++-------------- .../VkontakteProviderUserSupport.scala | 22 +++++----- .../providers/vkontakte/VkontakteToken.scala | 4 ++ 7 files changed, 32 insertions(+), 52 deletions(-) create mode 100644 social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteToken.scala diff --git a/project/Build.scala b/project/Build.scala index b1c0ee8..74cc59e 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -115,12 +115,10 @@ object ApplicationBuild extends Build { lazy val social = Project (id = "social", base = file ("social")) .settings( baseSettings, - name := appName + "-social", - libraryDependencies += "com.typesafe.play" %% "play" % playVersion % "provided", - libraryDependencies += "com.typesafe.play" %% "play-ws" % playVersion % "provided", - libraryDependencies += "com.fasterxml.jackson.module" % "jackson-module-scala_2.10" % "2.7.2", - libraryDependencies += "net.minidev" % "json-smart" % "1.0.9", - publishMavenStyle := appPublishMavenStyle, + name := appName + "-social", + libraryDependencies += "com.typesafe.play" %% "play" % playVersion % "provided", + libraryDependencies += "com.typesafe.play" %% "play-ws" % playVersion % "provided", + publishMavenStyle := appPublishMavenStyle, publishArtifact in Test := appPublishArtifactInTest, pomIncludeRepository := appPomIncludeRepository, publishTo <<=(version)(appPublishTo), diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/core/OAuth2Authenticator.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/core/OAuth2Authenticator.scala index bd76de6..b98567b 100644 --- a/social/src/main/scala/jp/t2v/lab/play2/auth/social/core/OAuth2Authenticator.scala +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/core/OAuth2Authenticator.scala @@ -22,6 +22,6 @@ trait OAuth2Authenticator extends OAuthAuthenticator { def getAuthorizationUrl(scope: String, state: String): String - def parseAccessTokenResponse(response: WSResponse): String + def parseAccessTokenResponse(response: WSResponse): AccessToken } diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/facebook/FacebookAuthenticator.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/facebook/FacebookAuthenticator.scala index bccc829..d219842 100644 --- a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/facebook/FacebookAuthenticator.scala +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/facebook/FacebookAuthenticator.scala @@ -51,7 +51,7 @@ class FacebookAuthenticator extends OAuth2Authenticator { s"${authorizationUrl}?client_id=${encodedClientId}&redirect_uri=${encodedRedirectUri}&scope=${encodedScope}&state=${encodedState}" } - def parseAccessTokenResponse(response: WSResponse): String = { + def parseAccessTokenResponse(response: WSResponse): AccessToken = { Logger(getClass).debug("Parsing access token response: " + response.body) try { (for { diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/github/GitHubAuthenticator.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/github/GitHubAuthenticator.scala index 368fabd..1ce6a01 100644 --- a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/github/GitHubAuthenticator.scala +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/github/GitHubAuthenticator.scala @@ -50,7 +50,7 @@ class GitHubAuthenticator extends OAuth2Authenticator { s"${authorizationUrl}?client_id=${encodedClientId}&redirect_uri=${encodedRedirectUri}&scope=${encodedScope}&state=${encodedState}" } - def parseAccessTokenResponse(response: WSResponse): String = { + def parseAccessTokenResponse(response: WSResponse): AccessToken = { Logger(getClass).debug("Parsing access token response: " + response.body) try { (response.json \ "access_token").as[String] diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala index 5fbd834..7ba9ff9 100644 --- a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteAuthenticator.scala @@ -1,18 +1,12 @@ package jp.t2v.lab.play2.auth.social.providers.vkontakte -/** - * Created by Yuri Rastegaev on 19.03.2016. - */ - import java.net.URLEncoder -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.scala.DefaultScalaModule -import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper import jp.t2v.lab.play2.auth.social.core.{AccessTokenRetrievalFailedException, OAuth2Authenticator} import play.api.Logger import play.api.Play.current import play.api.http.{HeaderNames, MimeTypes} +import play.api.libs.json.Json import play.api.libs.ws.{WS, WSResponse} import play.api.mvc.Results @@ -22,7 +16,7 @@ import scala.util.control.NonFatal class VkontakteAuthenticator extends OAuth2Authenticator { - type AccessToken = String + type AccessToken = VkontakteToken val providerName: String = "vkontakte" @@ -40,7 +34,7 @@ class VkontakteAuthenticator extends OAuth2Authenticator { lazy val callbackUrl = current.configuration.getString("vkontakte.callbackURL").getOrElse(sys.error("vkontakte.callbackURL is missing")) - def retrieveAccessToken(code: String)(implicit ctx: ExecutionContext): Future[AccessToken] = { + override def retrieveAccessToken(code: String)(implicit ctx: ExecutionContext): Future[AccessToken] = { WS.url(accessTokenUrl) .withQueryString( "client_id" -> clientId, @@ -55,7 +49,7 @@ class VkontakteAuthenticator extends OAuth2Authenticator { } } - def getAuthorizationUrl(scope: String, state: String): String = { + override def getAuthorizationUrl(scope: String, state: String): String = { val encodedClientId = URLEncoder.encode(clientId, "utf-8") val encodedRedirectUri = URLEncoder.encode(callbackUrl, "utf-8") val encodedScope = URLEncoder.encode(scope, "utf-8") @@ -68,23 +62,15 @@ class VkontakteAuthenticator extends OAuth2Authenticator { s"&state=${encodedState}" } - def parseAccessTokenResponse(response: WSResponse): String = { + override def parseAccessTokenResponse(response: WSResponse): VkontakteToken = { Logger(getClass).debug("Parsing access token response: " + response.body) try { - val mapper = new ObjectMapper() with ScalaObjectMapper - mapper.registerModule(DefaultScalaModule) - val responseMap = mapper.readValue[Map[String, Object]](response.body) - - var access_token = responseMap.get("access_token") - var email = responseMap.get("email") - var expires_in = responseMap.get("expires_in") - var user_id = responseMap.get("user_id") - - VkontakteAuthenticator.email = email.get.toString - VkontakteAuthenticator.expires_in = expires_in.get.toString - VkontakteAuthenticator.user_id = user_id.get.toString - - access_token.get.toString + val jsonValue = Json.parse(response.body.toString) + var access_token = jsonValue \ "access_token" + var email = jsonValue \ "email" + var expires_in = jsonValue \ "expires_in" + var user_id = jsonValue \ "user_id" + new VkontakteToken(access_token.get.toString(), email.get.toString(), expires_in.get.as[Long], user_id.get.as[Long]) } catch { case NonFatal(e) => throw new AccessTokenRetrievalFailedException(s"Failed to retrieve access token. ${response.body}", e) @@ -93,9 +79,3 @@ class VkontakteAuthenticator extends OAuth2Authenticator { } -object VkontakteAuthenticator { - var email = "" - var expires_in = "" - var user_id = "" - -} diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala index d20eae5..a1ab26b 100644 --- a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteProviderUserSupport.scala @@ -1,9 +1,5 @@ package jp.t2v.lab.play2.auth.social.providers.vkontakte -/** - * Created by Yuri Rastegaev on 19.03.2016. - */ - import jp.t2v.lab.play2.auth.social.core.OAuthProviderUserSupport import play.api.Logger import play.api.Play.current @@ -19,7 +15,9 @@ trait VkontakteProviderUserSupport extends OAuthProviderUserSupport { type ProviderUser = VkontakteUser - private def readProviderUser(accessToken: String, response: WSResponse): ProviderUser = { + type AccessToken = VkontakteToken + + private def readProviderUser(vkontakteToken: VkontakteToken, response: WSResponse): ProviderUser = { val j = response.json val resp = j \ "response" val uid = (resp.get \\ "uid").head.toString() @@ -30,26 +28,26 @@ trait VkontakteProviderUserSupport extends OAuthProviderUserSupport { VkontakteUser( uid, first_name, - VkontakteAuthenticator.email, + vkontakteToken.email, photo_50, - accessToken + vkontakteToken.access_token ) } - def retrieveProviderUser(accessToken: AccessToken, userId: String)(implicit ctx: ExecutionContext): Future[ProviderUser] = { + def retrieveProviderUser(vkontakteToken: VkontakteToken, userId: String)(implicit ctx: ExecutionContext): Future[ProviderUser] = { for { response <- WS.url(USERS_GET_URL) - .withQueryString("user_id" -> userId.toString, "access_token" -> accessToken, "fields" -> "photo_50") + .withQueryString("user_id" -> userId.toString, "access_token" -> vkontakteToken.access_token, "fields" -> "photo_50") .get() } yield { Logger(getClass).debug("Retrieving user info from provider API: " + response.body) - readProviderUser(accessToken, response) + readProviderUser(vkontakteToken, response) } } - def retrieveProviderUser(accessToken: AccessToken)(implicit ctx: ExecutionContext): Future[ProviderUser] = { - retrieveProviderUser(accessToken, VkontakteAuthenticator.user_id) + def retrieveProviderUser(vkontakteToken: VkontakteToken)(implicit ctx: ExecutionContext): Future[ProviderUser] = { + retrieveProviderUser(vkontakteToken, vkontakteToken.user_id.toString) } } diff --git a/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteToken.scala b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteToken.scala new file mode 100644 index 0000000..8eb3c76 --- /dev/null +++ b/social/src/main/scala/jp/t2v/lab/play2/auth/social/providers/vkontakte/VkontakteToken.scala @@ -0,0 +1,4 @@ +package jp.t2v.lab.play2.auth.social.providers.vkontakte + +class VkontakteToken(var access_token: String, var email: String, var expires_in: Long, var user_id: Long) { +} From ae23b8e33abe3fde64b67c22dca816219d56178d Mon Sep 17 00:00:00 2001 From: rurik Date: Mon, 28 Mar 2016 23:10:20 +0300 Subject: [PATCH 6/6] pull request fix --- social-sample/app/controllers/Application.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/social-sample/app/controllers/Application.scala b/social-sample/app/controllers/Application.scala index 98f913a..fc3b2e2 100644 --- a/social-sample/app/controllers/Application.scala +++ b/social-sample/app/controllers/Application.scala @@ -4,7 +4,7 @@ import jp.t2v.lab.play2.auth.social.providers.twitter.{TwitterController, Twitte import jp.t2v.lab.play2.auth.social.providers.facebook.{FacebookController, FacebookProviderUserSupport} import jp.t2v.lab.play2.auth.social.providers.github.{GitHubController, GitHubProviderUserSupport} import jp.t2v.lab.play2.auth.social.providers.slack.SlackController -import jp.t2v.lab.play2.auth.social.providers.vkontakte.{VkontakteController, VkontakteProviderUserSupport} +import jp.t2v.lab.play2.auth.social.providers.vkontakte.{VkontakteToken, VkontakteController, VkontakteProviderUserSupport} import jp.t2v.lab.play2.auth._ import models._ import play.api.mvc.Results._ @@ -179,6 +179,8 @@ object VkontakteAuthController extends VkontakteController with AuthConfigImpl with VkontakteProviderUserSupport { + override type AccessToken = VkontakteToken + override def onOAuthLinkSucceeded(token: AccessToken, consumerUser: User)(implicit request: RequestHeader, ctx: ExecutionContext): Future[Result] = { retrieveProviderUser(token).map { providerUser => DB.localTx { implicit session =>