This library provides SPNEGO Authentication as a middleware for http4s.
Project is an adaptation of akka-http-spnego, but for http4s.
- Add library into your dependencies:
libraryDependencies += "io.github.novakov-alexey" % "http4s-spnego_2.13" % "<version>"
or
libraryDependencies += "io.github.novakov-alexey" % "http4s-spnego_2.12" % "<version>"
- Instantiate
Spnego
usingSpnegoConfig
andJaasConfig
case classes:
import io.github.novakovalexey.http4s.spnego.SpnegoConfig
import io.github.novakovalexey.http4s.spnego.JaasConfig
val realm = "EXAMPLE.ORG"
val principal = s"HTTP/myservice@$realm"
val keytab = "/etc/krb5.keytab"
val debug = true
val domain = Some("myservice")
val path: Option[String] = None
val tokenValidity: FiniteDuration = 3600.seconds
val cookieName = "http4s.spnego"
val cfg = SpnegoConfig(
realm,
principal,
"secret",
domain,
path,
tokenValidity,
cookieName,
Some(JaasConfig(keytab, debug, None)) // option 1
)
val spnego = Spnego[IO](cfg)
JaasConfig can be also set to None
value (option 2) in order pass JaasConfig via standard JAAS file. For example:
System.setProperty("java.security.auth.login.config", "test-server/src/main/resources/server-jaas.conf")
See example of standard JAAS file at test-server/src/main/resources/server-jaas.conf
- Wrap AuthedRoutes with spnego#middleware, so that you can get an instance of SPNEGO token. Wrapped routes will be called successfully only if SPNEGO authentication succeeded.
import cats.effect.Sync
import cats.implicits._
import org.http4s.{AuthedRoutes, HttpRoutes}
import org.http4s.dsl.Http4sDsl
class LoginEndpoint[F[_]: Sync](spnego: Spnego[F]) extends Http4sDsl[F] {
val routes: HttpRoutes[F] =
spnego(AuthedRoutes.of[Token, F] {
case GET -> Root as token =>
Ok(s"This page is protected using HTTP SPNEGO authentication; logged in as ${token.principal}")
.map(_.addCookie(spnego.signCookie(token)))
})
}
- Use routes in your server:
val login = new LoginEndpoint[IO](spnego)
val finalHttpApp = Logger.httpApp(logHeaders = true, logBody = true)(login)
BlazeServerBuilder[F]
.bindHttp(8080, "0.0.0.0")
.withHttpApp(finalHttpApp)
.serve
If you need to add more fields into JWT token, there is a special String field Token.attributes
:
// this route is used to create cookie once SPNEGO is done
case GET -> Root as token =>
val id = "groupId=1"
Ok(s"logged in as ${token.principal}")
.map(_.addCookie(spnego.signCookie(token.copy(attributes = id))))
// this route takes already authenticated user and its token
case POST -> Root as token =>
val id = token.attributes
// do something with id
Ok("processed")
Added field will be used to create a JWT signature.
See tests and test-server module for more examples.
- Make sure Kerberos is installed and configured for your server and client machines.
- Configure test server with proper realm, principal, keytab path (see config above)
- Authenticate client via
kinit
CLI tool to the same realm used for the server side - Start test server:
sbt 'project test-server' run
- Use
curl
or Web-Browser to initiate a negotiation request (google for that or try this link). In case you want to test withcurl
, there is command for that:
curl -k --negotiate -u : -b ~/cookiejar.txt -c ~/cookiejar.txt http://<yourserver>:8080/