membership-attribute-service/app/actions/AuthAndBackendViaIdapiAction.scala (65 lines of code) (raw):
package actions
import com.gu.identity.auth.{AccessScope, IdapiUserCredentials, OktaUserCredentials}
import com.gu.identity.{IdapiService, RedirectAdviceResponse, SignedInRecently}
import com.gu.monitoring.SafeLogger.LogPrefix
import components.{TouchpointBackends, TouchpointComponents}
import filters.TestUserChecker
import models.{ApiError, UserFromToken}
import play.api.mvc.{ActionRefiner, Request, Result, Results}
import services.UserAndCredentials
import scala.concurrent.{ExecutionContext, Future}
class AuthAndBackendViaIdapiAction(
touchpointBackends: TouchpointBackends,
howToHandleRecencyOfSignedIn: HowToHandleRecencyOfSignedIn,
testUserChecker: TestUserChecker,
requiredScopes: List[AccessScope],
)(implicit
ex: ExecutionContext,
) extends ActionRefiner[Request, AuthAndBackendRequest] {
override protected val executionContext: ExecutionContext = ex
override protected def refine[A](request: Request[A]): Future[Either[Result, AuthAndBackendRequest[A]]] = {
val userAndCredentials = touchpointBackends.normal.identityAuthService.userAndCredentials(request, requiredScopes)
userAndCredentials flatMap {
case Left(error) => Future.successful(Left(ApiError.apiErrorToResult(error)))
case Right(UserAndCredentials(user, _: OktaUserCredentials)) => Future.successful(Right(oktaRefine(request, user)))
case Right(UserAndCredentials(user, _: IdapiUserCredentials)) =>
implicit val logPrefix: LogPrefix = user.logPrefix
idapiRefine(request, user.primaryEmailAddress)
}
}
private def backend(primaryEmailAddress: String)(implicit logPrefix: LogPrefix): TouchpointComponents =
if (testUserChecker.isTestUser(primaryEmailAddress)) {
touchpointBackends.test
} else {
touchpointBackends.normal
}
private def oktaRefine[A](request: Request[A], user: UserFromToken): AuthAndBackendRequest[A] =
new AuthAndBackendRequest(
redirectAdvice = RedirectAdviceResponse(
signInStatus = SignedInRecently,
userId = Some(user.identityId),
displayName = user.username,
emailValidated = user.userEmailValidated,
// Okta reauthentication redirect will be managed by the API client
redirect = None,
),
touchpoint = backend(user.primaryEmailAddress)(user.logPrefix),
request,
)
private def idapiRefine[A](request: Request[A], primaryEmailAddress: String)(implicit
logPrefix: LogPrefix,
): Future[Either[Result, AuthAndBackendRequest[A]]] =
touchpointBackends.normal.idapiService.RedirectAdvice
.getRedirectAdvice(
request.headers.get(IdapiService.HeaderNameCookie).getOrElse(""),
request.headers.get(IdapiService.HeaderNameIdapiForwardedScope),
)
.map(redirectAdvice =>
howToHandleRecencyOfSignedIn match {
case Return401IfNotSignedInRecently if redirectAdvice.signInStatus != SignedInRecently =>
Left(Results.Unauthorized.withHeaders(("X-GU-IDAPI-Redirect", redirectAdvice.redirect.map(_.url).getOrElse(""))))
case _ =>
val backendConf = backend(primaryEmailAddress)
Right(new AuthAndBackendRequest[A](redirectAdvice, backendConf, request))
},
)
}