DEV Community

ben
ben

Posted on

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

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

II. Overall Architecture Design

In PicoServer, we design the following architecture:

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

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
Enter fullscreen mode Exit fullscreen mode

IV. Implement the Login Interface

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

Request body:

{
  "username":"admin",
  "password":"123456"
}
Enter fullscreen mode Exit fullscreen mode

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; }
    }
}
Enter fullscreen mode Exit fullscreen mode

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

Session Data Structure:

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

    public string? Role { get; set; }

    public DateTime ExpireTime { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Example:

User
 ├─ admin
 └─ operator

Role
 ├─ admin
 └─ viewer

Permission
 ├─ product.view
 ├─ product.edit
 └─ order.manage
Enter fullscreen mode Exit fullscreen mode

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()
{
}
Enter fullscreen mode Exit fullscreen mode

Attribute Definition:

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

    public RequirePermissionAttribute(string permission)
    {
        Permission = permission;
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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); 
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)