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

com.gu.identity.play.IdapiPlayAuthService.scala Maven / Gradle / Ivy

There is a newer version: 4.33
Show newest version
package com.gu.identity.play

import cats.effect.IO
import cats.syntax.either._
import com.gu.identity.auth._
import com.gu.identity.model.User
import play.api.mvc.{Cookie, RequestHeader}

import scala.concurrent.ExecutionContext

// Class that builds upon functionality of IdapiPlayAuthService
// to make user authentication convenient in the context of a Play application.
// It handles extracting the user credentials from a Play request before authenticating them
// using the underlying IdapiPlayAuthService.
// Target client should be set if this service is being used to authenticate using crypto access tokens
// in addition to SC_GU_U cookies. See comments on UserCredentials.CryptoAccessToken for more context.
class IdapiPlayAuthService(idapiAuthService: IdapiAuthService, targetClient: Option[String]) {

  import IdapiPlayAuthService._

  // Authenticates the user and returns the respective identity id.
  // Use this if only authentication + identity id is required.
  // For more information see analogous method in IdentityService.
  //
  // UserCredentials are returned in addition to identity id as a convenience,
  // since business logic may depend on type of credentials extracted from request.
  @deprecated(
    "this function only looks for idapi credentials in the request, in the future only okta credentials will be supported"
  )
  def authenticateRequest(request: RequestHeader): IO[(IdapiUserCredentials, String)] =
    for {
      credentials <- getIdapiUserCredentialsFromRequest(request, targetClient)
      identityId <- idapiAuthService.authenticateUser(credentials)
    } yield (credentials, identityId)

  // Authenticates the user and returns the respective user.
  // Use this is authentication + user information (in addition to the identity id) is required.
  // For more information see analogous method in IdentityService.
  //
  // UserCredentials are returned in addition to the user as a convenience,
  // since business logic may depend on type of credentials extracted from request.
  @deprecated(
    "this function only looks for idapi credentials in the request, in the future only okta credentials will be supported"
  )
  def getUserFromRequest(request: RequestHeader): IO[(IdapiUserCredentials, User)] =
    for {
      credentials <- getIdapiUserCredentialsFromRequest(request, targetClient)
      user <- idapiAuthService.getUserFromCredentials(credentials)
    } yield (credentials, user)

  // Analogous to the authenticateRequest() method, but only attempts to use the SC_GU_U cookie.
  // Returning a Cookie (as modelled by Play) instead of just the cookie value
  // can be useful if other properties of the cookie are required by business logic.
  def authenticateRequestUsingSCGUUCookie(request: RequestHeader): IO[(Cookie, String)] =
    for {
      cookie <- getSCGUUCookieFromRequest(request)
      identityId <- idapiAuthService.authenticateUser(IdapiUserCredentials.SCGUUCookie(cookie.value))
    } yield (cookie, identityId)

  // Analogous to the getUserFromRequest() method, but only attempts to use the SC_GU_U cookie.
  // Returning a Cookie (as modelled by Play) instead of just the cookie value
  // can be useful if other properties of the cookie are required by business logic.
  def getUserFromRequestUsingSCGUUCookie(request: RequestHeader): IO[(Cookie, User)] =
    for {
      cookie <- getSCGUUCookieFromRequest(request)
      user <- idapiAuthService.getUserFromCredentials(IdapiUserCredentials.SCGUUCookie(cookie.value))
    } yield (cookie, user)

}

object IdapiPlayAuthService {

  case class UserCredentialsMissingError(message: String) extends Exception {
    override def getMessage: String = message
  }

  def getSCGUUCookieFromRequest(request: RequestHeader): IO[Cookie] =
    IO.fromEither(
      Either.fromOption(
        request.cookies.get("SC_GU_U"),
        UserCredentialsMissingError("SC_GU_U cookie not set")
      )
    )

  def getCryptoAccessTokenFromRequest(
      request: RequestHeader,
      targetClient: String
  ): IO[IdapiUserCredentials.CryptoAccessToken] =
    IO.fromEither(
      Either.fromOption(
        request.headers
          .get("GU-IdentityToken")
          .map(token => IdapiUserCredentials.CryptoAccessToken(token, targetClient)),
        UserCredentialsMissingError("GU-IdentityToken header not set")
      )
    )

  def getIdapiUserCredentialsFromRequest(
      request: RequestHeader,
      targetClient: Option[String]
  ): IO[IdapiUserCredentials] =
    getSCGUUCookieFromRequest(request)
      .redeemWith(
        // If no SC_GU_U cookie set, attempt to get crypto access token if target client is defined.
        err => {
          // If target client isn't set, then this indicates authentication with crypto access tokens is not supported.
          // In this case just return the original error.
          targetClient.fold(IO.raiseError[IdapiUserCredentials](err)) { client =>
            // Otherwise attempt to extract a crypto access toke for the target client.
            getCryptoAccessTokenFromRequest(request, client)
              .handleErrorWith { _ =>
                // At this point we know attempts were made to extract both credential types,
                // and that both attempts were unsuccessful due to the respective credentials not being present.
                IO.raiseError(UserCredentialsMissingError("neither SC_GU_U cookie or GU-IdentityToken header set"))
              }
          }
        },
        cookie => IO(IdapiUserCredentials.SCGUUCookie(cookie.value))
      )

  def unsafeInit[A <: AccessClaims, I <: IdentityClaims](
      idapiAuthConfig: IdapiAuthConfig
  )(implicit ec: ExecutionContext): IdapiPlayAuthService = {
    val idapiAuthService =
      IdapiAuthService.unsafeInit(IdapiAuthConfig(idapiAuthConfig.identityApiUri, idapiAuthConfig.accessToken))
    new IdapiPlayAuthService(idapiAuthService, idapiAuthConfig.targetClient)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy