Kerberos Authentication issue: SpnegoEngineException: No LoginModules configured for "
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.
- Block of code that makes the request:
wsClient.url(someUrl).withAuth(username =someUser, password = somePwd, WSAuthScheme.KERBEROS).post(someJsonBody)
- 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
- 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.
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.
@ignasi35 Looks like this issue should be transferred to https://github.com/playframework/play-ws
@ignasi35 Looks like this issue should be transferred to playframework/play-ws
I agree, let me handle it.
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()
}