All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cuma-sso-backend-client_3.0.7.3.source-code.SsoJwtReader.scala Maven / Gradle / Ivy

// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package lucuma.sso.client

import cats.data.*
import cats.effect.Concurrent
import cats.implicits.*
import lucuma.core.model.ServiceUser
import lucuma.core.model.StandardUser
import lucuma.core.model.User
import lucuma.sso.client.util.JwtDecoder
import org.http4s.Credentials
import org.http4s.EntityDecoder
import org.http4s.InvalidMessageBodyFailure
import org.http4s.Request
import org.http4s.Response
import org.http4s.headers.Authorization
import org.typelevel.ci.CIString
import pdi.jwt.exceptions.JwtException

trait SsoJwtReader[F[_]] { outer =>

  /**
   * Retrieve the JWT from the `Authorization: Bearer ` header, if present, raising an
   * error in `F` if otherwise.
   */
  def findClaim(req: Request[F]): F[Option[SsoJwtClaim]]

  /** Retrieve the JWT from the `Authorization: Bearer ` header, if present. */
  def attemptFindClaim(req: Request[F]): F[Option[Either[JwtException, SsoJwtClaim]]]

  /** Retrieve the User from the `Authorization: Bearer ` header, if present. */
  def findUser(req: Request[F]): F[Option[User]]

  /** Retrieve the StandardUser from the `Authorization: Bearer ` header, if present. */
  def findStandardUser(req: Request[F]): F[Option[StandardUser]]

  /** Retrieve the ServiceUser from the `Authorization: Bearer ` header, if present. */
  def findServiceUser(req: Request[F]): F[Option[ServiceUser]]

  /** Retrieve the JWT from the response body. */
  def findClaim(res: Response[F]): F[SsoJwtClaim]

  // import this!
  implicit def entityDecoder: EntityDecoder[F, SsoJwtClaim]


  def decodeClaim(jwt: String): F[SsoJwtClaim]

  def decodeUser(jwt: String): F[User]

  def decodeStandardUser(jwt: String): F[StandardUser]

}

object SsoJwtReader {

  private[client] val JwtCookie  = "lucuma-jwt"
  private[client] val lucumaUser = SsoJwtClaim.lucumaUser

  def apply[F[_]: Concurrent](jwtDecoder: JwtDecoder[F]): SsoJwtReader[F] =
    new SsoJwtReader[F] {

      implicit val entityDecoder: EntityDecoder[F, SsoJwtClaim] =
        EntityDecoder.text[F].flatMapR { token =>
          EitherT(jwtDecoder.attemptDecode(token))
            .map(SsoJwtClaim(_))
            .leftMap {
              case e: Exception => InvalidMessageBodyFailure(s"Invalid or missing JWT.", Some(e))
            }
        }

      val Bearer = CIString("Bearer")

      def decodeClaim(jwt: String): F[SsoJwtClaim] =
        jwtDecoder.decode(jwt).map(SsoJwtClaim(_))

      def decodeUser(jwt: String): F[User] =
        decodeClaim(jwt).map(_.getUser).flatMap(_.liftTo[F])

      def decodeStandardUser(jwt: String): F[StandardUser] =
        decodeUser(jwt).flatMap {
          case u: StandardUser => u.pure[F]
          case _ => Concurrent[F].raiseError(new RuntimeException("Not a standard user."))
        }

      def attemptFindClaim(req: Request[F]): F[Option[Either[JwtException, SsoJwtClaim]]] =
        findBearerAuthorization(req).flatMap {
          case None    => none.pure[F]
          case Some(c) => jwtDecoder.attemptDecode(c).map(_.map(SsoJwtClaim(_)).some)
        }

      def findBearerAuthorization(req: Request[F]): F[Option[String]]  =
        req.headers.get[Authorization].collect {
          case Authorization(Credentials.Token(Bearer, token)) => token
        } .pure[F]

      def findClaim(req: Request[F]): F[Option[SsoJwtClaim]] =
        OptionT(findBearerAuthorization(req))
          .flatMapF(token => jwtDecoder.decodeOption(token).map(_.map(SsoJwtClaim(_))))
          .value

      def findClaim(res: Response[F]): F[SsoJwtClaim] =
        res.as[SsoJwtClaim]

      def findUser(req: Request[F]): F[Option[User]] =
        findClaim(req).map(_.flatMap(_.getUser.toOption))

      def findServiceUser(req: Request[F]): F[Option[ServiceUser]] =
        findUser(req).map(_.collect { case u: ServiceUser => u })

      def findStandardUser(req: Request[F]): F[Option[StandardUser]] =
        findUser(req).map(_.collect { case u: StandardUser => u })


    }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy