Skip to main content
Early Stage — cedar4s is under active development. APIs may change between releases. We'd love your feedback!
cedar4s logo

cedar4s

Type-safe Cedar authorization for Scala

1. Define Authorization Schema

Start by defining your authorization model in Cedar, a policy language created by AWS for fine-grained access control.

Cedar separates policy logic from application code, making authorization rules auditable and easy to understand.

Define entities (users, resources), actions, and their relationships. cedar4s generates type-safe Scala code from this schema.

schema.cedarschema
namespace DocShare;

entity User {}

entity Folder {}

entity Document in [Folder] {}

action "Document::Read" appliesTo {
  principal: [User],
  resource: Document
};

action "Document::Write" appliesTo {
  principal: [User],
  resource: Document
};

2. Implement Entity Fetchers

cedar4s generates Scala traits and case classes from your Cedar schema. Implement EntityFetcher to load entities from your database.

The fetchBatch method enables efficient bulk loading, reducing N+1 queries when authorizing multiple resources.

Built-in caching with Caffeine minimizes database hits for frequently accessed entities.

DocumentFetcher.scala
class DocumentFetcher(db: Database)
    extends EntityFetcher[IO, Entities.Document, String] {

  def fetch(id: String): IO[Option[Entities.Document]] =
    db.findDocument(id).map(_.map { doc =>
      Entities.Document(id = doc.id, folderId = doc.folderId)
    })

  override def fetchBatch(ids: Set[String]) =
    db.findDocuments(ids).map(_.map(d => d.id -> toCedar(d)).toMap)
}

3. Authorize with Type Safety

Use the generated request types to perform authorization checks. No string-based action names or resource IDs that can drift from your schema.

Batch operations like filterAllowed efficiently authorize multiple resources in a single pass.

Works with any effect type: Future, cats-effect IO,ZIO, or your own.

Authorization.scala
import myapp.cedar.MyApp

val runtime = CedarRuntime[IO](engine, store, CedarRuntime.resolverFrom(buildPrincipal))

given session: CedarSession[IO] = runtime.session(currentUser)

// Type-safe authorization check
MyApp.Document.Read(folderId, documentId).require

// Check without throwing
val canRead: IO[Boolean] = MyApp.Document.Read(folderId, documentId).isAllowed

// Batch filter - only returns allowed documents
val allowed: IO[Seq[Document]] = session.filterAllowed(documents) { doc =>
  MyApp.Document.Read(doc.folderId, doc.id)
}