Hello everyone,
I’m having an hard time wrapping my head around using together JWT and OAuth2.
I’ll copy and paste my Module.scala, hoping someone will spot what’s wrong.
I’m guessing that provideOAuth2StateProvider perhaps shouldn’t be returning a cookie, since I’m using JWT?
Well, I must admit I’m VERY confused.
By the way, JWT Auth is working just fine.
I’m using Play 2.5 and Silhouette 4.0.
package module
import com.google.inject.name.Named
import com.google.inject.{ AbstractModule, Provides }
import com.mohiva.play.silhouette.api.{ Environment, EventBus, Silhouette, SilhouetteProvider }
import com.mohiva.play.silhouette.api.actions.SecuredErrorHandler
import com.mohiva.play.silhouette.api.crypto.Base64AuthenticatorEncoder
import com.mohiva.play.silhouette.api.repositories.AuthInfoRepository
import com.mohiva.play.silhouette.api.services.{ AuthenticatorService, AvatarService, IdentityService }
import com.mohiva.play.silhouette.api.util._
import com.mohiva.play.silhouette.impl.authenticators.{ JWTAuthenticator, JWTAuthenticatorService, JWTAuthenticatorSettings }
import com.mohiva.play.silhouette.impl.providers._
import com.mohiva.play.silhouette.impl.providers.oauth2.FacebookProvider
import com.mohiva.play.silhouette.impl.exceptions.OAuth2StateException
import com.mohiva.play.silhouette.impl.providers.OAuth2Provider._
import com.mohiva.play.silhouette.impl.providers.oauth2.state.{ CookieStateProvider, CookieStateSettings }
import com.mohiva.play.silhouette.impl.providers.oauth2.state.CookieStateProvider._
import com.mohiva.play.silhouette.impl.providers.{ OAuth2State, OAuth2StateProvider }
import com.mohiva.play.silhouette.crypto.{ JcaCookieSigner, JcaCookieSignerSettings, JcaCrypter, JcaCrypterSettings }
import com.mohiva.play.silhouette.api.crypto.{ CookieSigner, Crypter }
import com.mohiva.play.silhouette.impl.providers.oauth1.secrets._
//import com.mohiva.play.silhouette.impl.providers.{CredentialsProvider, SocialProviderRegistry}
import com.mohiva.play.silhouette.impl.services.GravatarService
import com.mohiva.play.silhouette.impl.util.{ DefaultFingerprintGenerator, PlayCacheLayer, SecureRandomIDGenerator }
import com.mohiva.play.silhouette.password.BCryptPasswordHasher
import com.mohiva.play.silhouette.persistence.daos.{ DelegableAuthInfoDAO, InMemoryAuthInfoDAO }
import com.mohiva.play.silhouette.persistence.repositories.DelegableAuthInfoRepository
import daos._
import models.UserModel
import net.codingwell.scalaguice.ScalaModule
import play.api.Configuration
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.ws.WSClient
import services.AuthService
import utils.ErrorHandler
import utils.auth.JWTEnv
import scala.concurrent.duration.{ FiniteDuration, Duration }
class Module extends AbstractModule with ScalaModule {
def configure() {
bind[IdentityService[UserModel]].to[AuthService]
bind[UserDao].to[MongoUserDao]
bind[CoreoDao].to[MongoCoreoDao]
bind[UserTokenDao].to[MongoUserTokenDao]
bind[SecuredErrorHandler].to[ErrorHandler]
bind[DelegableAuthInfoDAO[PasswordInfo]].to[PasswordInfoDao]
bind[DelegableAuthInfoDAO[OAuth1Info]].to[OAuth1InfoDao]
bind[DelegableAuthInfoDAO[OAuth2Info]].to[OAuth2InfoDao]
//bind[DelegableAuthInfoDAO[OAuth2Info]].toInstance(new InMemoryAuthInfoDAO[OAuth2Info]) //.to[OAuth2InfoDao]
//bind[DelegableAuthInfoDAO[OpenIDInfo]].toInstance(new InMemoryAuthInfoDAO[OpenIDInfo])
bind[IDGenerator].toInstance(new SecureRandomIDGenerator())
bind[PasswordHasher].toInstance(new BCryptPasswordHasher)
bind[EventBus].toInstance(EventBus())
bind[FingerprintGenerator].toInstance(new DefaultFingerprintGenerator(false))
bind[Clock].toInstance(Clock())
bind[CacheLayer].to[PlayCacheLayer]
bind[Silhouette[JWTEnv]].to[SilhouetteProvider[JWTEnv]]
bind[IdentityService[UserModel]].to[AuthService]
}
@Provides
def provideHTTPLayer(client: WSClient): HTTPLayer = new PlayHTTPLayer(client)
@Provides
def provideEnvironment(
identityService: IdentityService[UserModel],
authenticatorService: AuthenticatorService[JWTAuthenticator],
eventBus: EventBus): Environment[JWTEnv] = {
Environment[JWTEnv](
identityService,
authenticatorService,
Seq(),
eventBus)
}
/**
* Provides the Facebook provider.
*
* @param httpLayer The HTTP layer implementation.
* @param stateProvider The OAuth2 state provider implementation.
* @param configuration The Play configuration.
* @return The Facebook provider.
*/
@Provides
def provideFacebookProvider(
httpLayer: HTTPLayer,
stateProvider: OAuth2StateProvider,
configuration: Configuration): FacebookProvider = {
/* val authUrl = configuration.getString("silhouette.facebook.authorizationURL") //.getOrElse("AuthUrl Not found")
val acsTknUrl = configuration.getString("silhouette.facebook.accessTokenURL") //.getOrElse("AcsTknUrl Not found")
val rdrctUrl = configuration.getString("silhouette.facebook.redirectURL") //.getOrElse("RdrctUrl Not found")
val clientId = configuration.getString("silhouette.facebook.clientID") //.getOrElse("clientID Not found")
val clientSecret = configuration.getString("silhouette.facebook.clientSecret") //.getOrElse("clientSecret Not found")
val scope = configuration.getString("silhouette.facebook.scope") //.getOrElse("scope Not found")
println("authUrl: " + authUrl)
println("acsTknUrl: " + acsTknUrl)
println("rdrctUrl: " + rdrctUrl)
println("clientId: " + clientId)
println("clientSecret: " + clientSecret)
println("scope: " + scope)*/
//val settings = new OAuth2Settings(authUrl, acsTknUrl.get, rdrctUrl.get, None, clientId.get, clientSecret.get, scope)
val settings = OAuth2Settings(
authorizationURL = configuration.getString("silhouette.facebook.authorizationURL"),
accessTokenURL = configuration.getString("silhouette.facebook.accessTokenURL").get,
redirectURL = configuration.getString("silhouette.facebook.redirectURL").get,
clientID = configuration.getString("silhouette.facebook.clientID").get,
clientSecret = configuration.getString("silhouette.facebook.clientSecret").get,
scope = configuration.getString("silhouette.facebook.scope")
)
//println("settings: " + settings)
//new FacebookProvider(httpLayer, stateProvider, configuration.underlying.as[OAuth2Settings]("silhouette.facebook"))
new FacebookProvider(httpLayer, stateProvider, settings)
}
/**
* Provides the cookie signer for the authenticator.
*
* @param configuration The Play configuration.
* @return The cookie signer for the authenticator.
*/
@Provides @Named("authenticator-cookie-signer")
def provideAuthenticatorCookieSigner(configuration: Configuration): CookieSigner = {
//val config = configuration.underlying.as[JcaCookieSignerSettings]("silhouette.authenticator.cookie.signer")
val config = JcaCookieSignerSettings(key = configuration.getString("silhouette.authenticator.cookie.signer").get)
new JcaCookieSigner(config)
}
/**
* Provides the cookie signer for the OAuth1 token secret provider.
*
* @param configuration The Play configuration.
* @return The cookie signer for the OAuth1 token secret provider.
*/
@Provides @Named("oauth1-token-secret-cookie-signer")
def provideOAuth1TokenSecretCookieSigner(configuration: Configuration): CookieSigner = {
//val config = configuration.underlying.as[JcaCookieSignerSettings]("silhouette.oauth1TokenSecretProvider.cookie.signer")
val config = JcaCookieSignerSettings(configuration.getString("silhouette.oauth1TokenSecretProvider.cookie.signer").get)
new JcaCookieSigner(config)
}
/**
* Provides the crypter for the OAuth1 token secret provider.
*
* @param configuration The Play configuration.
* @return The crypter for the OAuth1 token secret provider.
*/
@Provides @Named("oauth1-token-secret-crypter")
def provideOAuth1TokenSecretCrypter(configuration: Configuration): Crypter = {
//val config = configuration.underlying.as[JcaCrypterSettings]("silhouette.oauth1TokenSecretProvider.crypter")
val config = JcaCrypterSettings(key = configuration.getString("silhouette.oauth1TokenSecretProvider.crypter").get)
new JcaCrypter(config)
}
/**
* Provides the cookie signer for the OAuth2 state provider.
*
* @param configuration The Play configuration.
* @return The cookie signer for the OAuth2 state provider.
*/
@Provides @Named("oauth2-state-cookie-signer")
def provideOAuth2StageCookieSigner(configuration: Configuration): CookieSigner = {
//val config = configuration.underlying.as[JcaCookieSignerSettings]("silhouette.oauth2StateProvider.cookie.signer")
val config = JcaCookieSignerSettings(key = configuration.getString("silhouette.oauth2StateProvider.cookie.signer").get)
println("config: " + config)
new JcaCookieSigner(config)
}
/**
* Provides the OAuth1 token secret provider.
*
* @param cookieSigner The cookie signer implementation.
* @param crypter The crypter implementation.
* @param configuration The Play configuration.
* @param clock The clock instance.
* @return The OAuth1 token secret provider implementation.
*/
@Provides
def provideOAuth1TokenSecretProvider(
@Named("oauth1-token-secret-cookie-signer") cookieSigner: CookieSigner,
@Named("oauth1-token-secret-crypter") crypter: Crypter,
configuration: Configuration,
clock: Clock): OAuth1TokenSecretProvider = {
//val settings = configuration.underlying.as[CookieSecretSettings]("silhouette.oauth1TokenSecretProvider")
val settings = CookieSecretSettings(
cookieName = configuration.getString("oauth1TokenSecretProvider.cookieName").get,
cookiePath = configuration.getString("oauth1TokenSecretProvider.cookiePath").get,
secureCookie = configuration.getString("oauth1TokenSecretProvider.secureCookie").get.toBoolean,
httpOnlyCookie = configuration.getString("oauth1TokenSecretProvider.httpOnlyCookie").get.toBoolean,
expirationTime = Duration.apply(configuration.getString("oauth1TokenSecretProvider.expirationTime").get).asInstanceOf[FiniteDuration] //.get.collect { case d: FiniteDuration => d }
)
new CookieSecretProvider(settings, cookieSigner, crypter, clock)
}
//Duration.apply(myString).asInstanceOf[FiniteDuration]
/**
* Provides the OAuth2 state provider.
*
* @param idGenerator The ID generator implementation.
* @param cookieSigner The cookie signer implementation.
* @param configuration The Play configuration.
* @param clock The clock instance.
* @return The OAuth2 state provider implementation.
*/
@Provides
def provideOAuth2StateProvider(
idGenerator: IDGenerator,
@Named("oauth2-state-cookie-signer") cookieSigner: CookieSigner,
configuration: Configuration, clock: Clock): OAuth2StateProvider = {
//val settings = configuration.underlying.as[CookieStateSettings]("silhouette.oauth2StateProvider")
val settings = CookieStateSettings(
cookieName = configuration.getString("silhouette.oauth2StateProvider.cookieName").get,
cookiePath = configuration.getString("silhouette.oauth2StateProvider.cookiePath").get,
secureCookie = configuration.getString("silhouette.oauth2StateProvider.secureCookie").get.toBoolean,
httpOnlyCookie = configuration.getString("silhouette.oauth2StateProvider.httpOnlyCookie").get.toBoolean,
expirationTime = Duration.apply(configuration.getString("silhouette.oauth2StateProvider.expirationTime").get).asInstanceOf[FiniteDuration] //.get.collect { case d: FiniteDuration => d }
)
new CookieStateProvider(settings, idGenerator, cookieSigner, clock)
}
/**
* Provides the social provider registry.
*
* @param facebookProvider The Facebook provider implementation.
* @param googleProvider The Google provider implementation.
* @param vkProvider The VK provider implementation.
* @param clefProvider The Clef provider implementation.
* @param twitterProvider The Twitter provider implementation.
* @param xingProvider The Xing provider implementation.
* @param yahooProvider The Yahoo provider implementation.
* @return The Silhouette environment.
*/
@Provides
def provideSocialProviderRegistry(
facebookProvider: FacebookProvider //,
//googleProvider: GoogleProvider,
//twitterProvider: TwitterProvider,
): SocialProviderRegistry = {
SocialProviderRegistry(Seq(
//googleProvider,
facebookProvider //,
//twitterProvider,
))
}
@Provides
def provideAuthenticatorService(
fingerprintGenerator: FingerprintGenerator,
idGenerator: IDGenerator,
configuration: Configuration,
clock: Clock): AuthenticatorService[JWTAuthenticator] = {
val settings = JWTAuthenticatorSettings(
sharedSecret = configuration.getString("play.crypto.secret").get)
new JWTAuthenticatorService(
settings = settings,
repository = None,
authenticatorEncoder = new Base64AuthenticatorEncoder,
idGenerator = idGenerator,
clock = Clock())
}
@Provides
def provideAuthInfoRepository(
passwordInfoDAO: DelegableAuthInfoDAO[PasswordInfo]): AuthInfoRepository = {
new DelegableAuthInfoRepository(passwordInfoDAO)
}
@Provides
def providePasswordHasherRegistry(passwordHasher: PasswordHasher): PasswordHasherRegistry = {
new PasswordHasherRegistry(passwordHasher)
}
@Provides
def provideCredentialsProvider(
authInfoRepository: AuthInfoRepository,
passwordHasherRegistry: PasswordHasherRegistry): CredentialsProvider = {
new CredentialsProvider(authInfoRepository, passwordHasherRegistry)
}
@Provides
def provideAvatarService(httpLayer: HTTPLayer): AvatarService = new GravatarService(httpLayer)
}
That’s the error I’m getting:
application - Unexpected provider error
com.mohiva.play.silhouette.impl.exceptions.OAuth2StateException: [Silhouette][CookieState] State cookie doesn't exists for name: OAuth2State
at com.mohiva.play.silhouette.impl.providers.oauth2.state.CookieStateProvider.clientState(CookieState.scala:194)
at com.mohiva.play.silhouette.impl.providers.oauth2.state.CookieStateProvider.validate(CookieState.scala:150)
at com.mohiva.play.silhouette.impl.providers.OAuth2Provider$class.authenticate(OAuth2Provider.scala:111)
at com.mohiva.play.silhouette.impl.providers.oauth2.FacebookProvider.authenticate(FacebookProvider.scala:114)
at controllers.SocialAuthController$$anonfun$authenticate$1.apply(SocialAuthController.scala:79)
at controllers.SocialAuthController$$anonfun$authenticate$1.apply(SocialAuthController.scala:75)
at play.api.mvc.Action$.invokeBlock(Action.scala:498)
at play.api.mvc.Action$.invokeBlock(Action.scala:495)
at play.api.mvc.ActionBuilder$$anon$2.apply(Action.scala:458)
at play.api.mvc.Action$$anonfun$apply$2$$anonfun$apply$5$$anonfun$apply$6.apply(Action.scala:112)
and this is my SocialAuthController:
class SocialAuthController @Inject() (
val messagesApi: MessagesApi,
silhouette: Silhouette[JWTEnv],
userService: AuthService,
authInfoRepository: AuthInfoRepository,
socialProviderRegistry: SocialProviderRegistry,
cache: CacheApi)
extends Controller with I18nSupport { //with Logger
/**
* Authenticates a user against a social provider.
*
* @param provider The ID of the provider to authenticate against.
* @return The result to display.
*/
def authenticate(provider: String) = Action.async { implicit request =>
println("ma da qui dentro passo? " + provider)
(socialProviderRegistry.get[SocialProvider](provider) match {
case Some(p: SocialProvider with CommonSocialProfileBuilder) =>
p.authenticate().flatMap {
case Left(result) => Future.successful(result)
case Right(authInfo) => for {
profile <- p.retrieveProfile(authInfo)
user <- userService.save(profile)
authInfo <- authInfoRepository.save(profile.loginInfo, authInfo)
authenticator <- silhouette.env.authenticatorService.create(profile.loginInfo)
value <- silhouette.env.authenticatorService.init(authenticator)
result <- silhouette.env.authenticatorService.embed(value, Ok)
} yield {
silhouette.env.eventBus.publish(LoginEvent(user, request))
//result
Ok(Json.obj("token" -> value))
}
}
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")))
}
}
}
Thanks in advance,
Stefano Tondo