make Rho swagger.json not requier Auth

265 Views Asked by At

Faced a problem while trying to implement self-documenting API using Rho lib (https://github.com/http4s/rho) By requirements our Routes should be protected by Auth middleware (https://http4s.org/v0.21/auth/) and now swagger.json generated by Rho middleware is also required authentication. Here is the code:

def startApp(xa:Transactor[IO],appConfig: AppConfig): IO[ExitCode] = {
    val stream = for {
      authService <- Stream.eval(IO(new AuthService(appConfig)))
      routes <- Stream.eval(IO(appRoutes(xa,appConfig,authService.middleware())))
      ...
    } yield exitCode
      ...
  }


  def appRoutes(transactor: Transactor[IO],appConfig: AppConfig,authMiddleware: AuthMiddleware[IO,APIUser]): Kleisli[IO, Request[IO], Response[IO]] = {

    val service = Router[IO](
      baseDataPath -> authMiddleware.apply(
        AuthService.Auth.toService(BootstrapAPI.supportedAPI(transactor, appConfig).toRoutes(swaggerRhoMiddleware))))
  }

Is there any way to exclude swagger.json REST call from Auth protection?

1

There are 1 best solutions below

1
On

I override the SwaggerSupport and extract the swagger route.

import cats.effect.Sync
import cats.implicits.catsSyntaxOptionId
import org.http4s.rho.RhoMiddleware
import org.http4s.rho.RhoRoute
import org.http4s.rho.bits.PathAST.PathMatch
import org.http4s.rho.bits.PathAST.TypedPath
import org.http4s.rho.swagger.DefaultSwaggerFormats
import org.http4s.rho.swagger.SwaggerSupport
import org.http4s.rho.swagger.models._
import shapeless._

import scala.collection.immutable.Seq
import scala.reflect.runtime.universe._

object CustomSwaggerSupport {
  def apply[F[_]: Sync](implicit etag: WeakTypeTag[F[_]]): CustomSwaggerSupport[F] = new CustomSwaggerSupport[F] {}
}

abstract class CustomSwaggerSupport[F[_]](implicit F: Sync[F], etag: WeakTypeTag[F[_]]) extends SwaggerSupport[F] {

  /**
    * Create a RhoMiddleware adding a route to get the Swagger json file
    * representing the full API
    */
  def createRhoMiddlewares: (RhoMiddleware[F], RhoMiddleware[F]) =
    ({ routes => routes }, { routes =>
      lazy val swaggerSpec: Swagger =
        createSwagger(
          swaggerFormats = DefaultSwaggerFormats,
          apiInfo = Info(
            title = "API",
            version = "1.0.0"
          ),
          host = None,
          basePath = "/".some,
          schemes = List(Scheme.HTTP),
          consumes = Nil,
          produces = Nil,
          security = List(SecurityRequirement("bearer", List())),
          securityDefinitions = Map(
            "bearer" -> ApiKeyAuthDefinition("Authorization", In.HEADER)
          ),
          tags = Nil,
          vendorExtensions = Map.empty
        )(
          routes
        )

      lazy val swaggerRoute: Seq[RhoRoute[F, _ <: HList]] =
        createSwaggerRoute(swaggerSpec, TypedPath(PathMatch("swagger.json"))).getRoutes

      swaggerRoute
    })
}

Then I use it like this:

  // Create a middleware that will transform RhoService into HttpService with attached Swagger definition
  val (swaggerMiddleware, swaggerRoutes): (RhoMiddleware[IO], RhoMiddleware[IO]) = CustomSwaggerSupport
    .apply[IO]
    .createRhoMiddlewares

  lazy val protectedRoutes: HttpRoutes[IO] = 
    routes.toRoutes(swaggerRoutes) <+> authMiddleware
      .apply(Auth.toService(routes.toRoutes(swaggerMiddleware)))