---
title: Enforce Authorization At Trusted Service Layer
impact: CRITICAL
impactDescription: prevents unauthorized access to protected resources and data
tags: authorization, rbac, abac, security, access-control, kotlin
---

## Enforce Authorization At Trusted Service Layer

Client-side checks are purely for UI/UX and can be trivially bypassed. Every request reaching the server must be authenticated and authorized against the required permissions in a trusted environment.

**Incorrect (trusting client state):**

```kotlin
// Relying on a hidden field or value sent from client
@PostMapping("/user/update")
fun update(@RequestBody req: UpdateRequest): String {
    // req.isAdmin is provided by the client's form!
    if (req.isAdmin) {
        db.updatePermissions(req.userId)
    }
    return "ok"
}

// Insecure check in a Controller without proper security context
@DeleteMapping("/post/{id}")
fun delete(@PathVariable id: String) {
    // No check if 'current user' owns this 'post'
    postService.delete(id)
}
```

**Correct (server-side authorization):**

```kotlin
// Using Spring Security with Method Security
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/users/{id}")
fun deleteUser(@PathVariable id: String) {
    userService.delete(id)
}

// Data-level authorization (checking ownership)
@PutMapping("/posts/{id}")
fun updatePost(@PathVariable id: String, @RequestBody data: PostData) {
    val post = postService.findById(id) ?: throw NotFoundException()
    val currentUser = SecurityContextHolder.getContext().authentication.name
    
    // Check ownership at the server layer
    if (post.author != currentUser && !isAdmin(currentUser)) {
        throw AccessDeniedException("You do not own this post")
    }
    
    postService.update(id, data)
}

// Ktor Authentication/Authorization
route("/admin") {
    install(Authorization) {
        check {
            val user = call.principal<UserPrincipal>()
            user?.roles?.contains("ADMIN") ?: false
        }
    }
    get("/dashboard") { /* handle admin request */ }
}
```

**Never trust for Authorization:**
- Client-side Boolean flags (e.g., `user.isAdmin`).
- Parameters like `?role=admin` in the URL.
- Unsigned or unvalidated JWT claims from local storage.
- The existence of a "secret" URL (Security by Obscurity).

**Tools:** Spring Security, Ktor Auth, Shiro, OPA (Open Policy Agent), Manual Security Review
