Authorization
GrydAuth provides a flexible authorization system combining Role-Based Access Control (RBAC) and Permission-Based Access Control (PBAC) with support for custom policies.
Authorization Models
Role-Based Access Control (RBAC)
Roles group users with similar access requirements:
csharp
[Authorize(Roles = "Admin")]
public IActionResult AdminOnly() { ... }
[Authorize(Roles = "Admin,Manager")]
public IActionResult AdminOrManager() { ... }1
2
3
4
5
2
3
4
5
Permission-Based Access Control (PBAC)
Fine-grained permissions for specific actions:
csharp
[RequirePermission("create:products")]
public IActionResult CreateProduct() { ... }
[RequirePermission("read:products", "update:products")]
public IActionResult ManageProducts() { ... } // Requires ALL permissions1
2
3
4
5
2
3
4
5
Permission Format
GrydAuth uses the convention {action}:{entity}:
| Permission | Description |
|---|---|
create:products | Can create products |
read:products | Can view products |
update:products | Can update products |
delete:products | Can delete products |
manage:users | Can manage users |
admin:system | System administration |
Role Management
Built-in Roles
GrydAuth comes with predefined roles:
| Role | Description |
|---|---|
SuperAdmin | Full system access |
Admin | Administrative access |
User | Standard user access |
Guest | Limited read-only access |
Creating Roles
http
POST /api/roles
Authorization: Bearer {admin-token}
Content-Type: application/json
{
"name": "Manager",
"description": "Department manager with elevated permissions",
"permissions": [
"read:products",
"create:products",
"update:products",
"read:users",
"create:reports"
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Assigning Roles to Users
http
POST /api/users/{userId}/roles
Authorization: Bearer {admin-token}
Content-Type: application/json
{
"roleNames": ["Manager", "User"]
}1
2
3
4
5
6
7
2
3
4
5
6
7
Programmatic Role Management
csharp
public class RoleService
{
private readonly IRoleRepository _roleRepository;
private readonly IUserRepository _userRepository;
public async Task AssignRoleAsync(Guid userId, string roleName)
{
var user = await _userRepository.GetByIdAsync(userId);
var role = await _roleRepository.GetByNameAsync(roleName);
user.AssignRole(role);
await _userRepository.UpdateAsync(user);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Permission Management
Creating Permissions
http
POST /api/permissions
Authorization: Bearer {admin-token}
Content-Type: application/json
{
"name": "export:reports",
"description": "Can export reports to PDF/Excel",
"category": "Reports"
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Auto-Generated Permissions
Use [AutoPermission] attribute to automatically generate permissions for your controllers:
csharp
using Gryd.API.Attributes;
[ApiController]
[Route("api/[controller]")]
[AutoPermission("products")] // Generates: create:products, read:products, etc.
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult GetAll() { ... } // read:products
[HttpPost]
public IActionResult Create() { ... } // create:products
[HttpPut("{id}")]
public IActionResult Update() { ... } // update:products
[HttpDelete("{id}")]
public IActionResult Delete() { ... } // delete:products
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Permission Generation Service
Generate all permissions for seeding:
csharp
using GrydCrud.Auth.Abstractions;
public class PermissionSeeder
{
private readonly ICrudPermissionGenerator _permissionGenerator;
private readonly IPermissionRepository _permissionRepository;
public async Task SeedPermissionsAsync()
{
// Scan all controllers for [AutoPermission] attributes
var permissions = _permissionGenerator.GeneratePermissions(
typeof(ProductsController).Assembly,
typeof(UsersController).Assembly
);
foreach (var permission in permissions)
{
await _permissionRepository.CreateIfNotExistsAsync(permission);
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Authorization Attributes
[Authorize]
Standard ASP.NET Core authorization:
csharp
[Authorize] // Any authenticated user
public class SecureController : ControllerBase { }
[Authorize(Roles = "Admin")]
public class AdminController : ControllerBase { }1
2
3
4
5
2
3
4
5
[RequirePermission]
GrydAuth permission-based authorization:
csharp
// Single permission
[RequirePermission("read:products")]
public IActionResult GetProducts() { ... }
// Multiple permissions (ALL required)
[RequirePermission("read:products", "export:products")]
public IActionResult ExportProducts() { ... }1
2
3
4
5
6
7
2
3
4
5
6
7
[AllowAnonymous]
Skip authorization:
csharp
[AllowAnonymous]
public IActionResult PublicEndpoint() { ... }1
2
2
Policy-Based Authorization
Defining Policies
csharp
builder.Services.AddAuthorization(options =>
{
// Simple permission policy
options.AddPolicy("CanReadProducts", policy =>
policy.RequirePermission("read:products"));
// Multiple permissions (ALL required)
options.AddPolicy("CanManageProducts", policy =>
policy.RequirePermission("create:products", "update:products", "delete:products"));
// Role-based policy
options.AddPolicy("AdminOnly", policy =>
policy.RequireRole("Admin", "SuperAdmin"));
// Combined policy
options.AddPolicy("ProductManager", policy =>
policy.RequireRole("Manager")
.RequirePermission("read:products", "update:products"));
// Custom requirement
options.AddPolicy("MinimumAge", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(18)));
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Using Policies
csharp
[Authorize(Policy = "CanManageProducts")]
public IActionResult ManageProducts() { ... }1
2
2
Custom Authorization Handlers
csharp
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public int MinimumAge { get; }
public MinimumAgeRequirement(int minimumAge) => MinimumAge = minimumAge;
}
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinimumAgeRequirement requirement)
{
var dateOfBirthClaim = context.User.FindFirst(c => c.Type == "date_of_birth");
if (dateOfBirthClaim != null)
{
var dateOfBirth = DateTime.Parse(dateOfBirthClaim.Value);
var age = DateTime.Today.Year - dateOfBirth.Year;
if (age >= requirement.MinimumAge)
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
// Register handler
builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Checking Permissions in Code
Using IAuthorizationService
csharp
public class ProductService
{
private readonly IAuthorizationService _authorizationService;
private readonly IHttpContextAccessor _httpContextAccessor;
public async Task<Result> DeleteProductAsync(Guid productId)
{
var user = _httpContextAccessor.HttpContext!.User;
var authResult = await _authorizationService.AuthorizeAsync(
user,
null,
new PermissionRequirement("delete:products"));
if (!authResult.Succeeded)
{
return Result.Failure("ACCESS_DENIED", "You don't have permission to delete products");
}
// Proceed with deletion...
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Using ICurrentUserService
csharp
public class ProductService
{
private readonly ICurrentUserService _currentUser;
public bool CanUserEditProduct(Product product)
{
// Check if user has update permission
if (_currentUser.HasPermission("update:products"))
return true;
// Or is the owner
if (product.CreatedBy == _currentUser.UserId)
return true;
return false;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ICurrentUserService Methods
| Method | Description |
|---|---|
HasPermission(string) | Check single permission |
HasAnyPermission(params string[]) | Check if user has any of the permissions |
HasAllPermissions(params string[]) | Check if user has all permissions |
IsInRole(string) | Check if user is in role |
IsInAnyRole(params string[]) | Check if user is in any of the roles |
Resource-Based Authorization
For complex scenarios where authorization depends on the resource:
csharp
public class DocumentAuthorizationHandler :
AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Document resource)
{
var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (requirement.Name == Operations.Read.Name)
{
// Anyone in the same department can read
if (resource.DepartmentId == GetUserDepartment(context.User))
{
context.Succeed(requirement);
}
}
else if (requirement.Name == Operations.Update.Name)
{
// Only owner or admin can update
if (resource.OwnerId.ToString() == userId ||
context.User.IsInRole("Admin"))
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
// Usage
var document = await _documentRepository.GetByIdAsync(id);
var authResult = await _authorizationService.AuthorizeAsync(
User, document, Operations.Update);
if (!authResult.Succeeded)
return Forbid();1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Hierarchical Permissions
Implement permission hierarchies:
csharp
public class HierarchicalPermissionHandler : AuthorizationHandler<PermissionRequirement>
{
private readonly Dictionary<string, string[]> _hierarchies = new()
{
["admin:*"] = new[] { "create:*", "read:*", "update:*", "delete:*" },
["manage:products"] = new[] { "create:products", "read:products", "update:products" },
};
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
PermissionRequirement requirement)
{
var userPermissions = context.User.Claims
.Where(c => c.Type == "permissions")
.Select(c => c.Value)
.ToHashSet();
// Check direct permission
if (userPermissions.Contains(requirement.Permission))
{
context.Succeed(requirement);
return Task.CompletedTask;
}
// Check hierarchical permissions
foreach (var (parent, children) in _hierarchies)
{
if (userPermissions.Contains(parent) &&
children.Contains(requirement.Permission))
{
context.Succeed(requirement);
break;
}
}
return Task.CompletedTask;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Best Practices
- Principle of Least Privilege - Grant minimum permissions needed
- Use Permissions over Roles - Roles group permissions, but check permissions
- Avoid Hardcoded Roles - Use configuration or database
- Audit Permission Changes - Log all authorization changes
- Regular Permission Review - Periodically review user permissions
- Use Resource-Based Auth - For complex scenarios, check the resource itself