app/auth/AuthorisedAction.scala (37 lines of code) (raw):
package auth
import com.gu.identity.auth.*
import play.api.Logging
import play.api.mvc.*
import play.api.mvc.Results.*
import utils.RequestHelper.origin
import scala.concurrent.{ExecutionContext, Future}
/** An action that requires a valid access token with the given required access scopes. If the token is valid, the
* request is passed to the next action in the chain.
*
* If the request doesn't have an Authorization header or the bearer token in the header isn't a valid access token,
* the action returns a 401 Unauthorized response. If the token is valid but doesn't have the required scopes, the
* action returns a 403 Forbidden response.
*/
class AuthorisedAction(
oktaAuthService: OktaAuthService,
val parser: BodyParser[AnyContent],
requiredScopes: List[AccessScope]
)(implicit
val executionContext: ExecutionContext
) extends ActionBuilder[RequestWithClaims, AnyContent]
with ActionRefiner[Request, RequestWithClaims]
with Logging {
protected def refine[A](request: Request[A]): Future[Either[Result, RequestWithClaims[A]]] = {
Helpers.fetchBearerTokenFromAuthHeader(request.headers.get) match {
case Left(_) => Future.successful(Left(Unauthorized("Request has no Authorization header")))
case Right(token) =>
oktaAuthService
.validateAccessToken(AccessToken(token), requiredScopes)
.redeem(
{
case OktaValidationException(err: ValidationError) =>
logger.info(
s"Token validation failed for request from ${origin(request)}: ${request.method} ${request.path}: ${err.message}"
)
Left(new Status(err.suggestedHttpResponseCode)("Access token validation failed."))
case err => Left(InternalServerError(err.getMessage))
},
claims => Right(RequestWithClaims(claims, request))
)
.unsafeToFuture()
}
}
}