andreamarcolin / oidc4s   0.1.0

Apache License 2.0 GitHub

A OpenID Connect token verifier for http4s

Scala versions: 3.x 2.13

oidc4s

CI Status Scala Steward badge Cats friendly

A OpenID Connect JWT verification library supporting JWK, JWA, JWE and configuration via OpenID Connect Discovery.

This library is based on cats, cats-effect, sttp, circe and jwt-scala. First-class integration with http4s is implemented by the dedicated oidc4s-http4s module via a http4s AuthMiddleware.

Published both for Scala 3 and Scala 2.13.

Getting Started

To use oidc4s you need to add the following to your build.sbt:

libraryDependencies ++= Seq(
  // Start with this one
  "io.github.andreamarcolin" %% "oidc4s-core" % "<version>",

  // If you are planning to make use of the http4s integration, also add this one
  "io.github.andreamarcolin" %% "oidc4s-http4s" % "<version>"
)

Creating a verifier

import cats.effect._
import cats.syntax.all._
import oidc4s._
import org.http4s.ember.client.EmberClientBuilder
import sttp.client3._
import sttp.client3.http4s.Http4sBackend

for {
  client   <- EmberClientBuilder.default[IO].build.map(Http4sBackend.usingClient(_))
  verifier <- OidcJwtVerifier.create[IO](client, uri"http://localhost:8080/realms/test")
} yield verifier

Note OidcJwtVerifier.create requires a log4cats LoggerFactory implicit instance (see here for details).
If your project already depends on log4cats-slf4j, you can just add the following import:
import org.typelevel.log4cats.slf4j._

Verifying tokens (and extracting custom claims)

import io.circe.parser.parse

for {
  claims <- verifier.verifyAndExtract(token)
  content = parse(claims.content).flatMap(_.hcursor.get[String]("custom_claim"))
} yield content

Http4s integration

import cats.effect._
import cats.syntax.all._
import io.circe._
import org.http4s.{Uri => _, _}
import org.http4s.dsl.io._
import org.http4s.server._
import sttp.client3._

val authedRoutes: AuthedRoutes[String, IO] =
  AuthedRoutes.of[String, IO] {
    case GET -> Root as userId => Ok(userId)
  }

val extractor: JwtClaim => Either[io.circe.Error, String] =
  c => parse(c.content).flatMap(_.hcursor.get[String]("custom_claim"))

val middleware: AuthMiddleware[IO, String] =
  OidcJwtMiddleware(verifier, uri"http://localhost:8080/realms/test", extractor, _.toString)

val routes: HttpRoutes[IO] = middleware(authedRoutes)

Notes

Note that oidc4s is pre-1.0 software and is still undergoing active development. New versions are not binary compatible with prior versions, although in most cases user code will be source compatible. See here for more details about the adopted version policy