Overview
Overview
Molecule provides declarative, compile-time validated authorization integrated directly into your domain model. Define authorization rules once in the domain structure definition and they're automatically enforced at runtime.
The 4 authorization layers
Authorization in Molecule is built on 4 distinct layers that work together:
- Roles - Which roles have access to entities
- Action Grants - Action grants at entity level
- Attribute Restrictions - Attribute role restrictions
- Attribute Updates - Attribute update grants
These layers build on each other, providing progressively finer control.
Note: Raw SQL actions (rawQuery, rawTransact) operate outside these 4 layers and only check role-level permissions.
Core principles
- Public entities (no roles) are unrestricted - Entities with no roles can be accessed by anyone with all 5 actions (query, save, insert, update, delete) without authentication
- Roles define baseline capabilities via action traits (Layer 1)
- Action grants ADD capabilities to specific roles (Layer 2)
- Attribute restrictions NARROW access from entity baseline (Layer 3)
- Attribute update grants ADD capabilities to specific roles for specific fields (Layer 4)
- All 5 actions must be available - For role-restricted entities, combined roles and grants must provide access to all 5 actions
- User authenticates as ONE role at a time (for role-restricted entities)
- No magic roles - all roles follow same rules
- One entity = one database table (no entity variations)
Action traits
Core Actions (CRUD)
Scala marker traits are used to define actions that a role can perform on an entity:
trait query extends Action // Read data via queries
trait save extends Action // Create single entity
trait insert extends Action // Batch insert multiple entities
trait update extends Action // Modify existing data
trait delete extends Action // Remove dataRaw SQL Actions (Advanced)
trait rawQuery extends Action // Raw SQL SELECT queries (read-only)
trait rawTransact extends Action // Raw SQL mutations (dangerous!)The raw SQL actions provide fallback methods for advanced use cases not covered by Molecule's query API. They operate outside the 4-layer authorization model and should be granted sparingly.
Role definition
Roles are defined by extending Role and its allowed actions:
trait RoleName extends Role with action1 with action2 ...Example:
Define some roles in your domain structure definition and let entities extend their allowed roles:
import molecule.DomainStructure
object MyDomain extends DomainStructure {
trait Guest extends Role with query
trait Member extends Role with query with save
trait Moderator extends Role with query with save with insert with update with delete
trait Admin extends Role with query with save with insert with update with delete
trait Post extends Member with Admin {
val content = oneString
}
// More entity definitions ...
}Limit: Up to 32 different roles per domain.
Entity-level authorization
Public entities
Entities that don't extend any roles are public:
trait Article {
val title = oneString
}- Anyone can access without authentication
- All 5 actions available (query, save, insert, update, delete)
Layer 1: Role-restricted entities
trait Post extends Member with Admin { // ADD roles to entity
val content = oneString
}- Only Member and Admin can access
- Each role uses their own action access
Layer 2: With action grants
trait Post extends Member with Admin
with updating[Member] { // ADD update capability to Member
val content = oneString
}- Member gets update capability added
- Admin unaffected (already has update)
Attribute-level authorization
Layer 3: Restrictions
Allow an attribute to be accessed by specific roles:
val attr = oneType // All entity roles (baseline)
val attr = oneType.only[Role] // ONLY these roles (replacement)
val attr = oneType.exclude[Role] // All EXCEPT these roles (subtraction)Layer 4: Update grants
Allow an attribute to be updated by specific roles (when they lack update capability):
val attr = oneType.updating[Role] // ADD update capability to this roleCompile-time validation
When boilerplate code is generated from your domain structure definition (with sbt moleculeGen), Molecule validates the authorization rules at compile time:
- All 5 actions must be available for role-restricted entities
- At least one role can query
- At least one role can save
- Action grants reference roles in entity baseline
- Action grants compatible with attribute restrictions
- Attribute
.only/.excludereference roles in entity baseline - Attribute
.updatingreferences roles in entity baseline
Runtime usage
Authentication
User authentication (validating credentials, managing sessions) is handled by your application. Molecule only requires you to provide the authenticated user's ID and role:
// After your app authenticates the user (password, OAuth, etc.)
// provide userId and role to Molecule
implicit val conn = baseConn.withAuth(userId, "Member")Once authenticated, the connection enforces all authorization rules automatically.
Automatic Authorization
// All operations check authorization automatically in .transact
Post.title.query.get // ✓ or ✗ based on role
Post.title("Hi").save.transact // ✓ or ✗ based on role
Post(id).title("New").update.transact // ✓ or ✗ based on role
Post(id).delete.transact // ✓ or ✗ based on roleError Messages
When authorization fails:
Post(id).title("New").update.transact
// ModelError: Access denied: Role 'Member' cannot update attribute 'Post.title'
Post(id).delete.transact
// ModelError: Access denied: Role 'Member' cannot delete entity 'Post'Advantages
- Compile-time safety - catch errors at build time
- Zero boilerplate - no middleware, decorators, or resolver code
- Centralized - all authorization in domain definition
- Flexible - additive grants allow fine-grained control
- Natural syntax - reads like English
- Type-safe - full Scala type system backing
- Fast runtime checks - uses highly optimized bit masking for minimal overhead (no string comparisons)
