play-ws icon indicating copy to clipboard operation
play-ws copied to clipboard

Kerberos Authentication issue: SpnegoEngineException: No LoginModules configured for "

Open sandeepsingh74 opened this issue 5 years ago • 4 comments

Play Version

2.8.1

API

Scala

Operating System

CentOS Linux release 7.7.1908 (Core)

JDK

OpenJDK Runtime Environment (build 1.8.0_232-b09)

Library Dependencies

N/A

Expected Behavior

The playframework doc says it supports KERBEROS AuthScheme (https://www.playframework.com/documentation/2.8.x/ScalaWS#Configuring-WS). I am trying to connect an end-point which requires Kerberos authentication from a Play web application.

  1. Block of code that makes the request:
wsClient.url(someUrl).withAuth(username =someUser, password = somePwd, WSAuthScheme.KERBEROS).post(someJsonBody)
  1. My JVM options to specify Jaas login configuration: -Djava.security.auth.login.config=/path/to/jaas.conf Content of jaas.conf:
SomeContext {
     com.sun.security.auth.module.Krb5LoginModule required
     useKeyTab=false
     useTicketCache=true
     ticketCache="/dev/shm/ccache"
     principal="[email protected]";
};

I expected play framework to use my jaas.conf and load Kerberos login module. And then use the ticket cache from the location specified in ticketCache above. Prior to running the code I had performed kinit and verified that the ticket cache had the valid token.

Actual Behavior

  1. When I post the request, I get an Exception play.shaded.ahc.org.asynchttpclient.spnego.SpnegoEngineException: No LoginModules configured for "
javax.security.auth.login.LoginContext.init(LoginContext.java:264)
javax.security.auth.login.LoginContext.<init>(LoginContext.java:512)
play.shaded.ahc.org.asynchttpclient.spnego.SpnegoEngine.generateToken(SpnegoEngine.java:183)
play.shaded.ahc.org.asynchttpclient.util.AuthenticatorUtils.perConnectionAuthorizationHeader(AuthenticatorUtils.java:185)
play.shaded.ahc.org.asynchttpclient.netty.request.NettyRequestSender.sendRequestWithNewChannel(NettyRequestSender.java:279)
play.shaded.ahc.org.asynchttpclient.netty.request.NettyRequestSender.sendRequestWithCertainForceConnect(NettyRequestSender.java:142)
play.shaded.ahc.org.asynchttpclient.netty.request.NettyRequestSender.sendRequest(NettyRequestSender.java:113)
play.shaded.ahc.org.asynchttpclient.DefaultAsyncHttpClient.execute(DefaultAsyncHttpClient.java:241)
play.shaded.ahc.org.asynchttpclient.DefaultAsyncHttpClient.executeRequest(DefaultAsyncHttpClient.java:210)
play.api.libs.ws.ahc.StandaloneAhcWSClient.execute(StandaloneAhcWSClient.scala:90)
play.api.libs.ws.ahc.StandaloneAhcWSRequest.$anonfun$execute$1(StandaloneAhcWSRequest.scala:216)
play.api.libs.ws.WSRequestExecutor$$anon$2.apply(WSRequestFilter.scala:52)
play.api.libs.ws.WSRequestExecutor$$anon$2.apply(WSRequestFilter.scala:51)
play.api.libs.ws.ahc.StandaloneAhcWSRequest.execute(StandaloneAhcWSRequest.scala:219)
play.api.libs.ws.ahc.AhcWSRequest.execute(AhcWSRequest.scala:264)
play.api.libs.ws.ahc.AhcWSRequest.execute(AhcWSRequest.scala:260)
play.api.libs.ws.ahc.AhcWSRequest.post(AhcWSRequest.scala:192)

Is there any guide or example on how to use the WSAuthScheme.KERBEROS authentication? There are no examples or any config references for Kerberos authentication in Play framework doc (https://www.playframework.com/documentation/2.8.x/ScalaWS#Configuring-WS).

After tracing the underlying asynchttp client code, I noticed that the following line sets the loginContextName to empty string. https://github.com/AsyncHttpClient/async-http-client/blob/master/client/src/main/java/org/asynchttpclient/spnego/SpnegoEngine.java#L178 Which is responsible for the exception: play.shaded.ahc.org.asynchttpclient.spnego.SpnegoEngineException: No LoginModules configured for "

Is there a way to configure Play server, so the loginContextName is set to a value, say "SomeContext" as in my jaas.conf? The bug seems to be with play framework not specifying a default loginContextName or allowing user to specify one.

sandeepsingh74 avatar Feb 19 '20 22:02 sandeepsingh74

I experience the same issue when upgrading from Play 2.6 to Play 2.8 on Java 11

I downgraded to compile group: 'com.typesafe.play', name: 'play-ahc-ws-standalone_2.12', version: '1.1.14' Which does work, so this seems like a regression to me.

hhagblom avatar Aug 31 '20 07:08 hhagblom

@ignasi35 Looks like this issue should be transferred to https://github.com/playframework/play-ws

ihostage avatar Sep 03 '20 13:09 ihostage

@ignasi35 Looks like this issue should be transferred to playframework/play-ws

I agree, let me handle it.

ignasi35 avatar Sep 03 '20 15:09 ignasi35

Here are workaround which i'm found

// create custom WSClient For kerberos request. RELEASE after all work!
protected def getStandAloneWsClient(login: String, password: String)
                                     (implicit materializer: Materializer): AhcWSClient = {
      import scala.jdk.CollectionConverters._
      // custom realm building for this client
      val realmBuilder = new Realm.Builder(login, password)
          .setScheme(AuthScheme.KERBEROS)
          .setUsePreemptiveAuth(true)
          // tried if can't find any LoggingContext, src - play.shaded.ahc.org.asynchttpclient.spnego.SpnegoEngine.getLoginConfiguration
          // should not be empty!
          .setCustomLoginConfig(
              Map[String, String](
                  "useKeyTab" -> "false",
                  //                        "useTicketCache" -> "true",
                  //                        "ticketCache" -> "/dev/shm/ccache",
                  //                        "principal" -> principal,
              ).asJava
          )
      val configBuilder = new AhcConfigBuilder(AhcWSClientConfigFactory.forConfig())
      configBuilder.builder.setRealm(realmBuilder)
      val config: AsyncHttpClientConfig = configBuilder.build()
      // from play.api.libs.ws.ahc.StandaloneAhcWSClient.apply for setting Realm throught wrappers layers
      val asyncHttpClient = new DefaultAsyncHttpClient(config)
      val wsClient = new StandaloneAhcWSClient(asyncHttpClient)
      new AhcWSClient(wsClient)
  }

And then use as

val wsClient = getStandAloneWsClient(authSettings)
val responseF = wsClient.url(url).get
 // free resources
responseF.onComplete(_ => wsClient.close())
responseF

Don't use .withAuth on this client or it will be overwritten again! This is looks like source of problem btw

// play/api/libs/ws/ahc/StandaloneAhcWSRequest.scala:276
 auth.foreach { data =>
      val realm = auth(data._1, data._2, authScheme(data._3))
      builder.setRealm(realm)
    }
    
 //  play.api.libs.ws.ahc.StandaloneAhcWSRequest#auth 
   /**
   * Add http auth headers. Defaults to HTTP Basic.
   */
  private[libs] def auth(username: String, password: String, scheme: Realm.AuthScheme = Realm.AuthScheme.BASIC): Realm = {
    val usePreemptiveAuth = scheme match {
      case AuthScheme.DIGEST => false
      case _ => true
    }


  // totibi: What about loggin context here?
    new Realm.Builder(username, password)
      .setScheme(scheme)
      .setUsePreemptiveAuth(usePreemptiveAuth)
      .build()
  }

totibi avatar May 18 '21 16:05 totibi