Repository: hmrc/income-tax-state-benefits-frontend
Pull request #105: SASS-11220 - disable secondary agent authentication
Author: login: baha-aboukoura
Created: 2025-03-07T16:24:12Z | Merged: 2025-03-10T13:23:48Z

### Description

Please include a brief summary of the change you've made and the issue that it fixes.

Add a link to the relevant story in Jira
[SASS-11170](https://jira.tools.tax.service.gov.uk/browse/SASS-11170)

### Checklist PR Reviewer

##### Before Reviewing

- [ ]  Have you pulled the branch down?
- [ ]  Have you assigned yourself to the PR?
- [ ]  Have you moved the task to “in review” on JIRA?
- [ ]  Have you checked to ensure all dependencies are up to date?
- [ ]  Have you checked to ensure its been rebased against the current version of main?

##### Whilst Reviewing

- [ ]  Have you run the tests?
- [ ]  Have you run the journey tests?
- [ ]  Have you looked at the JIRA story to make sure all Acceptance Criteria has been met?

##### After Reviewing

- [ ]  Have you checked for merge conflicts or any changes in the current main that may affect the current pull request?
  i.e. does it need another rebase?
- [ ]  Have you checked to make sure there are no builds in the pipeline before you merge?
- [ ]  Have you moved the task to “in pipeline” on Jira?

### Checklist PR Raiser

##### Before creating PR
- [x]  Have you run the tests?
- [ ]  Have you run the journey tests? (where applicable)
- [ ]  Have you run the smoke tests? (where applicable)
- [ ]  Have you run the performance tests? (where applicable)
- [x]  Have you addressed warnings where appropriate?
- [x]  Have you rebased against the current version of main?
- [ ]  Have you checked code coverage isn’t lower than previously?

##### After PRs been raised

- [ ]  Have you checked the PR Builder passes?



Diff:
diff --git a/app/actions/AuthorisedAction.scala b/app/actions/AuthorisedAction.scala
index 95ef0cf5..4077da8a 100644
--- a/app/actions/AuthorisedAction.scala
+++ b/app/actions/AuthorisedAction.scala
@@ -128,25 +128,21 @@ class AuthorisedAction @Inject()(authService: AuthorisationService,
     case _: NoActiveSession =>
       logger.info(s"$agentAuthLogString - No active session. Redirecting to ${appConfig.signInUrl}")
       signInRedirectFutureResult
-    case _: AuthorisationException if appConfig.emaSupportingAgentsEnabled =>
+    case _: AuthorisationException =>
       authService.authorised(secondaryAgentPredicate(mtdItId))
         .retrieve(allEnrolments)(
           enrolments => handleForValidAgent(block, mtdItId, nino, enrolments, isSupportingAgent = true)
         )
         .recover {
           case _: AuthorisationException =>
-            logger.warn(s"$agentAuthLogString - Agent does not have delegated primary or secondary authority for Client.")
+            logger.warn(s"$agentAuthLogString - Agent does not have delegated authority for Client.")
             agentErrorRedirectResult
           case e =>
-            logger.error(s"[AuthorisedAction][agentAuthentication] - Unexpected exception of type '${e.getClass.getSimpleName}' was caught.")
+            logger.error(s"$agentAuthLogString - Unexpected exception of type '${e.getClass.getSimpleName}' was caught.")
             errorHandler.internalServerError()
         }
-    case _: AuthorisationException =>
-      logger.warn(s"$agentAuthLogString - Agent does not have delegated authority for Client.")
-      Future.successful(agentErrorRedirectResult)
-
     case e =>
-      logger.error(s"[AuthorisedAction][agentAuthentication] - Unexpected exception of type '${e.getClass.getSimpleName}' was caught.")
+      logger.error(s"$agentAuthLogString - Unexpected exception of type '${e.getClass.getSimpleName}' was caught.")
       Future.successful(errorHandler.internalServerError())
   }
 
@@ -156,19 +152,24 @@ class AuthorisedAction @Inject()(authService: AuthorisationService,
                                      enrolments: Enrolments,
                                      isSupportingAgent: Boolean)
                                     (implicit request: Request[A], hc: HeaderCarrier): Future[Result] = {
-    enrolmentGetIdentifierValue(Agent.key, Agent.value, enrolments) match {
-      case Some(arn) => sessionIdBlock(
-        errorLogString = s"$agentAuthLogString - No session id in request",
-        errorAction = signInRedirectFutureResult
-      )(sessionId =>
-        block(AuthorisationRequest(
-          user = models.User(mtdItId, Some(arn), nino, sessionId, AffinityGroup.Agent.toString, isSupportingAgent),
-          request = request
-        ))
-      )
-      case None =>
-        logger.info(s"$agentAuthLogString - Agent with no HMRC-AS-AGENT enrolment. Rendering unauthorised view.")
-        Future.successful(Redirect(controllers.errors.routes.YouNeedAgentServicesController.show))
+    if (isSupportingAgent) {
+      logger.warn(s"$agentAuthLogString - Secondary agent unauthorised")
+      Future.successful(Redirect(controllers.errors.routes.SupportingAgentAuthErrorController.show))
+    } else {
+      enrolmentGetIdentifierValue(Agent.key, Agent.value, enrolments) match {
+        case Some(arn) => sessionIdBlock(
+          errorLogString = s"$agentAuthLogString - No session id in request",
+          errorAction = signInRedirectFutureResult
+        )(sessionId =>
+          block(AuthorisationRequest(
+            user = models.User(mtdItId, Some(arn), nino, sessionId, AffinityGroup.Agent.toString, isSupportingAgent),
+            request = request
+          ))
+        )
+        case None =>
+          logger.info(s"$agentAuthLogString - Agent with no HMRC-AS-AGENT enrolment. Rendering unauthorised view.")
+          Future.successful(Redirect(controllers.errors.routes.YouNeedAgentServicesController.show))
+      }
     }
   }
 
@@ -186,7 +187,7 @@ class AuthorisedAction @Inject()(authService: AuthorisationService,
           )
           .recoverWith(agentRecovery(block, mtdItId, nino))
       case (mtdItId, nino) =>
-        logger.info(s"[AuthorisedAction][agentAuthentication] - Agent does not have session key values. " +
+        logger.info(s"$agentAuthLogString - Agent does not have session key values. " +
           s"Redirecting to view & change. MTDITID missing:${mtdItId.isEmpty}, NINO missing:${nino.isEmpty}")
         Future.successful(Redirect(appConfig.viewAndChangeEnterUtrUrl))
     }
diff --git a/app/config/AppConfig.scala b/app/config/AppConfig.scala
index be51f4f5..c2ae40ef 100644
--- a/app/config/AppConfig.scala
+++ b/app/config/AppConfig.scala
@@ -41,14 +41,13 @@ trait AppConfig {
   def contactUrl(isAgent: Boolean): String
   def feedbackSurveyUrl(isAgent: Boolean): String
   def viewAndChangeEnterUtrUrl: String
+  def viewAndChangeViewUrlAgent: String
   def betaFeedbackUrl(request: RequestHeader, isAgent: Boolean): String
   def stateBenefitsServiceBaseUrl: String
   def taxYearErrorFeature: Boolean
 
   // TODO: Get rid of this
   def routeToSwitchLanguage: String => Call
-
-  def emaSupportingAgentsEnabled: Boolean
 }
 
 @Singleton
@@ -105,6 +104,7 @@ class AppConfigImpl @Inject()(servicesConfig: ServicesConfig) extends AppConfig
   def feedbackSurveyUrl(isAgent: Boolean): String = s"$feedbackFrontendUrl/feedback/${contactFormServiceIdentifier(isAgent)}"
 
   def viewAndChangeEnterUtrUrl: String = s"$vcBaseUrl/report-quarterly/income-and-expenses/view/agents/client-utr"
+  def viewAndChangeViewUrlAgent: String = s"$vcBaseUrl/report-quarterly/income-and-expenses/view/agents"
 
   def betaFeedbackUrl(request: RequestHeader, isAgent: Boolean): String = {
     val requestUri = SafeRedirectUrl(applicationUrl + request.uri).encodedUrl
@@ -119,6 +119,4 @@ class AppConfigImpl @Inject()(servicesConfig: ServicesConfig) extends AppConfig
   // TODO: Get rid of this
   def routeToSwitchLanguage: String => Call =
     (lang: String) => controllers.routes.LanguageSwitchController.switchToLanguage(lang)
-
-  lazy val emaSupportingAgentsEnabled: Boolean = servicesConfig.getBoolean("feature-switch.ema-supporting-agents-enabled")
 }
diff --git a/app/controllers/errors/SupportingAgentAuthErrorController.scala b/app/controllers/errors/SupportingAgentAuthErrorController.scala
new file mode 100644
index 00000000..5813b23c
--- /dev/null
+++ b/app/controllers/errors/SupportingAgentAuthErrorController.scala
@@ -0,0 +1,36 @@
+/*
+ * 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 controllers.errors
+
+import config.AppConfig
+import play.api.i18n.I18nSupport
+import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
+import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController
+import views.html.pages.SupportingAgentAuthErrorView
+
+import javax.inject.Inject
+
+class SupportingAgentAuthErrorController @Inject()(
+                                                    val mcc: MessagesControllerComponents,
+                                                    implicit val appConfig: AppConfig,
+                                                    view: SupportingAgentAuthErrorView
+                                                  ) extends FrontendController(mcc) with I18nSupport {
+
+  def show: Action[AnyContent] = Action { implicit request =>
+    Unauthorized(view())
+  }
+}
diff --git a/app/views/pages/SupportingAgentAuthErrorView.scala.html b/app/views/pages/SupportingAgentAuthErrorView.scala.html
new file mode 100644
index 00000000..17c2e951
--- /dev/null
+++ b/app/views/pages/SupportingAgentAuthErrorView.scala.html
@@ -0,0 +1,39 @@
+@*
+ * 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.
+ *@
+
+@import views.html.templates.Layout
+@import views.html.templates.helpers.Heading
+
+@this(layout: Layout, heading: Heading)
+
+@()(implicit request: Request[_], messages: Messages, appConfig: AppConfig)
+@layout(messages("error.unauthorised-user.heading"), true) {
+
+    @heading(messages("error.unauthorised-user.heading"), "", "xl")
+
+    <p class="govuk-body">@messages("error.supp.agent.unauthorised.paragraph")</p>
+
+    <p class="govuk-body">
+        <a class="govuk-link" id="account_home_link" href="@appConfig.viewAndChangeViewUrlAgent">
+            @messages("error.supp.agent.unauthorised.link")
+        </a>
+    </p>
+
+}
+
+@{
+// $COVERAGE-OFF$
+}
diff --git a/conf/app.routes b/conf/app.routes
index de5ee162..041e8832 100644
--- a/conf/app.routes
+++ b/conf/app.routes
@@ -52,3 +52,4 @@ GET         /error/wrong-tax-year
 GET         /error/you-need-client-authorisation                                        controllers.errors.AgentAuthErrorController.show
 GET         /error/not-authorised-to-use-service                                        controllers.errors.UnauthorisedUserErrorController.show
 GET         /error/you-need-agent-services-account                                      controllers.errors.YouNeedAgentServicesController.show
+GET         /error/supporting-agent-not-authorised                                      controllers.errors.SupportingAgentAuthErrorController.show
diff --git a/conf/application.conf b/conf/application.conf
index 4a67d7e0..4eeb8285 100644
--- a/conf/application.conf
+++ b/conf/application.conf
@@ -128,7 +128,6 @@ taxYearErrorFeatureSwitch = false
 feature-switch {
   welshLanguageEnabled = true
   sectionCompletedQuestionEnabled = false
-  ema-supporting-agents-enabled = true
 }
 
 tracking-consent-frontend {
diff --git a/conf/messages b/conf/messages
index 12591b42..8958cd50 100644
--- a/conf/messages
+++ b/conf/messages
@@ -325,6 +325,9 @@ taxYear-error-template.paragraph.1 = You can only enter information for the tax
 taxYear-error-template.paragraph.1.single = You can only enter information for a valid tax year.
 taxYear-error-template.paragraph.2 = Check that you’ve entered the correct web address.
 
+error.supp.agent.unauthorised.paragraph = You’re a supporting agent for this client. Only your client or their main agent, if they have one, can access and submit their tax return.
+error.supp.agent.unauthorised.link = Go back to account home
+
 #################       Timeout messages        ########################################
 timeout.button = Sign in
 timeout.heading = For your security, we signed you out
diff --git a/conf/messages.cy b/conf/messages.cy
index 1660ab31..edc53a26 100644
--- a/conf/messages.cy
+++ b/conf/messages.cy
@@ -325,6 +325,9 @@ taxYear-error-template.paragraph.1 = Gallwch nodi gwybodaeth ar gyfer blynyddoed
 taxYear-error-template.paragraph.1.single = Gallwch nodi gwybodaeth ar gyfer blwyddyn dreth ddilys yn unig.
 taxYear-error-template.paragraph.2 = Gwiriwch eich bod wedi nodi’r cyfeiriad gwe cywir.
 
+error.supp.agent.unauthorised.paragraph = Rydych yn asiant ategol ar gyfer y cleient hwn. Dim ond eich cleient neu ei brif asiant, os oes ganddo un, sy’n gallu cael at a chyflwyno ei Ffurflen Dreth.
+error.supp.agent.unauthorised.link = Yn ôl i hafan y cyfrif
+
 #################       Timeout messages        ########################################
 timeout.button = Mewngofnodi
 timeout.heading = Er eich diogelwch, gwnaethom eich allgofnodi
diff --git a/project/AppDependencies.scala b/project/AppDependencies.scala
index ae44bba8..c8a77de0 100644
--- a/project/AppDependencies.scala
+++ b/project/AppDependencies.scala
@@ -19,7 +19,7 @@ import sbt.*
 object AppDependencies {
 
   private val bootstrapPlay30Version = "8.6.0"
-  private val hmrcMongoPlay30Version = "2.2.0"
+  private val hmrcMongoPlay30Version = "2.5.0"
 
   val jacksonAndPlayExclusions: Seq[InclusionRule] = Seq(
     ExclusionRule(organization = "com.fasterxml.jackson.core"),
@@ -31,7 +31,7 @@ object AppDependencies {
 
   val compile: Seq[ModuleID] = Seq(
     "uk.gov.hmrc"                   %% "bootstrap-frontend-play-30" % bootstrapPlay30Version,
-    "uk.gov.hmrc"                   %% "play-frontend-hmrc-play-30" % "11.11.0",
+    "uk.gov.hmrc"                   %% "play-frontend-hmrc-play-30" % "11.12.0",
     "uk.gov.hmrc.mongo"             %% "hmrc-mongo-play-30"         % hmrcMongoPlay30Version,
     "com.fasterxml.jackson.module"  %% "jackson-module-scala"       % "2.17.0",
     "com.beachape"                  %% "enumeratum"                 % "1.7.3",
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 1e59e259..272bf219 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -23,11 +23,11 @@ ThisBuild / libraryDependencySchemes ++= Seq(
 )
 
 addSbtPlugin("uk.gov.hmrc"          % "sbt-auto-build"         % "3.24.0")
-addSbtPlugin("uk.gov.hmrc"          % "sbt-distributables"     % "2.5.0")
-addSbtPlugin("org.playframework"    % "sbt-plugin"             % "3.0.5")
+addSbtPlugin("uk.gov.hmrc"          % "sbt-distributables"     % "2.6.0")
+addSbtPlugin("org.playframework"    % "sbt-plugin"             % "3.0.6")
 addSbtPlugin("org.scoverage"        % "sbt-scoverage"          % "2.0.9")
-addSbtPlugin("com.typesafe.sbt"     % "sbt-gzip"               % "1.0.2")
+addSbtPlugin("com.github.sbt"       % "sbt-gzip"               % "2.0.0")
 addSbtPlugin("io.github.irundaia"   % "sbt-sassify"            % "1.5.2")
 addSbtPlugin("org.scalastyle"       %% "scalastyle-sbt-plugin" % "1.0.0")
-addSbtPlugin("net.ground5hark.sbt"  % "sbt-concat"             % "0.2.0")
-addSbtPlugin("com.typesafe.sbt"     % "sbt-digest"             % "1.1.4")
\ No newline at end of file
+addSbtPlugin("com.github.sbt"       % "sbt-concat"             % "1.0.0")
+addSbtPlugin("com.github.sbt"       % "sbt-digest"             % "2.1.0")
\ No newline at end of file
diff --git a/test/actions/AuthorisedActionSpec.scala b/test/actions/AuthorisedActionSpec.scala
index 4a0ab5d5..e16f04e0 100644
--- a/test/actions/AuthorisedActionSpec.scala
+++ b/test/actions/AuthorisedActionSpec.scala
@@ -21,14 +21,14 @@ import models.authorisation.SessionValues
 import models.authorisation.SessionValues.{CLIENT_MTDITID, CLIENT_NINO}
 import models.requests.AuthorisationRequest
 import org.apache.pekko.actor.ActorSystem
-import org.scalamock.handlers.{CallHandler0, CallHandler4}
+import org.scalamock.handlers.CallHandler4
 import org.scalamock.scalatest.MockFactory
 import play.api.http.{HeaderNames, Status => TestStatus}
 import play.api.mvc.Results.{InternalServerError, Ok}
 import play.api.mvc._
 import play.api.test.{FakeRequest, ResultExtractors}
 import support.ControllerUnitTest
-import support.builders.UserBuilder.{aUser, anAgentUser}
+import support.builders.UserBuilder.aUser
 import support.mocks.{MockAppConfig, MockAuthorisationService, MockErrorHandler}
 import support.providers.FakeRequestProvider
 import uk.gov.hmrc.auth.core._
@@ -67,11 +67,6 @@ class AuthorisedActionSpec extends ControllerUnitTest
     Enrolment(Nino.key, Seq(EnrolmentIdentifier(Nino.value, aUser.nino)), "Activated")
   ))
 
-  private val agentEnrolment = Enrolments(Set(
-    Enrolment(Individual.key, Seq(EnrolmentIdentifier(Individual.value, aUser.mtditid)), "Activated"),
-    Enrolment(Agent.key, Seq(EnrolmentIdentifier(Agent.value, anAgentUser.arn.get)), "Activated")
-  ))
-
   private val underTest = new AuthorisedAction(authorisationService, appConfig, mcc: ControllerComponents,mockErrorHandler)(executionContext)
 
   def bodyOf(awaitable: Future[Result]): String = {
@@ -97,11 +92,6 @@ class AuthorisedActionSpec extends ControllerUnitTest
         .withIdentifier("MTDITID", mtdId)
         .withDelegatedAuthRule("mtd-it-auth-supp")
 
-    def mockMultipleAgentsSwitch(bool: Boolean): CallHandler0[Boolean] =
-      (mockAppConfig.emaSupportingAgentsEnabled _: () => Boolean)
-        .expects()
-        .returning(bool)
-        .anyNumberOfTimes()
 
     val primaryAgentEnrolment: Enrolments = Enrolments(Set(
       Enrolment(Individual.key, Seq(EnrolmentIdentifier(Individual.value, mtdItId)), "Activated"),
@@ -298,7 +288,6 @@ class AuthorisedActionSpec extends ControllerUnitTest
 
       "results in an Exception other than an AuthException error being returned for Primary Agent check" should {
         "render an ISE page" in new AgentTest {
-          mockMultipleAgentsSwitch(false)
 
           mockAuthReturnException(new Exception("bang"), primaryAgentPredicate(mtdItId))
           mockInternalServerError(InternalServerError("An unexpected error occurred"))
@@ -313,27 +302,9 @@ class AuthorisedActionSpec extends ControllerUnitTest
         }
       }
 
-      "[EMA disabled] results in an AuthorisationException error being returned from Auth" should {
-        "return a redirect to the agent error page" in new AgentTest {
-          mockMultipleAgentsSwitch(false)
-
-          object AuthException extends AuthorisationException("Some reason")
-          mockAuthReturnException(AuthException, primaryAgentPredicate(mtdItId))
-
-          val result: Future[Result] = testAuth.agentAuthentication(testBlock)(
-            request = FakeRequest().withSession(fakeRequestWithMtditidAndNino.session.data.toSeq :_*),
-            hc = emptyHeaderCarrier
-          )
-
-          status(result) shouldBe SEE_OTHER
-          redirectUrl(result) shouldBe s"$baseUrl/error/you-need-client-authorisation"
-        }
-      }
-
-      "[EMA enabled] results in an AuthorisationException error being returned from Auth" should {
+      "results in an AuthorisationException error being returned from Auth" should {
 
         "render an ISE page when secondary agent auth call also fails with non-Auth exception" in new AgentTest {
-          mockMultipleAgentsSwitch(true)
 
           mockAuthReturnException(InsufficientEnrolments(), primaryAgentPredicate(mtdItId))
           mockAuthReturnException(new Exception("bang"), secondaryAgentPredicate(mtdItId))
@@ -348,8 +319,20 @@ class AuthorisedActionSpec extends ControllerUnitTest
           bodyOf(result) shouldBe "An unexpected error occurred"
         }
 
+        "return a redirect to the agent error page when secondary agent auth call also fails due to insufficient enrolments" in new AgentTest {
+
+          mockAuthReturnException(InsufficientEnrolments(), primaryAgentPredicate(mtdItId))
+          mockAuthReturnException(InsufficientEnrolments(), secondaryAgentPredicate(mtdItId))
+
+          lazy val result: Future[Result] = testAuth.agentAuthentication(testBlock)(
+            request = FakeRequest().withSession(fakeRequestWithMtditidAndNino.session.data.toSeq: _*),
+            hc = emptyHeaderCarrier
+          )
+          status(result) shouldBe SEE_OTHER
+          redirectUrl(result) shouldBe s"$baseUrl/error/you-need-client-authorisation"
+        }
+
         "return a redirect to the agent error page when secondary agent auth call also fails" in new AgentTest {
-          mockMultipleAgentsSwitch(true)
 
           object AuthException extends AuthorisationException("Some reason")
           mockAuthReturnException(AuthException, primaryAgentPredicate(mtdItId))
@@ -364,8 +347,7 @@ class AuthorisedActionSpec extends ControllerUnitTest
           redirectUrl(result) shouldBe s"$baseUrl/error/you-need-client-authorisation"
         }
 
-        "handle appropriately when a supporting agent is properly authorised" in new AgentTest {
-          mockMultipleAgentsSwitch(true)
+        "return a redirect to the secondary agent not authorised page when a supporting agent has correct credentials" in new AgentTest {
 
           object AuthException extends AuthorisationException("Some reason")
           mockAuthReturnException(AuthException, primaryAgentPredicate(mtdItId))
@@ -376,8 +358,8 @@ class AuthorisedActionSpec extends ControllerUnitTest
             hc = validHeaderCarrier
           )
 
-          status(result) shouldBe OK
-          contentAsString(result) shouldBe s"$mtdItId $arn"
+          status(result) shouldBe SEE_OTHER
+          redirectUrl(result) shouldBe s"$baseUrl/error/supporting-agent-not-authorised"
         }
       }
 
diff --git a/test/controllers/errors/SupportingAgentAuthErrorControllerSpec.scala b/test/controllers/errors/SupportingAgentAuthErrorControllerSpec.scala
new file mode 100644
index 00000000..18617a86
--- /dev/null
+++ b/test/controllers/errors/SupportingAgentAuthErrorControllerSpec.scala
@@ -0,0 +1,39 @@
+/*
+ * 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 controllers.errors
+
+import play.api.test.FakeRequest
+import play.api.test.Helpers._
+import support.ControllerUnitTest
+import views.html.pages.SupportingAgentAuthErrorView
+
+class SupportingAgentAuthErrorControllerSpec extends ControllerUnitTest {
+
+  private val pageView: SupportingAgentAuthErrorView = app.injector.instanceOf[SupportingAgentAuthErrorView]
+
+  lazy val underTest = new SupportingAgentAuthErrorController(stubMessagesControllerComponents(), appConfig, pageView)
+
+  "The show method" should {
+    "return an UNAUTHORIZED response when .show() is called" in {
+      val fakeRequest = FakeRequest(GET, routes.SupportingAgentAuthErrorController.show.url)
+      val result = underTest.show()(fakeRequest)
+
+      status(result) shouldBe UNAUTHORIZED
+      contentAsString(result) shouldBe pageView()(fakeRequest, stubMessages(), appConfig).toString
+    }
+  }
+}
diff --git a/test/support/stubs/AppConfigStub.scala b/test/support/stubs/AppConfigStub.scala
index 10f3aa7c..847259b4 100644
--- a/test/support/stubs/AppConfigStub.scala
+++ b/test/support/stubs/AppConfigStub.scala
@@ -34,6 +34,7 @@ class AppConfigStub extends MockFactory {
     override lazy val signInUrl: String = "/sign-in-url"
     override lazy val incomeTaxSubmissionIvRedirect: String = "/update-and-submit-income-tax-return/iv-uplift"
     override lazy val viewAndChangeEnterUtrUrl: String = "/report-quarterly/income-and-expenses/view/agents/client-utr"
+    override lazy val viewAndChangeViewUrlAgent: String = "/report-quarterly/income-and-expenses/view/agents"
     override lazy val incomeTaxSubmissionBaseUrl: String = "/income-tax-submission-base-url"
     override lazy val signOutUrl: String = "/sign-out-url"
 
diff --git a/test/views/pages/SupportingAgentAuthErrorViewSpec.scala b/test/views/pages/SupportingAgentAuthErrorViewSpec.scala
new file mode 100644
index 00000000..2b3590da
--- /dev/null
+++ b/test/views/pages/SupportingAgentAuthErrorViewSpec.scala
@@ -0,0 +1,82 @@
+/*
+ * 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 views.pages
+
+import models.requests.AuthorisationRequest
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Document
+import play.api.i18n.Messages
+import play.api.mvc.AnyContent
+import support.ViewUnitTest
+import views.html.pages.SupportingAgentAuthErrorView
+
+class SupportingAgentAuthErrorViewSpec extends ViewUnitTest {
+
+  object Selectors {
+    val p1Selector = "#main-content > div > div > p:nth-child(2)"
+    val authoriseAsAnAgentLinkSelector = "#account_home_link"
+  }
+
+  trait CommonExpectedResults {
+    val h1Expected: String
+    val accessServiceText: String
+    val linkText: String
+    val tryAnotherClientExpectedHref: String
+  }
+
+  object CommonExpectedEN extends CommonExpectedResults {
+    val h1Expected = "You are not authorised to use this service"
+    val accessServiceText: String = "You’re a supporting agent for this client. Only your client or their main agent, if they have one, can access and submit their tax return."
+    val linkText = "Go back to account home"
+    val tryAnotherClientExpectedHref = "/report-quarterly/income-and-expenses/view/agents"
+  }
+
+  object CommonExpectedCY extends CommonExpectedResults {
+    val h1Expected = "Nid ydych wedi’ch awdurdodi i ddefnyddio’r gwasanaeth hwn"
+    val accessServiceText: String = "Rydych yn asiant ategol ar gyfer y cleient hwn. Dim ond eich cleient neu ei brif asiant, os oes ganddo un, sy’n gallu cael at a chyflwyno ei Ffurflen Dreth."
+    val linkText = "Yn ôl i hafan y cyfrif"
+    val tryAnotherClientExpectedHref = "/report-quarterly/income-and-expenses/view/agents"
+  }
+
+  val userScenarios: Seq[UserScenario[CommonExpectedResults, CommonExpectedResults]] = Seq(
+    UserScenario(isWelsh = false, isAgent = true, CommonExpectedEN),
+    UserScenario(isWelsh = true, isAgent = true, CommonExpectedCY)
+  )
+
+  private lazy val underTest = inject[SupportingAgentAuthErrorView]
+
+  userScenarios.foreach { userScenario =>
+    import Selectors._
+    import userScenario.commonExpectedResults._
+    s"language is ${welshTest(userScenario.isWelsh)} and request is from an ${agentTest(userScenario.isAgent)}" should {
+      "Render correctly" which {
+        implicit val authRequest: AuthorisationRequest[AnyContent] = getAuthRequest(userScenario.isAgent)
+        implicit val messages: Messages = getMessages(userScenario.isWelsh)
+
+        val htmlFormat = underTest()
+
+        implicit val document: Document = Jsoup.parse(htmlFormat.body)
+
+        welshToggleCheck(userScenario.isWelsh)
+        titleCheck(h1Expected, userScenario.isWelsh)
+        h1Check(h1Expected, size = "xl")
+        textOnPageCheck(s"$accessServiceText", p1Selector)
+        linkCheck(linkText, authoriseAsAnAgentLinkSelector, tryAnotherClientExpectedHref)
+      }
+    }
+  }
+}


Reviews:
user: login: matthewwardcap
body: Just a couple minor things.

Also a few PR-bot comments need addressing. For the bobby rules you need to swap out these three for slightly tweaked ones sbt-gzip, sbt-concat, sbt-digest. Here's the new ones below:

`
addSbtPlugin("com.github.sbt"       % "sbt-gzip"               % "2.0.0")
`

`
addSbtPlugin("com.github.sbt"       % "sbt-concat"             % "1.0.0")
`

`
addSbtPlugin("com.github.sbt"       % "sbt-digest"             % "2.1.0")
`
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T08:48:32Z
user: login: baha-aboukoura
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T11:11:33Z
user: login: matthewwardcap
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T12:50:28Z

Review comments:
diff_hunk: @@ -128,25 +128,25 @@ class AuthorisedAction @Inject()(authService: AuthorisationService,
     case _: NoActiveSession =>
       logger.info(s"$agentAuthLogString - No active session. Redirecting to ${appConfig.signInUrl}")
       signInRedirectFutureResult
-    case _: AuthorisationException if appConfig.emaSupportingAgentsEnabled =>
+    case _: AuthorisationException =>
       authService.authorised(secondaryAgentPredicate(mtdItId))
         .retrieve(allEnrolments)(
           enrolments => handleForValidAgent(block, mtdItId, nino, enrolments, isSupportingAgent = true)
         )
         .recover {
           case _: AuthorisationException =>
-            logger.warn(s"$agentAuthLogString - Agent does not have delegated primary or secondary authority for Client.")
+            logger.warn(s"$agentAuthLogString - Agent does not have delegated authority for Client.")
             agentErrorRedirectResult
           case e =>
-            logger.error(s"[AuthorisedAction][agentAuthentication] - Unexpected exception of type '${e.getClass.getSimpleName}' was caught.")
+            logger.error(s"$agentAuthLogString - Unexpected exception of type '${e.getClass.getSimpleName}' was caught.")
             errorHandler.internalServerError()
         }
     case _: AuthorisationException =>
path: app/actions/AuthorisedAction.scala
user: login: matthewwardcap
body: This case should be removed it's now impossible to ever be triggered as we already have case _: AuthorisationException above it
created_at: 2025-03-10T08:38:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -313,27 +307,9 @@ class AuthorisedActionSpec extends ControllerUnitTest
         }
       }
 
-      "[EMA disabled] results in an AuthorisationException error being returned from Auth" should {
-        "return a redirect to the agent error page" in new AgentTest {
-          mockMultipleAgentsSwitch(false)
-
-          object AuthException extends AuthorisationException("Some reason")
-          mockAuthReturnException(AuthException, primaryAgentPredicate(mtdItId))
-
-          val result: Future[Result] = testAuth.agentAuthentication(testBlock)(
-            request = FakeRequest().withSession(fakeRequestWithMtditidAndNino.session.data.toSeq :_*),
-            hc = emptyHeaderCarrier
-          )
-
-          status(result) shouldBe SEE_OTHER
-          redirectUrl(result) shouldBe s"$baseUrl/error/you-need-client-authorisation"
-        }
-      }
-
-      "[EMA enabled] results in an AuthorisationException error being returned from Auth" should {
+      "results in an AuthorisationException error being returned from Auth" should {
 
         "render an ISE page when secondary agent auth call also fails with non-Auth exception" in new AgentTest {
-          mockMultipleAgentsSwitch(true)
 
           mockAuthReturnException(InsufficientEnrolments(), primaryAgentPredicate(mtdItId))
           mockAuthReturnException(new Exception("bang"), secondaryAgentPredicate(mtdItId))
path: test/actions/AuthorisedActionSpec.scala
user: login: matthewwardcap
body: Could we possibly add a similar new test scenario in addition to this one for the following case:

```
mockAuthReturnException(InsufficientEnrolments(), primaryAgentPredicate(mtdItId))
mockAuthReturnException(InsufficientEnrolments(), secondaryAgentPredicate(mtdItId))
```

Maybe a new test could be called
"render an ISE page when secondary agent auth call also fails with insufficient enrolments"
created_at: 2025-03-10T08:40:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -128,25 +128,25 @@ class AuthorisedAction @Inject()(authService: AuthorisationService,
     case _: NoActiveSession =>
       logger.info(s"$agentAuthLogString - No active session. Redirecting to ${appConfig.signInUrl}")
       signInRedirectFutureResult
-    case _: AuthorisationException if appConfig.emaSupportingAgentsEnabled =>
+    case _: AuthorisationException =>
       authService.authorised(secondaryAgentPredicate(mtdItId))
         .retrieve(allEnrolments)(
           enrolments => handleForValidAgent(block, mtdItId, nino, enrolments, isSupportingAgent = true)
         )
         .recover {
           case _: AuthorisationException =>
-            logger.warn(s"$agentAuthLogString - Agent does not have delegated primary or secondary authority for Client.")
+            logger.warn(s"$agentAuthLogString - Agent does not have delegated authority for Client.")
             agentErrorRedirectResult
           case e =>
-            logger.error(s"[AuthorisedAction][agentAuthentication] - Unexpected exception of type '${e.getClass.getSimpleName}' was caught.")
+            logger.error(s"$agentAuthLogString - Unexpected exception of type '${e.getClass.getSimpleName}' was caught.")
             errorHandler.internalServerError()
         }
     case _: AuthorisationException =>
path: app/actions/AuthorisedAction.scala
user: login: baha-aboukoura
body: when I removed it one of the unit tests started failing
created_at: 2025-03-10T11:11:32Z
author_association: CONTRIBUTOR

PR comments:
user: login: platops-pr-bot
created_at: 2025-03-07T16:27:23Z
body: - `uk.gov.hmrc.sbt-distributables` can be updated to `2.6.0` in [project/plugins.sbt](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/plugins.sbt#L26)
- uk.gov.hmrc.mongo:hmrc-mongo-play-30 can be updated from 2.2.0 to 2.5.0 in [project/AppDependencies.scala](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/AppDependencies.scala#L1).
- uk.gov.hmrc.mongo:hmrc-mongo-test-play-30 can be updated from 2.2.0 to 2.5.0 in [project/AppDependencies.scala](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/AppDependencies.scala#L1).
- There is a bobby warning (builds will be prevented after 30 Jun 2025) - see the pr builder for details
author_association: NONE
user: login: platops-pr-bot
created_at: 2025-03-10T11:19:42Z
body: - ~~`uk.gov.hmrc.sbt-distributables` can be updated to `2.6.0` in [project/plugins.sbt](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/plugins.sbt#L26)~~
- ~~uk.gov.hmrc.mongo:hmrc-mongo-play-30 can be updated from 2.2.0 to 2.5.0 in [project/AppDependencies.scala](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/AppDependencies.scala#L1).~~
- ~~uk.gov.hmrc.mongo:hmrc-mongo-test-play-30 can be updated from 2.2.0 to 2.5.0 in [project/AppDependencies.scala](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/AppDependencies.scala#L1).~~
- ~~There is a bobby warning (builds will be prevented after 30 Jun 2025) - see the pr builder for details~~
- Runtime issues may occur. Change `org.playframework:sbt-plugin` to `3.0.6` in [project/plugins.sbt](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/plugins.sbt#L27) - to match what `uk.gov.hmrc.mongo.hmrc-mongo-play-30` uses
author_association: NONE
user: login: platops-pr-bot
created_at: 2025-03-10T11:33:06Z
body: - ~~`uk.gov.hmrc.sbt-distributables` can be updated to `2.6.0` in [project/plugins.sbt](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/plugins.sbt#L26)~~
- ~~uk.gov.hmrc.mongo:hmrc-mongo-play-30 can be updated from 2.2.0 to 2.5.0 in [project/AppDependencies.scala](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/AppDependencies.scala#L1).~~
- ~~uk.gov.hmrc.mongo:hmrc-mongo-test-play-30 can be updated from 2.2.0 to 2.5.0 in [project/AppDependencies.scala](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/AppDependencies.scala#L1).~~
- ~~There is a bobby warning (builds will be prevented after 30 Jun 2025) - see the pr builder for details~~
- ~~Runtime issues may occur. Change `org.playframework:sbt-plugin` to `3.0.6` in [project/plugins.sbt](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/plugins.sbt#L27) - to match what `uk.gov.hmrc.mongo.hmrc-mongo-play-30` uses~~
- uk.gov.hmrc:play-frontend-hmrc-play-30 can be updated from 11.11.0 to 11.12.0 in [project/AppDependencies.scala](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/AppDependencies.scala#L1).
author_association: NONE
user: login: platops-pr-bot
created_at: 2025-03-10T11:45:32Z
body: - ~~`uk.gov.hmrc.sbt-distributables` can be updated to `2.6.0` in [project/plugins.sbt](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/plugins.sbt#L26)~~
- ~~uk.gov.hmrc.mongo:hmrc-mongo-play-30 can be updated from 2.2.0 to 2.5.0 in [project/AppDependencies.scala](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/AppDependencies.scala#L1).~~
- ~~uk.gov.hmrc.mongo:hmrc-mongo-test-play-30 can be updated from 2.2.0 to 2.5.0 in [project/AppDependencies.scala](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/AppDependencies.scala#L1).~~
- ~~There is a bobby warning (builds will be prevented after 30 Jun 2025) - see the pr builder for details~~
- ~~Runtime issues may occur. Change `org.playframework:sbt-plugin` to `3.0.6` in [project/plugins.sbt](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/plugins.sbt#L27) - to match what `uk.gov.hmrc.mongo.hmrc-mongo-play-30` uses~~
- ~~uk.gov.hmrc:play-frontend-hmrc-play-30 can be updated from 11.11.0 to 11.12.0 in [project/AppDependencies.scala](https://github.com/hmrc/income-tax-state-benefits-frontend/blob/SASS-11220/project/AppDependencies.scala#L1).~~
author_association: NONE