VOOZH about

URL: https://dev.to/ben23/practical-combat-of-maui-embedded-web-architecture-6-picoserver-web-admin-permission-system-3m72

⇱ Practical Combat of MAUI Embedded Web Architecture (6) PicoServer Web Admin Permission System Design and Authentication Architecture - DEV Community


PicoServer
Source Code URL:
https://github.com/densen2014/MauiPicoAdmin

In the previous articles, we have gradually built a complete PicoServer local Web Admin system, including:

  • Routing mechanism
  • REST API architecture
  • Static file hosting
  • Web Admin management backend

Up to this point, we already have the following architecture:

Web UI
 ↓
REST API
 ↓
Business Service

However, for any real-world system, there is still one most critical component missing:
Authentication and Authorization System (Auth System)

Without a permission system:

  • Any user can access the management backend
  • All APIs can be called directly
  • The entire system has no security boundaries

Therefore, in this article, we will design a:
complete authentication and authorization architecture suitable for PicoServer

And implement the following features:

  • Login authentication
  • Token mechanism
  • API permission protection
  • Role-based permission model
  • API filter
  • Permission attribute mechanism

Ultimately, we will build a scalable permission system framework.

I. Permission System Architecture Design

In web systems, a permission system usually consists of three layers:

  1. Authentication Identity verification
  2. Authorization Permission control
  3. Permission Model Permission modeling

The relationship between these layers is as follows:

User Login
 ↓
Get Token
 ↓
API Requests Carry Token
 ↓
Server Validates Token
 ↓
Check User Permissions
 ↓
Allow / Deny Access

II. Overall Architecture Design

In PicoServer, we design the following architecture:

 Web Admin UI
 │
 │ fetch API
 ▼
 ┌───────────────────┐
 │ PicoServer │
 │ │
 │ Auth Middleware │
 │ │ │
 │ ▼ │
 │ Permission Filter │
 │ │ │
 │ ▼ │
 │ Controller │
 │ │ │
 │ ▼ │
 │ Service Layer │
 └───────────────────┘

Core Idea:
All API requests pass through the authentication middleware first.

III. Token Authentication Mechanism

In a local Web Admin system, Token-based authentication is recommended for the following reasons:

  • Does not rely on browser sessions
  • Suitable for REST APIs
  • Simple to call from the frontend
  • Low implementation cost

The authentication process is as follows:

User Login
 ↓
Server Generates Token
 ↓
Frontend Stores Token
 ↓
Token is Carried in Each API Request
 ↓
Server Validates Token

IV. Implement the Login Interface

First, implement the login API:
POST /api/login

Request body:

{"username":"admin","password":"123456"}

Login Interface Implementation

public class LoginController
{
 public async Task Login(HttpListenerRequest request, HttpListenerResponse response)
 {
 var body = await new StreamReader(request.InputStream).ReadToEndAsync();

 var login = JsonSerializer.Deserialize<LoginRequest>(body, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

 if (login != null && login.Username == "admin" && login.Password == "123456")
 {
 var token = TokenService.GenerateToken(login.Username);

 await response.WriteJsonAsync(new
 {
 success = true,
 token = token
 });
 }
 else
 {
 response.StatusCode = 401;

 await response.WriteJsonAsync(new
 {
 success = false,
 message = "Invalid username or password"
 });
 }
 }

 public class LoginRequest
 {
 public string? Username { get; set; }
 public string? Password { get; set; }
 }
}

HttpHelper Tool Correction (from Article 3)
The previous version had a bug where the contentType was not set correctly. The corrected version is as follows, with the method name changed from WriteJson to WriteJsonAsync:

public static class HttpHelper
{
 public static async Task WriteJsonAsync(HttpListenerResponse response, object obj)
 {
 string json = JsonSerializer.Serialize(obj);
 await response.WriteAsync(json, contentType: "application/json");
 }
}

V. Token Service Design

To manage tokens, we create a TokenService:

public class TokenService
{
 static Dictionary<string, UserSession> sessions = new();

 public static string GenerateToken(string username)
 {
 var token = Guid.NewGuid().ToString();

 sessions[token] = new UserSession
 {
 Username = username,
 Role = "admin",
 ExpireTime = DateTime.Now.AddHours(12)
 };

 return token;
 }

 public static UserSession Validate(string token)
 {
 if(!sessions.ContainsKey(token))
 return null;

 var session = sessions[token];

 if(session.ExpireTime < DateTime.Now)
 return null;

 return session;
 }
}

Session Data Structure:

public class UserSession
{
 public string? Username { get; set; }

 public string? Role { get; set; }

 public DateTime ExpireTime { get; set; }
}

VI. Authentication Middleware (Auth Middleware)

To avoid manually validating tokens in every API, we design a unified authentication middleware.

Request flow:

All APIs
 ↓
Auth Middleware
 ↓
Controller

Implementation:

public class AuthMiddleware
{
 public static UserSession? Authenticate(HttpListenerRequest request)
 {
 var token = request.Headers["Authorization"];

 if (string.IsNullOrEmpty(token))
 return null;

 return TokenService.Validate(token);
 }
}

VII. API Filter Mechanism

Furthermore, we can add an API Filter with the following functions:

  • Automatically intercept unauthorized access
  • Return unified error responses

Example implementation:

public static async Task<bool> RequireAuth(
 HttpListenerRequest request,
 HttpListenerResponse response)
{
 var session = AuthMiddleware.Authenticate(request);

 if(session == null)
 {
 response.StatusCode = 401;

 await response.WriteJsonAsync(new
 {
 message = "Unauthorized"
 });

 return false;
 }

 return true;
}

VIII. Protect API Interfaces

Now APIs can be written like this:

public async Task GetUserList(HttpListenerRequest request, HttpListenerResponse response)
{
 if(!await RequireAuth(request,response))
 return;

 var users = new[]
 {
 new { id = 1, name = "admin" },
 new { id = 2, name = "operator" }
 };

 await response.WriteJsonAsync(users);
}

This way, all APIs are automatically protected.

IX. Permission Model Design

As the system becomes more complex, a role-based permission model needs to be introduced.

Classic permission model hierarchy:

User
 ↓
Role
 ↓
Permission

Example:

User
 ├─ admin
 └─ operator

Role
 ├─ admin
 └─ viewer

Permission
 ├─ product.view
 ├─ product.edit
 └─ order.manage

APIs can be associated with specific permissions:

  • /api/product/list → product.view
  • /api/product/create → product.edit
  • /api/order/delete → order.manage

The server determines access rights based on the user's role.

X. Permission Attribute Design (Advanced Architecture)

A more advanced design is to use permission attributes (Attribute):

Example:

[RequirePermission("product.view")]
public async Task ProductList()
{
}

Attribute Definition:

public class RequirePermissionAttribute : Attribute
{
 public string Permission { get; }

 public RequirePermissionAttribute(string permission)
 {
 Permission = permission;
 }
}

Before API execution:

  1. Read the attribute
  2. Check permissions
  3. Automatically intercept unauthorized requests

This is the mechanism used in many web frameworks, such as:

  • ASP.NET Core
  • Spring Boot
  • NestJS

XI. Complete Permission System Architecture

The final system architecture is as follows:

MAUI App
 │
 ├── WebView
 │
 └── PicoServer
 │
 ├── Static File Server
 │
 ├── REST API
 │
 ├── Auth Middleware
 │
 ├── Permission Filter
 │
 └── Service Layer

Advantages:

  • Serverless deployment
  • Local operation
  • High performance
  • Cross-platform compatibility

XII. Additional Code

Due to space limitations, some code is not fully displayed. Please refer to the synchronized source code in the project: https://github.com/densen2014/MauiPicoAdmin

Final Route Registration:

private void RegisterRoutes()
{
 var demo = new DemoController();
 var product = new ProductController();
 var login = new LoginController();
 var user = new UserController();
 api.AddStaticFiles("/", wwwrootPath);
 api.AddRoute("/api/hello", demo.Hello);
 api.AddRoute("/api/time", demo.GetTime);
 api.AddRoute("/api/status", demo.GetStatus);
 api.AddRoute("/api/product/list", product.List);
 api.AddRoute("/api/product/detail", product.Detail); 
 api.AddRoute("/api/login", login.Login); 
 api.AddRoute("/api/user/list", user.GetUserList); 
}

Add Test File: api_test.http
Relevant documentation: https://aka.ms/vs/httpfile

# For more info on HTTP files go to https://aka.ms/vs/httpfile

@hostname=127.0.0.1

### Hello
GET http://{{hostname}}:8090/api/hello

### Time
GET http://{{hostname}}:8090/api/time

### Status
GET http://{{hostname}}:8090/api/status

### Product List
GET http://{{hostname}}:8090/api/product/list

### Product Detail
GET http://{{hostname}}:8090/api/product/detail?id=1

### Login
### Automatically extract response content from the previous request through variables and scripts, and reuse it in the next request
# @name login
POST http://{{hostname}}:8090/api/login
Content-Type: application/json

{
 "username": "admin",
 "password": "123456"
} 

### User List (with token)
GET http://{{hostname}}:8090/api/user/list
Authorization: {{login.response.body.$.token}}

# Response

XIII. Summary of This Article

In this article, we have built a complete PicoServer permission system architecture, implementing the following features:

  • Login authentication API
  • Token mechanism
  • API permission protection
  • Auth Middleware
  • API Filter
  • Role-based permission model
  • Permission attribute architecture

At this point, our system has been equipped with:
enterprise-level Web Admin infrastructure

Next Article Preview

Next, we will continue to upgrade the system capabilities with:

Practical Combat of MAUI Embedded Web Architecture (7)
Local Database and Data Persistence

We will implement:

  • SQLite database
  • ORM data access
  • Product CRUD APIs
  • Admin data management
  • Local data synchronization

Ultimately, we will build:
a fully usable local management system