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))
}
}