def callback()

in support-frontend/app/controllers/AuthCodeFlowController.scala [85:171]


  def callback(
      code: Option[String],
      state: String,
      error: Option[String],
      errorDescription: Option[String],
  ): Action[AnyContent] = Action.async { implicit request =>
    val originUrl = request.session.get(SessionKey.originUrl)
    val referringUrl = request.session.get(SessionKey.referringUrl)
    val codeVerifier = request.session.get(SessionKey.codeVerifier)
    val sessionState = request.session.get(SessionKey.state)

    def cleansed(result: Result) =
      result.removingFromSession(
        SessionKey.originUrl,
        SessionKey.referringUrl,
        SessionKey.state,
        SessionKey.codeVerifier,
      )

    /*
     * We redirect back to the URL where the auth flow began and append a query param to identify the referring URL.
     * The referring URL is appended because it's lost during the auth flow, as the authorize() call on the auth server has a 'no-referrer'
     * in its Referrer-Policy response header.  This enables any analytics to know the referring URL of the page that gets redirected to at the end
     * of the auth flow as it will be the same as the referrer of the page that triggered the auth flow.
     */
    lazy val redirect = {
      /* This has to correspond with the param name at
       * https://github.com/guardian/ophan/blob/7cb4283cb6543c009a9e7a0d3d2486744facc902/tracker-js/assets/coffee/ophan/core.coffee#L41
       */
      val referrerParamName = "pre-auth-ref"
      val origin = originUrl.getOrElse("/")
      val url = referringUrl
        .map { refUrl =>
          val paramValue = urlEncode(refUrl)
          replaceParam(origin, referrerParamName, paramValue)
        }
        .getOrElse(origin)
      cleansed(Redirect(url))
        .flashing(FlashKey.authTried -> "true")
    }

    (code, codeVerifier, sessionState, error, errorDescription) match {

      case (Some(authorisationCode), Some(verifier), Some(stateInSession), None, None) if state == stateInSession =>
        /*
         * Successfully received an authorisation code - can now try to redeem it for ID and access tokens.
         * This is step (2) in the flow.
         * See https://developer.okta.com/docs/reference/api/oidc/#token
         */
        val requestBody = Map(
          "grant_type" -> "authorization_code",
          "client_id" -> config.oauthClientId,
          "code_verifier" -> verifier,
          "code" -> authorisationCode,
          "redirect_uri" -> oauthCallbackUrl(request),
        )

        authService
          .getTokens(toQuery(requestBody))
          .map { response =>
            if (response.status == 200) {
              val idToken = (response.json \ "id_token").as[String]
              val accessToken = (response.json \ "access_token").as[String]
              redirect.withCookies(
                secureCookie(config.idTokenCookieName, idToken),
                secureCookie(config.accessTokenCookieName, accessToken),
              )
            } else {
              logger.error(s"Failed to generate auth tokens: HTTP ${response.status}: ${response.body}")
              redirect
            }
          }

      case (None, _, _, Some(error), _) if error == "login_required" =>
        // Failed to generate auth tokens as user is signed out - this is expected.
        Future.successful(redirect)

      case (None, _, _, Some(error), Some(errorDescription)) =>
        logger.error(s"Failed to generate auth tokens: $error: $errorDescription")
        Future.successful(cleansed(BadRequest(s"$error: $errorDescription")))

      case _ =>
        // Should never get to this case
        logger.error(s"Failed to generate auth tokens for request: ${request.body}")
        Future.successful(cleansed(BadRequest))
    }
  }