How to implement multiple Silhouette authenticators?

I used play-silhouette-seed as a template for my application. Therefore, in my project, I use a cookie-based authenticator ( CookieAuthenticator ). This works absolutely fine, even for REST calls via JavaScript built into my Twirl template. However, now I want the REST soft calls to be soft for clients other than the browser. As a result, I would need to get the Set-Cookie: authenticator=... element Set-Cookie: authenticator=... for each response and set it as part of my request. In my JavaScript fragment, which is embedded in my Twirl template and displayed in the browser, this is not a problem, because I do not need to understand it, but for other clients (server, etc.) This causes headaches.

I want to implement the JWTAuthenticator now in addition to my CookieAuthenticator . Is this supported, or do I need to completely switch to JWTAuthenticator ? In addition, are separate actions necessary, although everything should be the same implementation, except for the authenticator?

+5
source share
1 answer

Yes, Silhouette allows you to implement multiple authenticators . Here you can implement the JWTAuthenticator , which provides its JWT authentication service along with your CookieAuthenticator :

  • As Douglas Liu already pointed out in a comment, you will need to create an additional environment type. He must connect Identity to the appropriate Authenticator .

For instance:

 trait CookieEnv extends Env { type I = Account type A = CookieAuthenticator } trait JWTEnv extends Env { type I = Account type A = JWTAuthenticator } 
  1. Implement JWT bindings in your Silhouette module. Please take a look at play-silhouette-angular-seed for a complete example.

For instance:

 class SilhouetteModule extends AbstractModule with ScalaModule { def configure() { bind[Silhouette[CookieEnv]].to[SilhouetteProvider[CookieEnv]] bind[Silhouette[JWTEnv]].to[SilhouetteProvider[JWTEnv]] // ... () } @Provides def provideCookieEnvironment( userService: AccountService, authenticatorService: AuthenticatorService[CookieAuthenticator], eventBus: EventBus): Environment[CookieEnv] = { Environment[CookieEnv]( userService, authenticatorService, Seq(), eventBus ) } @Provides def provideJWTEnvironment( userService: AccountService, authenticatorService: AuthenticatorService[JWTAuthenticator], eventBus: EventBus): Environment[JWTEnv] = { Environment[JWTEnv]( userService, authenticatorService, Seq(), eventBus ) } // ... @Provides def provideCookieAuthenticatorService( @Named("authenticator-cookie-signer") cookieSigner: CookieSigner, @Named("authenticator-crypter") crypter: Crypter, fingerprintGenerator: FingerprintGenerator, idGenerator: IDGenerator, configuration: Configuration, clock: Clock): AuthenticatorService[CookieAuthenticator] = { val config = configuration.underlying.as[CookieAuthenticatorSettings]("silhouette.authenticator") val encoder = new CrypterAuthenticatorEncoder(crypter) new CookieAuthenticatorService(config, None, cookieSigner, encoder, fingerprintGenerator, idGenerator, clock) } @Provides def provideJWTAuthenticatorService( @Named("authenticator-crypter") crypter: Crypter, idGenerator: IDGenerator, configuration: Configuration, clock: Clock): AuthenticatorService[JWTAuthenticator] = { val config = configuration.underlying.as[JWTAuthenticatorSettings]("silhouette.authenticator") val encoder = new CrypterAuthenticatorEncoder(crypter) new JWTAuthenticatorService(config, None, encoder, idGenerator, clock) } // ... } 
  1. Add JWTAuthenticator configuration options in silhouette.conf :

For instance:

 authenticator.fieldName = "X-Auth-Token" authenticator.requestParts = ["headers"] authenticator.issuerClaim = "Your fancy app" authenticator.authenticatorExpiry = 12 hours authenticator.sharedSecret = "!!!changeme!!!" 
  1. Create a separate route for authentication through the JWT:

For example, in your app.routes file add the following line:

 # JWT Authentication POST /api/jwt/authenticate controllers.auth.api.AuthController.authenticate 
  1. Finally, in the AuthController add the appropriate authenticate method.

Sample code (adapted from SignInController.scala ):

 implicit val dataReads = ( (__ \ 'email).read[String] and (__ \ 'password).read[String] and (__ \ 'rememberMe).read[Boolean] ) (SignInForm.SignInData.apply _) def authenticate = Action.async(parse.json) { implicit request => request.body.validate[SignInForm.SignInData].map { signInData => credentialsProvider.authenticate(Credentials(signInData.email, signInData.password)).flatMap { loginInfo => accountService.retrieve(loginInfo).flatMap { case Some(user) => silhouette.env.authenticatorService.create(loginInfo).map { case authenticator if signInData.rememberMe => val c = configuration.underlying authenticator.copy( expirationDateTime = clock.now + c.as[FiniteDuration]("silhouette.authenticator.rememberMe.authenticatorExpiry"), idleTimeout = c.getAs[FiniteDuration]("silhouette.authenticator.rememberMe.authenticatorIdleTimeout") ) case authenticator => authenticator }.flatMap { authenticator => Logger.info(s"User ${user._id} successfully authenticated.") silhouette.env.eventBus.publish(LoginEvent(user, request)) silhouette.env.authenticatorService.init(authenticator).map { token => Ok(Json.obj("token" -> token)) } } case None => Future.failed(new IdentityNotFoundException("Couldn't find user.")) } }.recover { /* Login did not succeed, because user provided invalid credentials. */ case e: ProviderException => Logger.info(s"Host ${request.remoteAddress} tried to login with invalid credentials (email: ${signInData.email}).") Unauthorized(Json.obj("error" -> Messages("error.invalidCredentials"))) } }.recoverTotal { case e: JsError => Logger.info(s"Host ${request.remoteAddress} sent invalid auth payload. Error: $e.") Future.successful(Unauthorized(Json.obj("error" -> Messages("error.invalidPayload")))) } } 
+1
source

Source: https://habr.com/ru/post/1264172/


All Articles