Repository: hmrc/pertax-frontend
Pull request #900: DDCNL-10231 Moved trusted helper retrieval to call fandf
Author: login: Nj147
Created: 2025-04-09T07:02:52Z | Merged: 2025-04-14T13:48:57Z



Diff:
diff --git a/a11y/views/testSpec.scala b/a11y/views/testSpec.scala
index 27c5799a8..c7a6a15aa 100644
--- a/a11y/views/testSpec.scala
+++ b/a11y/views/testSpec.scala
@@ -31,7 +31,7 @@ import play.api.test.Helpers.{GET, contentAsString, defaultAwaitTimeout, route,
 import testUtils.{A11ySpec, FileHelper}
 import uk.gov.hmrc.http.SessionKeys
 import uk.gov.hmrc.mongoFeatureToggles.model.FeatureFlag
-import uk.gov.hmrc.sca.models.{MenuItemConfig, PtaMinMenuConfig, UrBanner, WrapperDataResponse}
+import uk.gov.hmrc.sca.models.{MenuItemConfig, PtaMinMenuConfig, UrBanner, Webchat, WrapperDataResponse}
 import uk.gov.hmrc.scalatestaccessibilitylinter.domain.OutputFormat
 
 import java.util.UUID
@@ -180,7 +180,12 @@ class testSpec extends A11ySpec {
   )
   val wrapperDataResponse: String          = Json
     .toJson(
-      WrapperDataResponse(menuWrapperData, PtaMinMenuConfig("MenuName", "BackName"), List.empty[UrBanner])
+      WrapperDataResponse(
+        menuWrapperData,
+        PtaMinMenuConfig("MenuName", "BackName"),
+        List.empty[UrBanner],
+        List.empty[Webchat]
+      )
     )
     .toString
 
@@ -207,6 +212,8 @@ class testSpec extends A11ySpec {
         .get(urlMatching("/single-customer-account-wrapper-data/message-data.*"))
         .willReturn(ok(s"$messageCount"))
     )
+
+    server.stubFor(get(urlEqualTo("/delegation/get")).willReturn(notFound()))
   }
 
   override implicit lazy val app: Application = localGuiceApplicationBuilder()
@@ -215,6 +222,7 @@ class testSpec extends A11ySpec {
       "microservice.services.breathing-space-if-proxy.timeoutInMilliseconds" -> 4000,
       "microservice.services.taxcalc-frontend.port"                          -> server.port(),
       "microservice.services.tai.port"                                       -> server.port(),
+      "microservice.services.fandf.port"                                     -> server.port(),
       "sca-wrapper.services.single-customer-account-wrapper-data.url"        -> s"http://localhost:${server.port()}"
     )
     .build()
diff --git a/app/config/ConfigDecorator.scala b/app/config/ConfigDecorator.scala
index 68d977b45..e803e7cb2 100644
--- a/app/config/ConfigDecorator.scala
+++ b/app/config/ConfigDecorator.scala
@@ -86,6 +86,7 @@ class ConfigDecorator @Inject() (
   private lazy val nispFrontendHost: String                  = getExternalUrl(s"nisp-frontend.host").getOrElse("")
   private lazy val dfsFrontendHost: String                   = getExternalUrl(s"dfs-digital-forms-frontend.host").getOrElse("")
   private lazy val fandfFrontendHost: String                 = getExternalUrl(s"fandf-frontend.host").getOrElse("")
+  lazy val fandfHost: String                                 = servicesConfig.baseUrl("fandf")
   private lazy val agentClientManagementFrontendHost: String =
     getExternalUrl("agent-client-management-frontend.host").getOrElse("")
 
diff --git a/app/connectors/FandFConnector.scala b/app/connectors/FandFConnector.scala
new file mode 100644
index 000000000..9a28d5cc9
--- /dev/null
+++ b/app/connectors/FandFConnector.scala
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2025 HM Revenue & Customs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package connectors
+
+import cats.data.EitherT
+import com.google.inject.Inject
+import config.ConfigDecorator
+import play.api.Logging
+import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
+import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse, StringContextOps, UpstreamErrorResponse}
+import uk.gov.hmrc.http.client.HttpClientV2
+import uk.gov.hmrc.http.HttpReads.Implicits._
+
+import scala.concurrent.{ExecutionContext, Future}
+
+class FandFConnector @Inject() (
+  val httpClient: HttpClientV2,
+  configDecorator: ConfigDecorator,
+  httpClientResponse: HttpClientResponse
+)(implicit val ec: ExecutionContext)
+    extends Logging {
+
+  def getTrustedHelper()(implicit hc: HeaderCarrier): EitherT[Future, UpstreamErrorResponse, Option[TrustedHelper]] =
+    httpClientResponse
+      .read(
+        httpClient
+          .get(url"${configDecorator.fandfHost}/delegation/get")
+          .execute[Either[UpstreamErrorResponse, HttpResponse]]
+      )
+      .map(_.json.asOpt[TrustedHelper](uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper.reads))
+}
diff --git a/app/controllers/auth/AuthRetrievals.scala b/app/controllers/auth/AuthRetrievals.scala
index 184337df2..4a55c3c54 100644
--- a/app/controllers/auth/AuthRetrievals.scala
+++ b/app/controllers/auth/AuthRetrievals.scala
@@ -23,6 +23,7 @@ import io.lemonlabs.uri.Url
 import play.api.Logging
 import play.api.mvc._
 import repositories.JourneyCacheRepository
+import services.FandfService
 import uk.gov.hmrc.auth.core._
 import uk.gov.hmrc.auth.core.retrieve.v2.{Retrievals, TrustedHelper}
 import uk.gov.hmrc.auth.core.retrieve.{Credentials, Retrieval, ~}
@@ -35,7 +36,8 @@ import scala.concurrent.{ExecutionContext, Future}
 class AuthRetrievalsImpl @Inject() (
   val authConnector: AuthConnector,
   mcc: MessagesControllerComponents,
-  journeyCacheRepository: JourneyCacheRepository
+  journeyCacheRepository: JourneyCacheRepository,
+  fandfService: FandfService
 )(implicit ec: ExecutionContext, configDecorator: ConfigDecorator)
     extends AuthRetrievals
     with AuthorisedFunctions
@@ -48,7 +50,10 @@ class AuthRetrievalsImpl @Inject() (
     } yield res.replaceParams("redirect_uri", configDecorator.pertaxFrontendBackLink).toString()
 
   private type RetrievalsType = Option[String] ~ Option[AffinityGroup] ~ Enrolments ~ Option[Credentials] ~
-    Option[String] ~ ConfidenceLevel ~ Option[TrustedHelper] ~ Option[String]
+    Option[String] ~ ConfidenceLevel ~ Option[String]
+
+  private def getTrustedHelper(implicit headerCarrier: HeaderCarrier): Future[Option[TrustedHelper]] =
+    fandfService.getTrustedHelper()
 
   //scalastyle:off cyclomatic.complexity
   override def invokeBlock[A](request: Request[A], block: AuthenticatedRequest[A] => Future[Result]): Future[Result] = {
@@ -57,44 +62,45 @@ class AuthRetrievalsImpl @Inject() (
 
     val retrievals: Retrieval[RetrievalsType] =
       Retrievals.nino and Retrievals.affinityGroup and Retrievals.allEnrolments and Retrievals.credentials and Retrievals.credentialStrength and
-        Retrievals.confidenceLevel and Retrievals.trustedHelper and Retrievals.profile
+        Retrievals.confidenceLevel and Retrievals.profile
 
-    authorised()
-      .retrieve(retrievals) {
-        case Some(nino) ~
-            affinityGroup ~
-            Enrolments(enrolments) ~
-            Some(credentials) ~
-            Some(CredentialStrength.strong) ~
-            confidenceLevel ~
-            trustedHelper ~
-            profile =>
-          journeyCacheRepository.get(hc).flatMap { userAnswers =>
-            val trimmedRequest: Request[A] = request
-              .map {
-                case AnyContentAsFormUrlEncoded(data) =>
-                  AnyContentAsFormUrlEncoded(data.map { case (key, vals) =>
-                    (key, vals.map(_.trim))
-                  })
-                case b                                => b
-              }
-              .asInstanceOf[Request[A]]
+    authorised().retrieve(retrievals) {
+      case Some(nino) ~
+          affinityGroup ~
+          Enrolments(enrolments) ~
+          Some(credentials) ~
+          Some(CredentialStrength.strong) ~
+          confidenceLevel ~
+          profile =>
+        val finalRequest = for {
+          userAnswers   <- journeyCacheRepository.get(hc)
+          trustedHelper <- getTrustedHelper
+        } yield {
+          val trimmedRequest: Request[A] = request
+            .map {
+              case AnyContentAsFormUrlEncoded(data) =>
+                AnyContentAsFormUrlEncoded(data.map { case (key, vals) =>
+                  (key, vals.map(_.trim))
+                })
+              case b                                => b
+            }
+            .asInstanceOf[Request[A]]
 
-            val authenticatedRequest = AuthenticatedRequest[A](
-              authNino = Nino(nino),
-              credentials = credentials,
-              confidenceLevel = confidenceLevel,
-              trustedHelper = trustedHelper,
-              profile = addRedirect(profile),
-              enrolments = enrolments,
-              request = trimmedRequest,
-              affinityGroup = affinityGroup,
-              userAnswers = userAnswers
-            )
-            block(authenticatedRequest)
-          }
-        case _ => throw new RuntimeException("Can't authenticate user")
-      }
+          AuthenticatedRequest[A](
+            authNino = Nino(nino),
+            credentials = credentials,
+            confidenceLevel = confidenceLevel,
+            trustedHelper = trustedHelper,
+            profile = addRedirect(profile),
+            enrolments = enrolments,
+            request = trimmedRequest,
+            affinityGroup = affinityGroup,
+            userAnswers = userAnswers
+          )
+        }
+        finalRequest.flatMap(block(_))
+      case _ => throw new RuntimeException("Can't authenticate user")
+    }
   }
 
   override def parser: BodyParser[AnyContent] = mcc.parsers.defaultBodyParser
diff --git a/app/services/FandfService.scala b/app/services/FandfService.scala
new file mode 100644
index 000000000..ae1e01571
--- /dev/null
+++ b/app/services/FandfService.scala
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 HM Revenue & Customs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package services
+
+import com.google.inject.Inject
+import connectors.FandFConnector
+import play.api.Logging
+import play.api.http.Status.NOT_FOUND
+import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
+import uk.gov.hmrc.http.HeaderCarrier
+
+import scala.concurrent.{ExecutionContext, Future}
+
+class FandfService @Inject() (
+  fandFConnector: FandFConnector
+) extends Logging {
+
+  def getTrustedHelper()(implicit
+    hc: HeaderCarrier,
+    ec: ExecutionContext
+  ): Future[Option[TrustedHelper]] =
+    fandFConnector
+      .getTrustedHelper()
+      .foldF(
+        { ex =>
+          if (ex.statusCode != NOT_FOUND) {
+            logger.warn(s"Call to fandf failed with status ${ex.statusCode} and message ${ex.message}")
+          }
+          Future.successful(None)
+        },
+        helper => Future.successful(helper)
+      )
+
+}
diff --git a/build.sbt b/build.sbt
index 8ebdd887e..292504013 100644
--- a/build.sbt
+++ b/build.sbt
@@ -71,7 +71,8 @@ lazy val microservice = Project(appName, file("."))
       "uk.gov.hmrc.hmrcfrontend.views.html.components._",
       "uk.gov.hmrc.hmrcfrontend.views.html.helpers._"
     )
-  ).configs(A11yTest)
+  )
+  .configs(A11yTest)
   .settings(inConfig(A11yTest)(org.scalafmt.sbt.ScalafmtPlugin.scalafmtConfigSettings) *)
   .settings(headerSettings(A11yTest) *)
   .settings(automateHeaderSettings(A11yTest))
diff --git a/conf/application.conf b/conf/application.conf
index fbec8b6bf..c99e74388 100644
--- a/conf/application.conf
+++ b/conf/application.conf
@@ -190,6 +190,12 @@ microservice {
       host = localhost
       port = 9984
     }
+
+    fandf {
+      protocol = http
+      host = localhost
+      port = 9333
+    }
   }
 }
 
diff --git a/it/test/MainViewSpec.scala b/it/test/MainViewSpec.scala
index 38bfce411..61e496138 100644
--- a/it/test/MainViewSpec.scala
+++ b/it/test/MainViewSpec.scala
@@ -33,7 +33,7 @@ import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
 import uk.gov.hmrc.auth.core.{ConfidenceLevel, Enrolment, EnrolmentIdentifier}
 import uk.gov.hmrc.domain.{Generator, Nino, SaUtr, SaUtrGenerator}
 import uk.gov.hmrc.http.{HeaderCarrier, SessionId}
-import uk.gov.hmrc.sca.models.{MenuItemConfig, PtaMinMenuConfig, UrBanner, WrapperDataResponse}
+import uk.gov.hmrc.sca.models.{MenuItemConfig, PtaMinMenuConfig, UrBanner, Webchat, WrapperDataResponse}
 import views.MainView
 
 import java.time.LocalDate
@@ -101,7 +101,8 @@ class MainViewSpec extends IntegrationSpec {
           MenuItemConfig("signout", "Sign out", "link", leftAligned = false, 0, None, None)
         ),
         PtaMinMenuConfig("MenuName", "BackName"),
-        List.empty[UrBanner]
+        List.empty[UrBanner],
+        List.empty[Webchat]
       )
     )
     .toString
@@ -253,7 +254,11 @@ class MainViewSpec extends IntegrationSpec {
           doc.getElementById("attorneyBanner") mustBe an[Element]
 
           assertContainsText(doc, principalName)
-          assertContainsLink(doc, "Return to your account", "/return-url")
+          assertContainsLink(
+            doc,
+            "Return to your account",
+            "http://localhost:9231/trusted-helpers/redirect-to-trusted-helpers"
+          )
         }
       }
 
diff --git a/it/test/controllers/ContentsCheckSpec.scala b/it/test/controllers/ContentsCheckSpec.scala
index ccc83172c..de04882fc 100644
--- a/it/test/controllers/ContentsCheckSpec.scala
+++ b/it/test/controllers/ContentsCheckSpec.scala
@@ -32,7 +32,7 @@ import play.api.test.Helpers.{GET, contentAsString, defaultAwaitTimeout, route,
 import testUtils.{FileHelper, IntegrationSpec}
 import uk.gov.hmrc.http.SessionKeys
 import uk.gov.hmrc.mongoFeatureToggles.model.FeatureFlag
-import uk.gov.hmrc.sca.models.{MenuItemConfig, PtaMinMenuConfig, UrBanner, WrapperDataResponse}
+import uk.gov.hmrc.sca.models.{MenuItemConfig, PtaMinMenuConfig, UrBanner, Webchat, WrapperDataResponse}
 
 import java.util.UUID
 import scala.concurrent.Future
@@ -181,7 +181,12 @@ class ContentsCheckSpec extends IntegrationSpec {
   )
   val wrapperDataResponse: String          = Json
     .toJson(
-      WrapperDataResponse(menuWrapperData, PtaMinMenuConfig("MenuName", "BackName"), List.empty[UrBanner])
+      WrapperDataResponse(
+        menuWrapperData,
+        PtaMinMenuConfig("MenuName", "BackName"),
+        List.empty[UrBanner],
+        List.empty[Webchat]
+      )
     )
     .toString
 
@@ -212,6 +217,8 @@ class ContentsCheckSpec extends IntegrationSpec {
         .get(urlMatching("/single-customer-account-wrapper-data/message-data.*"))
         .willReturn(ok(s"$messageCount"))
     )
+
+    server.stubFor(post(urlEqualTo("/auth/authorise")).willReturn(ok(authResponseSA)))
   }
 
   override implicit lazy val app: Application = localGuiceApplicationBuilder()
@@ -219,55 +226,13 @@ class ContentsCheckSpec extends IntegrationSpec {
       "microservice.services.breathing-space-if-proxy.timeoutInMilliseconds" -> 4000,
       "microservice.services.taxcalc-frontend.port"                          -> server.port(),
       "microservice.services.tai.port"                                       -> server.port(),
+      "microservice.services.fandf.port"                                     -> server.port(),
       "sca-wrapper.services.single-customer-account-wrapper-data.url"        -> s"http://localhost:${server.port()}"
     )
     .build()
 
   val uuid: String = UUID.randomUUID().toString
 
-  val authResponseAttorney: String =
-    s"""
-       |{
-       |    "confidenceLevel": 200,
-       |    "nino": "$generatedNino",
-       |    "name": {
-       |        "name": "John",
-       |        "lastName": "Smith"
-       |    },
-       |    "loginTimes": {
-       |        "currentLogin": "2021-06-07T10:52:02.594Z",
-       |        "previousLogin": null
-       |    },
-       |    "optionalCredentials": {
-       |        "providerId": "4911434741952698",
-       |        "providerType": "GovernmentGateway"
-       |    },
-       |    "authProviderId": {
-       |        "ggCredId": "xyz"
-       |    },
-       |    "externalId": "testExternalId",
-       |    "allEnrolments": [
-       |       {
-       |          "key":"HMRC-PT",
-       |          "identifiers": [
-       |             {
-       |                "key":"NINO",
-       |                "value": "$generatedNino"
-       |             }
-       |          ]
-       |       }
-       |    ],
-       |    "affinityGroup": "Individual",
-       |    "credentialStrength": "strong",
-       |    "trustedHelper": {
-       |      "principalName": "principalName",
-       |      "attorneyName": "attorneyName",
-       |      "returnLinkUrl": "returnLink",
-       |      "principalNino": "$generatedNino"
-       |    }
-       |}
-       |""".stripMargin
-
   val authResponseSA: String =
     s"""
        |{
@@ -333,9 +298,9 @@ class ContentsCheckSpec extends IntegrationSpec {
       urls.foreach { case (url, expectedData: ExpectedData) =>
         s"pass content checks at url $url" in {
           if (expectedData.attorneyBannerPresent) {
-            server.stubFor(post(urlEqualTo("/auth/authorise")).willReturn(ok(authResponseAttorney)))
+            server.stubFor(get(urlEqualTo("/delegation/get")).willReturn(ok(fandfTrustedHelperResponse)))
           } else {
-            server.stubFor(post(urlEqualTo("/auth/authorise")).willReturn(ok(authResponseSA)))
+            server.stubFor(get(urlEqualTo("/delegation/get")).willReturn(notFound()))
           }
           val result: Future[Result] = route(app, request(url)).get
           val content                = Jsoup.parse(contentAsString(result))
diff --git a/it/test/controllers/HomeControllerScaISpec.scala b/it/test/controllers/HomeControllerScaISpec.scala
index 3ecfd7daf..0236ba436 100644
--- a/it/test/controllers/HomeControllerScaISpec.scala
+++ b/it/test/controllers/HomeControllerScaISpec.scala
@@ -35,7 +35,7 @@ import uk.gov.hmrc.auth.core.{ConfidenceLevel, Enrolment, EnrolmentIdentifier}
 import uk.gov.hmrc.domain.{Generator, Nino, SaUtr, SaUtrGenerator}
 import uk.gov.hmrc.http.SessionKeys
 import uk.gov.hmrc.mongoFeatureToggles.model.FeatureFlag
-import uk.gov.hmrc.sca.models.{MenuItemConfig, PtaMinMenuConfig, UrBanner, WrapperDataResponse}
+import uk.gov.hmrc.sca.models.{MenuItemConfig, PtaMinMenuConfig, UrBanner, Webchat, WrapperDataResponse}
 
 import java.time.LocalDate
 import java.util.UUID
@@ -110,7 +110,8 @@ class HomeControllerScaISpec extends IntegrationSpec with MockitoSugar {
           )
         ),
         PtaMinMenuConfig("MenuName", "BackName"),
-        List.empty[UrBanner]
+        List.empty[UrBanner],
+        List.empty[Webchat]
       )
     )
     .toString
diff --git a/it/test/controllers/HomeControllerTrustedHelperISpec.scala b/it/test/controllers/HomeControllerTrustedHelperISpec.scala
index 3df23bdb6..ea7ab083f 100644
--- a/it/test/controllers/HomeControllerTrustedHelperISpec.scala
+++ b/it/test/controllers/HomeControllerTrustedHelperISpec.scala
@@ -100,6 +100,16 @@ class HomeControllerTrustedHelperISpec extends IntegrationSpec {
        |}
        |""".stripMargin
 
+  override val fandfTrustedHelperResponse: String =
+    s"""
+       |{
+       |   "principalName": "principal Name",
+       |   "attorneyName": "attorneyName",
+       |   "returnLinkUrl": "returnLink",
+       |   "principalNino": "$generatedHelperNino"
+       |}
+       |""".stripMargin
+
   def request: FakeRequest[AnyContentAsEmpty.type] =
     FakeRequest(GET, url).withSession(SessionKeys.sessionId -> uuid, SessionKeys.authToken -> "1")
 
@@ -116,6 +126,9 @@ class HomeControllerTrustedHelperISpec extends IntegrationSpec {
     )
 
     server.stubFor(post(urlEqualTo("/auth/authorise")).willReturn(ok(authTrustedHelperResponse)))
+
+    server.stubFor(get(urlEqualTo("/delegation/get")).willReturn(ok(fandfTrustedHelperResponse)))
+
     server.stubFor(
       get(urlEqualTo(s"/citizen-details/$generatedHelperNino/designatory-details"))
         .willReturn(ok(personDetailsResponse(generatedHelperNino.nino)))
diff --git a/it/test/testUtils/IntegrationSpec.scala b/it/test/testUtils/IntegrationSpec.scala
index b47faadbd..615119059 100644
--- a/it/test/testUtils/IntegrationSpec.scala
+++ b/it/test/testUtils/IntegrationSpec.scala
@@ -16,6 +16,7 @@
 
 package testUtils
 
+import com.github.tomakehurst.wiremock.client.WireMock
 import org.apache.pekko.Done
 import com.github.tomakehurst.wiremock.client.WireMock._
 import com.github.tomakehurst.wiremock.stubbing.StubMapping
@@ -263,6 +264,16 @@ trait IntegrationSpec
        |]
     """.stripMargin
 
+  val fandfTrustedHelperResponse: String =
+    s"""
+      |{
+      |   "principalName": "principal Name",
+      |   "attorneyName": "attorneyName",
+      |   "returnLinkUrl": "returnLink",
+      |   "principalNino": "$generatedNino"
+      |}
+      |""".stripMargin
+
   protected def localGuiceApplicationBuilder(): GuiceApplicationBuilder =
     GuiceApplicationBuilder()
       .overrides(
@@ -276,7 +287,8 @@ trait IntegrationSpec
         "microservice.services.message-frontend.port"           -> server.port(),
         "microservice.services.agent-client-authorisation.port" -> server.port(),
         "microservice.services.breathing-space-if-proxy.port"   -> server.port(),
-        "microservice.services.taxcalc-frontend.port"           -> server.port()
+        "microservice.services.taxcalc-frontend.port"           -> server.port(),
+        "microservice.services.fandf.port"                      -> server.port()
       )
 
   override def beforeEach(): Unit = {
@@ -318,6 +330,11 @@ trait IntegrationSpec
             .withBody("{\"code\": \"ACCESS_GRANTED\", \"message\": \"Access granted\"}")
         )
     )
+    server.stubFor(
+      WireMock
+        .get(urlMatching("/delegation/get"))
+        .willReturn(notFound())
+    )
   }
 
   def beforeEachHomeController(
diff --git a/project/AppDependencies.scala b/project/AppDependencies.scala
index 0a2fc78c8..6200eab47 100644
--- a/project/AppDependencies.scala
+++ b/project/AppDependencies.scala
@@ -3,10 +3,10 @@ import sbt.*
 
 object AppDependencies {
 
-  private val playVersion      = "play-30"
-  private val cryptoVersion    = "8.2.0"
-  private val webChatVersion   = "1.6.0"
-  private val scaWrapperVersion = "2.7.0"
+  private val playVersion               = "play-30"
+  private val cryptoVersion             = "8.2.0"
+  private val webChatVersion            = "1.6.0"
+  private val scaWrapperVersion         = "2.9.0"
   private val mongoFeatureClientVersion = "1.10.0"
 
   val compile: Seq[ModuleID] = Seq(
@@ -24,10 +24,10 @@ object AppDependencies {
   )
 
   val test: Seq[ModuleID] = Seq(
-    "uk.gov.hmrc"         %% s"sca-wrapper-test-$playVersion" % scaWrapperVersion,
-    "uk.gov.hmrc"         %% s"mongo-feature-toggles-client-test-$playVersion" % mongoFeatureClientVersion,
-    "org.mockito"         %% "mockito-scala-scalatest"        % "1.17.37",
-    "org.scalatestplus"   %% "scalacheck-1-17"                % "3.2.18.0",
+    "uk.gov.hmrc"       %% s"sca-wrapper-test-$playVersion"                  % scaWrapperVersion,
+    "uk.gov.hmrc"       %% s"mongo-feature-toggles-client-test-$playVersion" % mongoFeatureClientVersion,
+    "org.mockito"       %% "mockito-scala-scalatest"                         % "1.17.37",
+    "org.scalatestplus" %% "scalacheck-1-17"                                 % "3.2.18.0"
   ).map(_ % "test")
 
   val all: Seq[ModuleID]  = compile ++ test
diff --git a/project/plugins.sbt b/project/plugins.sbt
index a092665a6..48c98c5c8 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,5 +1,7 @@
 resolvers += MavenRepository("HMRC-open-artefacts-maven2", "https://open.artefacts.tax.service.gov.uk/maven2")
-resolvers += Resolver.url("HMRC-open-artefacts-ivy2", url("https://open.artefacts.tax.service.gov.uk/ivy2"))(Resolver.ivyStylePatterns)
+resolvers += Resolver.url("HMRC-open-artefacts-ivy2", url("https://open.artefacts.tax.service.gov.uk/ivy2"))(
+  Resolver.ivyStylePatterns
+)
 resolvers += "Typesafe Releases" at "https://repo.typesafe.com/typesafe/releases/"
 
 addSbtPlugin("org.scoverage"      % "sbt-scoverage"            % "2.3.0")
@@ -9,4 +11,4 @@ addSbtPlugin("org.playframework"  % "sbt-plugin"               % "3.0.6")
 addSbtPlugin("com.github.sbt"     % "sbt-digest"               % "2.0.0")
 addSbtPlugin("io.github.irundaia" % "sbt-sassify"              % "1.5.2")
 addSbtPlugin("org.scalameta"      % "sbt-scalafmt"             % "2.5.4")
-addSbtPlugin("uk.gov.hmrc"        % "sbt-accessibility-linter" % "1.0.0")
\ No newline at end of file
+addSbtPlugin("uk.gov.hmrc"        % "sbt-accessibility-linter" % "1.0.0")
diff --git a/test/connectors/FandFConnectorSpec.scala b/test/connectors/FandFConnectorSpec.scala
new file mode 100644
index 000000000..2fdfcc055
--- /dev/null
+++ b/test/connectors/FandFConnectorSpec.scala
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2023 HM Revenue & Customs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package connectors
+
+import config.ConfigDecorator
+import play.api.Application
+import play.api.test.{DefaultAwaitTimeout, Injecting}
+import testUtils.WireMockHelper
+import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
+import uk.gov.hmrc.domain.{Generator, Nino}
+import uk.gov.hmrc.http.client.HttpClientV2
+import uk.gov.hmrc.http.UpstreamErrorResponse
+import scala.util.Random
+
+class FandFConnectorSpec extends ConnectorSpec with WireMockHelper with DefaultAwaitTimeout with Injecting {
+
+  override implicit lazy val app: Application = app(
+    Map("microservice.services.fandf.port" -> server.port())
+  )
+
+  val nino: Nino = Nino(new Generator(new Random()).nextNino.nino)
+
+  val trustedHelper: TrustedHelper = TrustedHelper("principal Name", "attorneyName", "returnLink", Some(nino.nino))
+
+  val fandfTrustedHelperResponse: String =
+    s"""
+       |{
+       |   "principalName": "principal Name",
+       |   "attorneyName": "attorneyName",
+       |   "returnLinkUrl": "returnLink",
+       |   "principalNino": "$nino"
+       |}
+       |""".stripMargin
+
+  trait SpecSetup {
+
+    lazy val connector: FandFConnector = {
+      val configDecorator = app.injector.instanceOf[ConfigDecorator]
+      new FandFConnector(
+        inject[HttpClientV2],
+        configDecorator,
+        inject[HttpClientResponse]
+      )
+    }
+  }
+
+  "Calling FandFConnector.getTrustedHelper" must {
+
+    "return as Some(trustedHelper) when trustedHelper json returned" in new SpecSetup {
+      stubGet("/delegation/get", OK, Some(fandfTrustedHelperResponse))
+
+      val result: Option[TrustedHelper] =
+        connector.getTrustedHelper().value.futureValue.getOrElse(None)
+
+      result mustBe Some(trustedHelper)
+    }
+
+    List(
+      SERVICE_UNAVAILABLE,
+      IM_A_TEAPOT,
+      BAD_REQUEST,
+      NOT_FOUND
+    ).foreach { statusCode =>
+      s"return Left when a $statusCode is retrieved" in new SpecSetup {
+        stubGet("/delegation/get", statusCode, None)
+
+        val result: UpstreamErrorResponse =
+          connector.getTrustedHelper().value.futureValue.swap.getOrElse(UpstreamErrorResponse("", OK))
+
+        result.statusCode mustBe statusCode
+      }
+    }
+  }
+}
diff --git a/test/controllers/auth/AuthRetrievalsSpec.scala b/test/controllers/auth/AuthRetrievalsSpec.scala
index 983fdf303..9abd4be74 100644
--- a/test/controllers/auth/AuthRetrievalsSpec.scala
+++ b/test/controllers/auth/AuthRetrievalsSpec.scala
@@ -26,6 +26,7 @@ import play.api.mvc._
 import play.api.test.FakeRequest
 import play.api.test.Helpers._
 import repositories.JourneyCacheRepository
+import services.FandfService
 import testUtils.RetrievalOps._
 import testUtils.{BaseSpec, Fixtures}
 import uk.gov.hmrc.auth.core.AffinityGroup.Individual
@@ -46,6 +47,7 @@ class AuthRetrievalsSpec extends BaseSpec {
 
   private val mockAuthConnector: AuthConnector                   = mock[AuthConnector]
   private val mockJourneyCacheRepository: JourneyCacheRepository = mock[JourneyCacheRepository]
+  private val mockFandfService: FandfService                     = mock[FandfService]
 
   private class Harness(authAction: AuthRetrievalsImpl) extends InjectedController {
     def onPageLoad: Action[AnyContent] = authAction { request: AuthenticatedRequest[AnyContent] =>
@@ -58,8 +60,7 @@ class AuthRetrievalsSpec extends BaseSpec {
 
   private type AuthRetrievals =
     Option[String] ~ Option[AffinityGroup] ~ Enrolments ~ Option[Credentials] ~
-      Option[String] ~ ConfidenceLevel ~ Option[TrustedHelper] ~
-      Option[String]
+      Option[String] ~ ConfidenceLevel ~ Option[String]
 
   private val nino: String                                               = Fixtures.fakeNino.nino
   private val fakeCredentials: Credentials                               = Credentials("foo", "bar")
@@ -79,14 +80,13 @@ class AuthRetrievalsSpec extends BaseSpec {
     ),
     credentialStrength: String = CredentialStrength.strong,
     confidenceLevel: ConfidenceLevel = ConfidenceLevel.L200,
-    trustedHelper: Option[TrustedHelper] = None,
     profileUrl: Option[String] = None
   ): Harness = {
 
     when(mockAuthConnector.authorise[AuthRetrievals](any(), any())(any(), any())) thenReturn Future.successful(
       nino ~ affinityGroup ~ saEnrolments ~ Some(fakeCredentials) ~ Some(
         credentialStrength
-      ) ~ confidenceLevel ~ trustedHelper ~ profileUrl
+      ) ~ confidenceLevel ~ profileUrl
     )
 
     when(mockJourneyCacheRepository.get(any[HeaderCarrier])).thenReturn(Future.successful(UserAnswers.empty("id")))
@@ -95,7 +95,8 @@ class AuthRetrievalsSpec extends BaseSpec {
       new AuthRetrievalsImpl(
         mockAuthConnector,
         messagesControllerComponents,
-        mockJourneyCacheRepository
+        mockJourneyCacheRepository,
+        mockFandfService
       )(implicitly, config)
 
     new Harness(authAction)
@@ -104,6 +105,12 @@ class AuthRetrievalsSpec extends BaseSpec {
   "A user with a nino and an SA enrolment must" must {
     "create an authenticated request" in {
 
+      when(mockFandfService.getTrustedHelper()(any(), any())).thenReturn(
+        Future.successful(
+          None
+        )
+      )
+
       val utr = new SaUtrGenerator().nextSaUtr.utr
 
       val controller = retrievals(saEnrolments = Enrolments(fakeEnrolments(utr)))
@@ -118,10 +125,14 @@ class AuthRetrievalsSpec extends BaseSpec {
   "A user with trustedHelper must" must {
     "create an authenticated request containing the trustedHelper" in {
 
-      val controller =
-        retrievals(trustedHelper =
+      when(mockFandfService.getTrustedHelper()(any(), any())).thenReturn(
+        Future.successful(
           Some(TrustedHelper("principalName", "attorneyName", "returnUrl", Some(generatedTrustedHelperNino.nino)))
         )
+      )
+
+      val controller =
+        retrievals()
 
       val result = controller.onPageLoad(FakeRequest("", ""))
       status(result) mustBe OK
diff --git a/test/services/FandfServiceSpec.scala b/test/services/FandfServiceSpec.scala
new file mode 100644
index 000000000..723158d44
--- /dev/null
+++ b/test/services/FandfServiceSpec.scala
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2025 HM Revenue & Customs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package services
+
+import cats.data.EitherT
+import connectors.FandFConnector
+import org.mockito.ArgumentMatchers.any
+import org.scalatest.concurrent.IntegrationPatience
+import play.api.http.Status.INTERNAL_SERVER_ERROR
+import play.api.mvc.AnyContentAsEmpty
+import play.api.test.Helpers.GET
+import play.api.test.{FakeRequest, Injecting}
+import testUtils.BaseSpec
+import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
+import uk.gov.hmrc.http.UpstreamErrorResponse
+
+import scala.concurrent.Future
+
+class FandfServiceSpec extends BaseSpec with Injecting with IntegrationPatience {
+
+  val mockConnector: FandFConnector                         = mock[FandFConnector]
+  implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/")
+  val sut: FandfService                                     = new FandfService(mockConnector)
+
+  val trustedHelper: TrustedHelper =
+    TrustedHelper("principal Name", "attorneyName", "returnLink", Some(generatedNino.nino))
+
+  "FandfService" when {
+    "calling getTrustedHelper" must {
+      "return Some(trustedHelper)" in {
+        when(mockConnector.getTrustedHelper()(any())).thenReturn(
+          EitherT[Future, UpstreamErrorResponse, Option[TrustedHelper]](Future.successful(Right(Some(trustedHelper))))
+        )
+
+        val result =
+          sut
+            .getTrustedHelper()
+            .futureValue
+
+        result mustBe Some(trustedHelper)
+      }
+
+      "return None when call fails" in {
+        when(mockConnector.getTrustedHelper()(any())).thenReturn(
+          EitherT[Future, UpstreamErrorResponse, Option[TrustedHelper]](
+            Future.successful(Left(UpstreamErrorResponse("failure", INTERNAL_SERVER_ERROR)))
+          )
+        )
+
+        val result =
+          sut
+            .getTrustedHelper()
+            .futureValue
+
+        result mustBe None
+      }
+
+    }
+  }
+}


Reviews:
user: login: grantrobson
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T12:24:15Z
user: login: grantrobson
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T12:26:12Z
user: login: grantrobson
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T12:28:03Z
user: login: Nj147
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T13:16:34Z
user: login: Nj147
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T13:16:51Z
user: login: grantrobson
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T13:20:24Z
user: login: grantrobson
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T13:21:11Z
user: login: grantrobson
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T13:22:37Z
user: login: grantrobson
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T13:22:57Z
user: login: grantrobson
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T06:04:57Z
user: login: Nj147
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T07:04:35Z
user: login: grantrobson
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T08:09:57Z

Review comments:
diff_hunk: @@ -57,44 +62,45 @@ class AuthRetrievalsImpl @Inject() (
 
     val retrievals: Retrieval[RetrievalsType] =
       Retrievals.nino and Retrievals.affinityGroup and Retrievals.allEnrolments and Retrievals.credentials and Retrievals.credentialStrength and
-        Retrievals.confidenceLevel and Retrievals.trustedHelper and Retrievals.profile
+        Retrievals.confidenceLevel and Retrievals.profile
 
-    authorised()
-      .retrieve(retrievals) {
-        case Some(nino) ~
-            affinityGroup ~
-            Enrolments(enrolments) ~
-            Some(credentials) ~
-            Some(CredentialStrength.strong) ~
-            confidenceLevel ~
-            trustedHelper ~
-            profile =>
-          journeyCacheRepository.get(hc).flatMap { userAnswers =>
-            val trimmedRequest: Request[A] = request
-              .map {
-                case AnyContentAsFormUrlEncoded(data) =>
-                  AnyContentAsFormUrlEncoded(data.map { case (key, vals) =>
-                    (key, vals.map(_.trim))
-                  })
-                case b                                => b
-              }
-              .asInstanceOf[Request[A]]
+    authorised().retrieve(retrievals) {
+      case Some(nino) ~
+          affinityGroup ~
+          Enrolments(enrolments) ~
+          Some(credentials) ~
+          Some(CredentialStrength.strong) ~
+          confidenceLevel ~
+          profile =>
+        val finalRequest = for {
+          userAnswers   <- journeyCacheRepository.get(hc)
+          trustedHelper <- getTrustedHelper
+        } yield {
+          val trimmedRequest: Request[A] = request
+            .map {
+              case AnyContentAsFormUrlEncoded(data) =>
path: app/controllers/auth/AuthRetrievals.scala
user: login: grantrobson
body: What's this for?
created_at: 2025-04-09T12:24:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 HM Revenue & Customs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package connectors
+
+import config.ConfigDecorator
+import play.api.Application
+import play.api.test.{DefaultAwaitTimeout, Injecting}
+import testUtils.WireMockHelper
+import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
+import uk.gov.hmrc.domain.{Generator, Nino}
+import uk.gov.hmrc.http.client.HttpClientV2
+import uk.gov.hmrc.http.UpstreamErrorResponse
+import scala.util.Random
+
+class FandFConnectorSpec extends ConnectorSpec with WireMockHelper with DefaultAwaitTimeout with Injecting {
+
+  override implicit lazy val app: Application = app(
+    Map("microservice.services.fandf.port" -> server.port())
+  )
+
+  val nino: Nino = Nino(new Generator(new Random()).nextNino.nino)
+
+  val trustedHelper: TrustedHelper = TrustedHelper("principal Name", "attorneyName", "returnLink", Some(nino.nino))
+
+  val fandfTrustedHelperResponse: String =
+    s"""
+       |{
+       |   "principalName": "principal Name",
+       |   "attorneyName": "attorneyName",
+       |   "returnLinkUrl": "returnLink",
+       |   "principalNino": "$nino"
+       |}
+       |""".stripMargin
+
+  trait SpecSetup {
+
+    lazy val connector: FandFConnector = {
+      val configDecorator = app.injector.instanceOf[ConfigDecorator]
+      new FandFConnector(
+        inject[HttpClientV2],
+        configDecorator,
+        inject[HttpClientResponse]
+      )
+    }
+  }
+
+  "Calling FandFConnector.getTrustedHelper" must {
+
+    "return as Some(trustedHelper) when trustedHelper json returned" in new SpecSetup {
+      stubGet("/delegation/get", OK, Some(fandfTrustedHelperResponse))
+
+      val result: Option[TrustedHelper] =
+        connector.getTrustedHelper().value.futureValue.getOrElse(None)
+
+      result mustBe Some(trustedHelper)
+    }
+
+    List(
path: test/connectors/FandFConnectorSpec.scala
user: login: grantrobson
body: Can we have a test for fandf endpoint returning NotFound (404)?
created_at: 2025-04-09T12:26:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 HM Revenue & Customs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package services
+
+import com.google.inject.Inject
+import connectors.FandFConnector
+import play.api.Logging
+import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
+import uk.gov.hmrc.http.HeaderCarrier
+
+import scala.concurrent.{ExecutionContext, Future}
+
+class FandfService @Inject() (
+  fandFConnector: FandFConnector
+) extends Logging {
+
+  def getTrustedHelper()(implicit
+    hc: HeaderCarrier,
+    ec: ExecutionContext
+  ): Future[Option[TrustedHelper]] =
+    fandFConnector.getTrustedHelper().foldF(_ => Future.successful(None), helper => Future.successful(helper))
path: app/services/FandfService.scala
user: login: grantrobson
body: I'm wondering what would happen if we had an intermittent 500 error from fandf - the trusted helper will vanish but only intermittently - could be confusing. Maybe we need to just throw the exception?
created_at: 2025-04-09T12:28:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -57,44 +62,45 @@ class AuthRetrievalsImpl @Inject() (
 
     val retrievals: Retrieval[RetrievalsType] =
       Retrievals.nino and Retrievals.affinityGroup and Retrievals.allEnrolments and Retrievals.credentials and Retrievals.credentialStrength and
-        Retrievals.confidenceLevel and Retrievals.trustedHelper and Retrievals.profile
+        Retrievals.confidenceLevel and Retrievals.profile
 
-    authorised()
-      .retrieve(retrievals) {
-        case Some(nino) ~
-            affinityGroup ~
-            Enrolments(enrolments) ~
-            Some(credentials) ~
-            Some(CredentialStrength.strong) ~
-            confidenceLevel ~
-            trustedHelper ~
-            profile =>
-          journeyCacheRepository.get(hc).flatMap { userAnswers =>
-            val trimmedRequest: Request[A] = request
-              .map {
-                case AnyContentAsFormUrlEncoded(data) =>
-                  AnyContentAsFormUrlEncoded(data.map { case (key, vals) =>
-                    (key, vals.map(_.trim))
-                  })
-                case b                                => b
-              }
-              .asInstanceOf[Request[A]]
+    authorised().retrieve(retrievals) {
+      case Some(nino) ~
+          affinityGroup ~
+          Enrolments(enrolments) ~
+          Some(credentials) ~
+          Some(CredentialStrength.strong) ~
+          confidenceLevel ~
+          profile =>
+        val finalRequest = for {
+          userAnswers   <- journeyCacheRepository.get(hc)
+          trustedHelper <- getTrustedHelper
+        } yield {
+          val trimmedRequest: Request[A] = request
+            .map {
+              case AnyContentAsFormUrlEncoded(data) =>
path: app/controllers/auth/AuthRetrievals.scala
user: login: Nj147
body: It's pre-existing stuff, not actually sure what of the request it's actually trimming
created_at: 2025-04-09T13:16:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 HM Revenue & Customs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package connectors
+
+import config.ConfigDecorator
+import play.api.Application
+import play.api.test.{DefaultAwaitTimeout, Injecting}
+import testUtils.WireMockHelper
+import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
+import uk.gov.hmrc.domain.{Generator, Nino}
+import uk.gov.hmrc.http.client.HttpClientV2
+import uk.gov.hmrc.http.UpstreamErrorResponse
+import scala.util.Random
+
+class FandFConnectorSpec extends ConnectorSpec with WireMockHelper with DefaultAwaitTimeout with Injecting {
+
+  override implicit lazy val app: Application = app(
+    Map("microservice.services.fandf.port" -> server.port())
+  )
+
+  val nino: Nino = Nino(new Generator(new Random()).nextNino.nino)
+
+  val trustedHelper: TrustedHelper = TrustedHelper("principal Name", "attorneyName", "returnLink", Some(nino.nino))
+
+  val fandfTrustedHelperResponse: String =
+    s"""
+       |{
+       |   "principalName": "principal Name",
+       |   "attorneyName": "attorneyName",
+       |   "returnLinkUrl": "returnLink",
+       |   "principalNino": "$nino"
+       |}
+       |""".stripMargin
+
+  trait SpecSetup {
+
+    lazy val connector: FandFConnector = {
+      val configDecorator = app.injector.instanceOf[ConfigDecorator]
+      new FandFConnector(
+        inject[HttpClientV2],
+        configDecorator,
+        inject[HttpClientResponse]
+      )
+    }
+  }
+
+  "Calling FandFConnector.getTrustedHelper" must {
+
+    "return as Some(trustedHelper) when trustedHelper json returned" in new SpecSetup {
+      stubGet("/delegation/get", OK, Some(fandfTrustedHelperResponse))
+
+      val result: Option[TrustedHelper] =
+        connector.getTrustedHelper().value.futureValue.getOrElse(None)
+
+      result mustBe Some(trustedHelper)
+    }
+
+    List(
path: test/connectors/FandFConnectorSpec.scala
user: login: Nj147
body: Added NOT_FOUND to the list
created_at: 2025-04-09T13:16:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 HM Revenue & Customs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package services
+
+import com.google.inject.Inject
+import connectors.FandFConnector
+import play.api.Logging
+import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
+import uk.gov.hmrc.http.HeaderCarrier
+
+import scala.concurrent.{ExecutionContext, Future}
+
+class FandfService @Inject() (
+  fandFConnector: FandFConnector
+) extends Logging {
+
+  def getTrustedHelper()(implicit
+    hc: HeaderCarrier,
+    ec: ExecutionContext
+  ): Future[Option[TrustedHelper]] =
+    fandFConnector.getTrustedHelper().foldF(_ => Future.successful(None), helper => Future.successful(helper))
path: app/services/FandfService.scala
user: login: grantrobson
body: It's probably OK to do this as auth does the same. 
created_at: 2025-04-09T13:20:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -57,44 +62,45 @@ class AuthRetrievalsImpl @Inject() (
 
     val retrievals: Retrieval[RetrievalsType] =
       Retrievals.nino and Retrievals.affinityGroup and Retrievals.allEnrolments and Retrievals.credentials and Retrievals.credentialStrength and
-        Retrievals.confidenceLevel and Retrievals.trustedHelper and Retrievals.profile
+        Retrievals.confidenceLevel and Retrievals.profile
 
-    authorised()
-      .retrieve(retrievals) {
-        case Some(nino) ~
-            affinityGroup ~
-            Enrolments(enrolments) ~
-            Some(credentials) ~
-            Some(CredentialStrength.strong) ~
-            confidenceLevel ~
-            trustedHelper ~
-            profile =>
-          journeyCacheRepository.get(hc).flatMap { userAnswers =>
-            val trimmedRequest: Request[A] = request
-              .map {
-                case AnyContentAsFormUrlEncoded(data) =>
-                  AnyContentAsFormUrlEncoded(data.map { case (key, vals) =>
-                    (key, vals.map(_.trim))
-                  })
-                case b                                => b
-              }
-              .asInstanceOf[Request[A]]
+    authorised().retrieve(retrievals) {
+      case Some(nino) ~
+          affinityGroup ~
+          Enrolments(enrolments) ~
+          Some(credentials) ~
+          Some(CredentialStrength.strong) ~
+          confidenceLevel ~
+          profile =>
+        val finalRequest = for {
+          userAnswers   <- journeyCacheRepository.get(hc)
+          trustedHelper <- getTrustedHelper
+        } yield {
+          val trimmedRequest: Request[A] = request
+            .map {
+              case AnyContentAsFormUrlEncoded(data) =>
path: app/controllers/auth/AuthRetrievals.scala
user: login: grantrobson
body: Oh yes you are right, it's in main.
created_at: 2025-04-09T13:22:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 HM Revenue & Customs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package services
+
+import com.google.inject.Inject
+import connectors.FandFConnector
+import play.api.Logging
+import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
+import uk.gov.hmrc.http.HeaderCarrier
+
+import scala.concurrent.{ExecutionContext, Future}
+
+class FandfService @Inject() (
+  fandFConnector: FandFConnector
+) extends Logging {
+
+  def getTrustedHelper()(implicit
+    hc: HeaderCarrier,
+    ec: ExecutionContext
+  ): Future[Option[TrustedHelper]] =
+    fandFConnector.getTrustedHelper().foldF(_ => Future.successful(None), helper => Future.successful(helper))
path: app/services/FandfService.scala
user: login: grantrobson
body: Could we put a logger.warn here with the exception? As any 5xxx errors will result in no trusted helper in banner. If getting lots of timeouts we need to know so we can increase slots/ memory
created_at: 2025-04-10T06:04:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 HM Revenue & Customs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package services
+
+import com.google.inject.Inject
+import connectors.FandFConnector
+import play.api.Logging
+import uk.gov.hmrc.auth.core.retrieve.v2.TrustedHelper
+import uk.gov.hmrc.http.HeaderCarrier
+
+import scala.concurrent.{ExecutionContext, Future}
+
+class FandfService @Inject() (
+  fandFConnector: FandFConnector
+) extends Logging {
+
+  def getTrustedHelper()(implicit
+    hc: HeaderCarrier,
+    ec: ExecutionContext
+  ): Future[Option[TrustedHelper]] =
+    fandFConnector.getTrustedHelper().foldF(_ => Future.successful(None), helper => Future.successful(helper))
path: app/services/FandfService.scala
user: login: Nj147
body: Added logging for all errors except 404s
created_at: 2025-04-10T07:04:35Z
author_association: CONTRIBUTOR

PR comments:
user: login: platops-pr-bot
created_at: 2025-04-09T07:14:44Z
body: - `SBT` can be updated to `1.10.10` in [project/build.properties](https://github.com/hmrc/pertax-frontend/blob/DDCNL-10231/project/build.properties#L1)
- Your service is building with `Node 14.17.6` which is older than the platform recommended version 18.20.5. You can use `withNodeJs()` without params in [build-jobs](https://github.com/hmrc/build-jobs/blob/HEAD/jobs/live/ddcnlsPTA.groovy#L34) to use the platform default or update your .nvmrc if using `withNodeJsFromNvmrcFile()`
- You have multiple routes files `conf/prod.routes, conf/testOnlyDoNotUseInAppConf.routes` using the same package `controllers`, which leads to collision with the reverse routes. Please ensure each routes file refers to a different package.
- `uk.gov.hmrc:sca-wrapper-play-30` can be updated to `2.8.0` in [project/AppDependencies.scala](https://github.com/hmrc/pertax-frontend/blob/DDCNL-10231/project/AppDependencies.scala#L1).
- `uk.gov.hmrc:sca-wrapper-test-play-30` can be updated to `2.8.0` in [project/AppDependencies.scala](https://github.com/hmrc/pertax-frontend/blob/DDCNL-10231/project/AppDependencies.scala#L1).
- Your service can now use Java 21. Please set `JavaType.OPENJDK_JRE21` in [build-jobs](https://github.com/hmrc/build-jobs/blob/HEAD/jobs/live/ddcnlsPTA.groovy#L34) - See confluence https://confluence.tools.tax.service.gov.uk/x/xIACM
author_association: NONE
user: login: platops-pr-bot
created_at: 2025-04-09T10:48:10Z
body: - ~~`uk.gov.hmrc:sca-wrapper-play-30` can be updated to `2.8.0` in [project/AppDependencies.scala](https://github.com/hmrc/pertax-frontend/blob/DDCNL-10231/project/AppDependencies.scala#L1).~~
- ~~`uk.gov.hmrc:sca-wrapper-test-play-30` can be updated to `2.8.0` in [project/AppDependencies.scala](https://github.com/hmrc/pertax-frontend/blob/DDCNL-10231/project/AppDependencies.scala#L1).~~
- `SBT` can be updated to `1.10.10` in [project/build.properties](https://github.com/hmrc/pertax-frontend/blob/DDCNL-10231/project/build.properties#L1)
- Your service is building with `Node 14.17.6` which is older than the platform recommended version 18.20.5. You can use `withNodeJs()` without params in [build-jobs](https://github.com/hmrc/build-jobs/blob/HEAD/jobs/live/ddcnlsPTA.groovy#L34) to use the platform default or update your .nvmrc if using `withNodeJsFromNvmrcFile()`
- You have multiple routes files `conf/prod.routes, conf/testOnlyDoNotUseInAppConf.routes` using the same package `controllers`, which leads to collision with the reverse routes. Please ensure each routes file refers to a different package.
- `uk.gov.hmrc.digital-engagement-platform-chat-30` in [project/AppDependencies.scala](https://github.com/hmrc/pertax-frontend/blob/DDCNL-10231/project/AppDependencies.scala#L23) can be removed - it is already provided by `uk.gov.hmrc.sca-wrapper-play-30`
- Your service can now use Java 21. Please set `JavaType.OPENJDK_JRE21` in [build-jobs](https://github.com/hmrc/build-jobs/blob/HEAD/jobs/live/ddcnlsPTA.groovy#L34) - See confluence https://confluence.tools.tax.service.gov.uk/x/xIACM
author_association: NONE