Maxim Cherkasov
Maxim Cherkasov

Reputation: 115

Silhouette and mobile application

I've used as example play-silhouette-angular-seed. Authorization via Satellizer works fine.

When I try to authorize via iOs app I got next error:

com.mohiva.play.silhouette.impl.exceptions.UnexpectedResponseException: 
[Silhouette][facebook] Cannot build OAuth2Info because of invalid response format: 
List((/access_token,List(ValidationError(List(error.path.missing),WrappedArray()))))

I got an error 400 in this function from OAuth2Provider.scala :

protected def getAccessToken(code: String)(implicit request: RequestHeader): Future[OAuth2Info] = {
httpLayer.url(settings.accessTokenURL).withHeaders(headers: _*).post(Map(
  ClientID -> Seq(settings.clientID),
  ClientSecret -> Seq(settings.clientSecret),
  GrantType -> Seq(AuthorizationCode),
  Code -> Seq(code),
  RedirectURI -> Seq(resolveCallbackURL(settings.redirectURL))) ++ settings.accessTokenParams.mapValues(Seq(_))).flatMap { response =>
  logger.debug("[Silhouette][%s] Access token response: [%s]".format(id, response.body))
  Future.from(buildInfo(response))
}

}

Upvotes: 2

Views: 513

Answers (2)

Jules Olléon
Jules Olléon

Reputation: 6888

This happens when the OAuth2Provider gets a response it can't parse, which is, any non-success response. So there can be many reasons for this error, for instance the authorization code is invalid or expired, or you haven't configured the redirect_uri properly (check your Facebook app configuration on the Facebook dev site to set the redirect_uri).

Silhouette does log the response it gets from Facebook which should help you debug what the actual issue is, the log line to look for is in the snippet you provided:

logger.debug("[Silhouette][%s] Access token response:...

So check your logs, there you should see the response from Facebook, likely with an error indicating why they couldn't give you an access_token.

Upvotes: 0

Maxim Cherkasov
Maxim Cherkasov

Reputation: 115

This error has been risen because Satellizer for authentication via Facebook send to server an 'authentication code' and Silhouette server use this code to get Facebook 'access token' and create user.

Facebook iOs SDK, instead, obtained 'Access token' and I've tried to send it to server in Json in field 'code' like 'Satellizer.

To resolve this issue I send an 'access token' in Json field named 'access_token' and use next code to authenticate mobile application:

class MobileSocialAuthController @Inject() (
  val messagesApi: MessagesApi,
  userService: UserService,
  authInfoRepository: AuthInfoRepository,
  socialProviderRegistry: SocialProviderRegistry,
  val env: Environment[User, JWTAuthenticator])
  extends Silhouette[User, JWTAuthenticator]
{
  def authenticate(provider: String) = UserAwareAction.async(parse.json) {
    implicit request =>
      provider match {
        case "facebook" =>
          request.body.asOpt[OAuth2Info] match {
            case Some(authInfo) =>
              (socialProviderRegistry.get[FacebookProvider](provider) match {
                case Some(p: FacebookProvider) =>
                  for {
                    profile <-p.retrieveProfile(authInfo)
                    user <- userService.save(profile)
                    authInfo <- authInfoRepository.save(profile.loginInfo, authInfo)
                    authenticator <- env.authenticatorService.create(profile.loginInfo)
                    token <- env.authenticatorService.init(authenticator)
                  } yield {
                    env.eventBus.publish(LoginEvent(user, request, request2Messages))
                    Ok(Json.obj("token" -> token))
                  }
                case _ => Future.failed(new ProviderException(s"Cannot authenticate with unexpected social provider $provider"))
              }).recover {
                case e: ProviderException =>
                  logger.error("Unexpected provider error", e)
                  Unauthorized(Json.obj("message" -> Messages("could.not.authenticate")))
              }
            case _ =>
              Future(BadRequest(Json.obj(
                "message" -> "Bad OAuth2 json.")))
          }
        case _ =>
          Future(BadRequest(Json.obj(
            "message" -> "You can use only Facebook account for authentication.")))
      }
  }
}

As a result, I have a token which I use in ios application to obtain resources.

Upvotes: 2

Related Questions