Implementing Authorization
1. Implementing Role-Based Access Control
Example: Role check in interceptor
methodRoles := map[string][]string{
"/user.v1.UserService/DeleteUser": {"admin"},
}
func RBAC(ctx context.Context, req any, info *grpc.UnaryServerInfo,
h grpc.UnaryHandler) (any, error) {
claims := ctx.Value(claimsKey{}).(jwt.MapClaims)
role, _ := claims["role"].(string)
if need, ok := methodRoles[info.FullMethod]; ok && !slices.Contains(need, role) {
return nil, status.Error(codes.PermissionDenied, "forbidden")
}
return h(ctx, req)
}
| Field | Detail |
|---|---|
| Source of roles | JWT claim, DB lookup, IDP |
| Method → role map | Static config or annotation-driven |
2. Implementing Attribute-Based Access Control
| Attribute | Source |
|---|---|
| Subject | JWT claims (user id, dept, clearance) |
| Resource | Request fields (owner_id, tenant_id) |
| Action | Method name |
| Environment | Time, IP, region |
3. Creating Authorization Interceptor
| Pattern | Detail |
|---|---|
| Annotation-driven | Read custom method options at startup, build policy map |
| Policy engine | OPA / Cedar evaluation per call |
| Cache decisions | Key on (subject, method, resource) |
4. Defining Permission Policies
Example: OPA Rego policy
package grpc.authz
default allow := false
allow if {
input.method == "/order.v1.OrderService/GetOrder"
input.claims.sub == input.request.user_id
}
5. Using Open Policy Agent
| Mode | Detail |
|---|---|
| Sidecar | OPA running as HTTP service; query per RPC |
| Embedded (Go SDK) | Eval Rego in-process |
| Envoy ext_authz | OPA-Envoy plugin for service mesh |
6. Implementing Method-Level Authorization
| Mechanism | Detail |
|---|---|
| Custom proto option | option (auth.require) = "orders.read"; |
| Read at runtime | Via proto reflection |
| Default-deny | Missing annotation → deny |
7. Handling Authorization Errors
| Scenario | Status |
|---|---|
| Missing token | Unauthenticated (16) |
| Invalid token | Unauthenticated (16) |
| Insufficient privilege | PermissionDenied (7) |
| Resource not visible | NotFound (5) to avoid leaking existence |
8. Using Claims from JWT
Example: Read claims from ctx
claims := ctx.Value(claimsKey{}).(jwt.MapClaims)
sub := claims["sub"].(string)
scopes := strings.Fields(claims["scope"].(string))
9. Implementing Resource-Based Access
| Pattern | Detail |
|---|---|
| Ownership check | Compare claim.sub to resource.owner_id |
| Tenant isolation | Compare tenant_id from claim & request |
| ACL lookup | Per-resource permissions in DB / Zanzibar-style |
10. Auditing Authorization Decisions
| Field | Log |
|---|---|
| subject | User id / service account |
| method | Full RPC name |
| resource | Target id |
| decision | allow / deny + reason |
| trace_id | Correlate with request |