JWT Authenticator
This authenticator uses JWT for authentication. The contents of the actual identity
are transported in the subject
claim, and are encrypted if you choose the statelessEncrypted
or
statelessEncryptedArbitrary
option.
Defaults
The authenticator stateless
and withBackingStore
methods default
to transporting it in the Authorization
header as a Bearer token. If you must transport it in an arbitrary header,
use the methods that end in Arbitrary
, i.e statelessArbitrary
.
For the cases of a custom header, JWT authenticator uses TSecJWTSettings
for configuration:
final case class TSecJWTSettings(
headerName: String = "X-TSec-JWT",
expirationTime: FiniteDuration,
maxIdle: Option[FiniteDuration]
)
And for storage, a AugmentedJWT[A, Id]
, where Id
is your Id type (i.e UUID, Int, etc).
Notes:
- Choose between one of HMACSHA256, HMACSHA384 or HMACSHA512. Recommended default: HMACSHA256.
- Not vulnerable to CSRF.
- Okay to use with
CORS
- Tsec jwts are typed, so not vulnerable to this
- Stateless or stateful.
Authenticator Creation
import cats.effect.IO
import cats.Id
import org.http4s.HttpService
import org.http4s.dsl.io._
import tsec.authentication._
import tsec.common.SecureRandomId
import tsec.mac.jca.{HMACSHA256, MacSigningKey}
import scala.concurrent.duration._
object jwtStatefulExample {
import http4sExamples.ExampleAuthHelpers._
val jwtStore =
dummyBackingStore[IO, SecureRandomId, AugmentedJWT[HMACSHA256, Int]](s => SecureRandomId.coerce(s.id))
//We create a way to store our users. You can attach this to say, your doobie accessor
val userStore: BackingStore[IO, Int, User] = dummyBackingStore[IO, Int, User](_.id)
//Our signing key. Instantiate in a safe way using .generateKey[F]
val signingKey: MacSigningKey[HMACSHA256] = HMACSHA256.generateKey[Id]
val jwtStatefulAuth =
JWTAuthenticator.backed.inBearerToken(
expiryDuration = 10.minutes, //Absolute expiration time
maxIdle = None,
tokenStore = jwtStore,
identityStore = userStore,
signingKey = signingKey
)
val Auth =
SecuredRequestHandler(jwtStatefulAuth)
/*
Now from here, if want want to create services, we simply use the following
(Note: Since the type of the service is HttpService[IO], we can mount it like any other endpoint!):
*/
val service: HttpService[IO] = Auth.liftService(TSecAuthService {
//Where user is the case class User above
case request@GET -> Root / "api" asAuthed user =>
/*
Note: The request is of type: SecuredRequest, which carries:
1. The request
2. The Authenticator (i.e token)
3. The identity (i.e in this case, User)
*/
val r: SecuredRequest[IO, User, AugmentedJWT[HMACSHA256, Int]] = request
Ok()
})
}