---
title: "Secure Play Framework APIs with API Key Authentication"
description: "Secure your Play Framework API using a shared secret."
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/scalajava/playframework/secure-header"
framework: "Play Framework"
language: "Scala"
authStrategy: "shared secret header"
pageType: use-case
---

# Secure Play Framework APIs with API Key Authentication

Secure your Play Framework API using a shared secret.

## How Zuplo Handles It

Put Zuplo in front of your Play Framework backend to authenticate API keys and forward a shared secret header so your origin only accepts traffic from Zuplo.

## Play Framework Backend Code

```scala
import javax.inject._
import play.api.mvc._
import scala.concurrent.{ExecutionContext, Future}
import java.security.MessageDigest
import javax.xml.bind.DatatypeConverter

@Singleton
class ValidateSharedSecret @Inject()(val parser: BodyParsers.Default)(implicit val executionContext: ExecutionContext)
  extends ActionBuilderImpl(parser) {

  private val expectedSecret: String = sys.env.getOrElse("SHARED_SECRET", throw new Exception("Server configuration error"))

  override def invokeBlock[A](request: Request[A], block: Request[A] => Future[Result]): Future[Result] = {
    request.headers.get("x-shared-secret") match {
      case Some(secret) if timingSafeEqual(secret, expectedSecret) =>
        block(request)
      case Some(_) =>
        Future.successful(Results.Unauthorized("Invalid secret"))
      case None =>
        Future.successful(Results.Unauthorized("No secret provided"))
    }
  }

  private def timingSafeEqual(a: String, b: String): Boolean = {
    val aBytes = MessageDigest.getInstance("SHA-256").digest(a.getBytes)
    val bBytes = MessageDigest.getInstance("SHA-256").digest(b.getBytes)
    MessageDigest.isEqual(aBytes, bBytes)
  }
}

// Hook into your controller
@Singleton
class MyController @Inject()(cc: ControllerComponents, validateSharedSecret: ValidateSharedSecret) extends AbstractController(cc) {

  def protectedRoute: Action[AnyContent] = validateSharedSecret {
    Ok("Access granted")
  }

}
```

## Example Request

```bash
curl -X GET \
  'https://your-api.zuplo.dev/your-route' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

## Learn More

- [API Key Authentication on Zuplo](https://zuplo.com/docs/policies/api-key-auth-inbound)
- [JWT Authentication on Zuplo](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound)
- [All use cases](https://zuplo.com/use-cases)
