Play 2.6 Silhouette 5.0 Integration BearerToken + Custom Identity + Social + Custom config parser/loader


#1

I’m trying to use BearerTokenAuthenticator with Play Silhouette; however, my integration doesn’t seem to create calls to my remote service that manages my users. I’ve tried adding prints in my UserService and none of the methods are being invoked when a request comes.

I used the Play Silhouette seed as my starting point. I am at a complete loss now. Silhouette doesn’t seem to be making calls to my remote service, which should return the requester’s identity.

API calls containing my X-Auth-Token header generated by my remote service only gives me Authentication required responses.

Please guide me through as this is my first time using Silhouette and relatively a Play/Scala newbie.

Relevant code below

ChildController.scala

@Singleton
class ChildController @Inject() (silhouette: Silhouette[KerberosBearerTokenEnv],
                                    appConfig: AppConfig,
                                    val controllerComponents: ControllerComponents)
                                   (implicit ec: ExecutionContext) extends ParentController {

  def getStuff = silhouette.SecuredAction { implicit req =>
    Ok(Json.obj("identity" -> req.identity))
  }
}

UserService.scala

trait UserService extends IdentityService[KerberosUser] {
  // ...
}

@Singleton
class UserServiceImpl @Inject()(appConfig: AppConfig, ws: WSClient)(implicit ec: ExecutionContext) extends UserService {
  // ....
}

SilhouetteModule.scala

class SilhouetteModule extends ScalaModule {

  def configure(): Unit = {
    bind[Silhouette[KerberosBearerTokenEnv]].to[SilhouetteProvider[KerberosBearerTokenEnv]]
    bind[CacheLayer].to[PlayCacheLayer]
    bind[IdentityService[KerberosUser]].to[UserService]
    bind[UserService].to[UserServiceImpl]
    bind[IDGenerator].toInstance(new SecureRandomIDGenerator())
    bind[FingerprintGenerator].toInstance(new DefaultFingerprintGenerator(false))
    bind[EventBus].toInstance(EventBus())
    bind[Clock].toInstance(Clock())

    bind[AuthenticatorRepository[BearerTokenAuthenticator]].to[BearerTokenAuthenticatorDao]
    bind[DelegableAuthInfoDAO[PasswordInfo]].to[PasswordInfoDao]
    bind[DelegableAuthInfoDAO[OAuth1Info]].to[OAuth1InfoDao]
    bind[DelegableAuthInfoDAO[OAuth2Info]].to[OAuth2InfoDao]
  }

  @Provides
  def provideHTTPLayer(client: WSClient): HTTPLayer = new PlayHTTPLayer(client)

  @Provides
  def provideEnvironment(userService: UserService,
                         authenticatorService: AuthenticatorService[BearerTokenAuthenticator],
                         eventBus: EventBus): Environment[KerberosBearerTokenEnv] =
    Environment[KerberosBearerTokenEnv](userService, authenticatorService, Seq(), eventBus)


  @Provides
  def provideSocialProviderRegistry(facebookProvider: FacebookProvider,
                                    googleProvider: GoogleProvider,
                                    twitterProvider: TwitterProvider): SocialProviderRegistry =
    SocialProviderRegistry(Seq(
      facebookProvider,
      googleProvider,
      twitterProvider
    ))

  @Provides
  def provideAuthInfoRepository(passwordInfoDAO: DelegableAuthInfoDAO[PasswordInfo],
                                oauth1InfoDAO: DelegableAuthInfoDAO[OAuth1Info],
                                oauth2InfoDAO: DelegableAuthInfoDAO[OAuth2Info]): AuthInfoRepository =
    new DelegableAuthInfoRepository(passwordInfoDAO, oauth1InfoDAO, oauth2InfoDAO)

  @Provides
  def provideAuthenticatorService(cookieHeaderEncoding: CookieHeaderEncoding,
                                  fingerprintGenerator: FingerprintGenerator,
                                  authenticatorRepository: AuthenticatorRepository[BearerTokenAuthenticator],
                                  idGenerator: IDGenerator,
                                  clock: Clock): AuthenticatorService[BearerTokenAuthenticator] =
    new BearerTokenAuthenticatorService(BearerTokenAuthenticatorSettings(), authenticatorRepository, idGenerator, Clock())

  @Provides @Named("oauth1-token-secret-signer")
  def provideOAuth1TokenSecretSigner(appConfig: AppConfig): Signer =
    new JcaSigner(JcaSignerSettings(
      appConfig.config.getString("silhouette.oauth1.signer"),
      appConfig.config.getString("silhouette.oauth1.signerPepper")
    ))

  @Provides @Named("oauth1-token-secret-crypter")
  def provideOAuth1TokenSecretCrypter(appConfig: AppConfig): Crypter =
    new JcaCrypter(JcaCrypterSettings(appConfig.config.getString("silhouette.oauth1.crypter")))

  @Provides
  def provideOAuth1TokenSecretProvider(@Named("oauth1-token-secret-signer") signer: Signer,
                                       @Named("oauth1-token-secret-crypter") crypter: Crypter,
                                       appConfig: AppConfig,
                                       clock: Clock): OAuth1TokenSecretProvider = {
    new CookieSecretProvider(CookieSecretSettings(secureCookie = appConfig.config.getBoolean("silhouette.provider.secureCookie")), signer, crypter, Clock())
  }

  @Provides @Named("csrf-state-item-signer")
  def provideCSRFStateItemSigner(appConfig: AppConfig): Signer =
    new JcaSigner(JcaSignerSettings(
      appConfig.config.getString("silhouette.csrf.item.signer"),
      appConfig.config.getString("silhouette.csrf.item.signerPepper")
    ))

  @Provides
  def provideCsrfStateItemHandler(idGenerator: IDGenerator,
                                   @Named("csrf-state-item-signer") signer: Signer): CsrfStateItemHandler = {
    new CsrfStateItemHandler(CsrfStateSettings(), idGenerator, signer)
  }

  @Provides @Named("social-state-signer")
  def provideSocialStateSigner(appConfig: AppConfig): Signer =
    new JcaSigner(JcaSignerSettings(
      appConfig.config.getString("silhouette.socialStateHandler.signer"),
      appConfig.config.getString("silhouette.socialStateHandler.signerPepper")
    ))

  @Provides
  def provideSocialStateHandler(@Named("social-state-signer") signer: Signer,
                                csrfStateItemHandler: CsrfStateItemHandler): SocialStateHandler =
    new DefaultSocialStateHandler(Set(csrfStateItemHandler), signer)

  @Provides @Named("authenticator-signer")
  def provideAuthenticatorSigner(appConfig: AppConfig): Signer =
    new JcaSigner(JcaSignerSettings(
      appConfig.config.getString("silhouette.authenticator.signer"),
      appConfig.config.getString("silhouette.authenticator.signerPepper")
    ))

  @Provides @Named("authenticator-crypter")
  def provideAuthenticatorCrypter(appConfig: AppConfig): Crypter =
    new JcaCrypter(JcaCrypterSettings(appConfig.config.getString("silhouette.authenticator.crypter")))

  @Provides
  def providePasswordHasherRegistry(): PasswordHasherRegistry =
    PasswordHasherRegistry(new BCryptSha256PasswordHasher(), Seq(new BCryptPasswordHasher()))

  @Provides
  def provideCredentialsProvider(authInfoRepository: AuthInfoRepository,
                                 passwordHasherRegistry: PasswordHasherRegistry): CredentialsProvider =
    new CredentialsProvider(authInfoRepository, passwordHasherRegistry)

  @Provides
  def provideFacebookProvider(httpLayer: HTTPLayer,
                              appConfig: AppConfig,
                              socialStateHandler: SocialStateHandler): FacebookProvider =
    new FacebookProvider(httpLayer, socialStateHandler, OAuth2Settings(
      authorizationURL = Some(appConfig.config.getString("facebook.authorizationUrl")),
      accessTokenURL = appConfig.config.getString("facebook.accessTokenUrl"),
      redirectURL = Some(appConfig.config.getString("facebook.redirectUrl")),
      clientID = appConfig.config.getString("facebook.clientId"),
      clientSecret = appConfig.config.getString("facebook.clientSecret"),
      scope = Some(appConfig.config.getString("facebook.scope"))
    ))

  @Provides
  def provideGoogleProvider(httpLayer: HTTPLayer,
                            appConfig: AppConfig,
                            socialStateHandler: SocialStateHandler): GoogleProvider =
    new GoogleProvider(httpLayer, socialStateHandler, OAuth2Settings(
      authorizationURL = Some(appConfig.config.getString("google.authorizationUrl")),
      accessTokenURL = appConfig.config.getString("google.accessTokenUrl"),
      redirectURL = Some(appConfig.config.getString("google.redirectUrl")),
      clientID = appConfig.config.getString("google.clientId"),
      clientSecret = appConfig.config.getString("google.clientSecret"),
      scope = Some(appConfig.config.getString("google.scope"))
    ))

  @Provides
  def provideTwitterProvider(httpLayer: HTTPLayer,
                             appConfig: AppConfig,
                             oAuth1TokenSecretProvider: OAuth1TokenSecretProvider): TwitterProvider = {
    val settings = OAuth1Settings(
      requestTokenURL = appConfig.config.getString("twitter.requestTokenUrl"),
      accessTokenURL = appConfig.config.getString("twitter.accessTokenUrl"),
      authorizationURL = appConfig.config.getString("twitter.authorizationUrl"),
      callbackURL = appConfig.config.getString("twitter.callbackUrl"),
      consumerKey = appConfig.config.getString("twitter.consumerKey"),
      consumerSecret = appConfig.config.getString("twitter.consumerSecret"))

    new TwitterProvider(httpLayer, new PlayOAuth1Service(settings), oAuth1TokenSecretProvider, settings)
  }
}