我們必須創建FacebookProvider的自定義實現以處理新的Facebook答案。這是
import com.mohiva.play.silhouette.api.LoginInfo
import com.mohiva.play.silhouette.api.exceptions.AuthenticationException
import com.mohiva.play.silhouette.api.util.HTTPLayer
import com.mohiva.play.silhouette.impl.exceptions.ProfileRetrievalException
import com.mohiva.play.silhouette.impl.providers.OAuth2Provider._
import com.mohiva.play.silhouette.impl.providers._
import com.mohiva.play.silhouette.impl.providers.oauth2.{FacebookProfileBuilder, FacebookProvider}
import com.mohiva.play.silhouette.impl.providers.oauth2.FacebookProvider._
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.json.{ JsObject, JsValue }
import play.api.libs.ws.WSResponse
import play.api.libs.json._
import scala.concurrent.Future
import scala.util.{ Failure, Success, Try }
/**
* A Facebook OAuth2 Provider.
*
* @param httpLayer The HTTP layer implementation.
* @param stateProvider The state provider implementation.
* @param settings The provider settings.
*
* @see https://developers.facebook.com/tools/explorer
* @see https://developers.facebook.com/docs/graph-api/reference/user
* @see https://developers.facebook.com/docs/facebook-login/access-tokens
*/
abstract class FacebookProvider(httpLayer: HTTPLayer, stateProvider: OAuth2StateProvider, settings: OAuth2Settings)
extends OAuth2Provider(httpLayer, stateProvider, settings) {
/**
* The content type to parse a profile from.
*/
type Content = JsValue
/**
* Gets the provider ID.
*
* @return The provider ID.
*/
val id = ID
/**
* Defines the URLs that are needed to retrieve the profile data.
*/
protected val urls = Map("api" -> API)
/**
* Builds the social profile.
*
* @param authInfo The auth info received from the provider.
* @return On success the build social profile, otherwise a failure.
*/
protected def buildProfile(authInfo: OAuth2Info): Future[Profile] = {
httpLayer.url(urls("api").format(authInfo.accessToken)).get().flatMap { response =>
val json = response.json
(json \ "error").asOpt[JsObject] match {
case Some(error) =>
val errorMsg = (error \ "message").as[String]
val errorType = (error \ "type").as[String]
val errorCode = (error \ "code").as[Int]
throw new ProfileRetrievalException(SpecifiedProfileError.format(id, errorMsg, errorType, errorCode))
case _ => profileParser.parse(json)
}
}
}
/**
* Builds the OAuth2 info from response.
*
* @param response The response from the provider.
* @return The OAuth2 info on success, otherwise a failure.
*/
override def buildInfo(response: WSResponse): Try[OAuth2Info] = {
val responseJson = Json.parse(response.body)
responseJson.validate[OAuth2Info].asEither.fold(
error => Failure(new AuthenticationException(InvalidInfoFormat.format(id, error))),
info => Success(info)
)
}
}
/**
* The profile parser for the common social profile.
*/
class FacebookProfileParser extends SocialProfileParser[JsValue, CommonSocialProfile] {
/**
* Parses the social profile.
*
* @param json The content returned from the provider.
* @return The social profile from given result.
*/
def parse(json: JsValue) = Future.successful {
val userID = (json \ "id").as[String]
val firstName = (json \ "first_name").asOpt[String]
val lastName = (json \ "last_name").asOpt[String]
val fullName = (json \ "name").asOpt[String]
val avatarURL = (json \ "picture" \ "data" \ "url").asOpt[String]
val email = (json \ "email").asOpt[String]
CommonSocialProfile(
loginInfo = LoginInfo(ID, userID),
firstName = firstName,
lastName = lastName,
fullName = fullName,
avatarURL = avatarURL,
email = email)
}
}
/**
* The profile builder for the common social profile.
*/
trait FacebookProfileBuilder extends CommonSocialProfileBuilder {
self: FacebookProvider =>
/**
* The profile parser implementation.
*/
val profileParser = new FacebookProfileParser
}
/**
* The companion object.
*/
object FacebookWesuraProvider {
/**
* The error messages.
*/
val SpecifiedProfileError = "[Silhouette][%s] Error retrieving profile information. Error message: %s, type: %s, code: %s"
/**
* The Facebook constants.
*/
val ID = "facebook"
val API = "https://graph.facebook.com/me?fields=name,first_name,last_name,picture,email&return_ssl_resources=1&access_token=%s"
/**
* Creates an instance of the provider.
*
* @param httpLayer The HTTP layer implementation.
* @param stateProvider The state provider implementation.
* @param settings The provider settings.
* @return An instance of this provider.
*/
def apply(httpLayer: HTTPL
ayer, stateProvider: OAuth2StateProvider, settings: OAuth2Settings) = {
new FacebookProvider(httpLayer, stateProvider, settings) with FacebookProfileBuilder
}
}