# Forward Authentication (前置认证) 方案设计

## 概述

本文档描述 liner 引入 **Forward Authentication（前置认证）** 模式的技术设计方案。该模式将认证逻辑从业务服务中完全剥离，由独立的认证服务统一处理 OAuth/OIDC 流程，实现认证与业务的解耦。

### 核心理念

Forward Authentication 的核心思想是：**业务服务完全不处理登录逻辑**，甚至不需要引入任何 OAuth/OIDC 库。所有的认证工作（跳转 IdP 登录、验证 Token、种 Cookie、刷新 Token）都交给一个独立的"旁路服务"处理。

在 Go 生态和云原生领域，实现这一模式的事实标准且最轻量的方案是 **OAuth2 Proxy**。

### 设计目标

| 目标 | 说明 |
|------|------|
| **最小侵入** | 现有代码无需大改，仅通过配置和请求头传递用户信息 |
| **轻量高效** | 零依赖增加，复用现有 HTTP 处理链路 |
| **省内存** | 不在 liner 内部维护 Session 状态，依赖外部认证服务 |
| **标准兼容** | 兼容 OAuth2 Proxy、Authelia、Authentik 等主流方案 |
| **命名引用** | 类似 Traefik 中间件，通过名称引用认证配置，避免重复定义 |

---

## 总体架构

### 认证流程图

```mermaid
sequenceDiagram
    autonumber
    participant Client as 客户端
    participant Liner as Liner<br/>(反向代理)
    participant AuthSvc as OAuth2 Proxy<br/>(认证服务)
    participant IdP as 身份提供商<br/>(Google/OIDC)
    participant Backend as 后端服务

    Client->>Liner: 1. 请求受保护资源
    Liner->>AuthSvc: 2. 前置认证请求<br/>GET /oauth2/auth

    alt 未认证
        AuthSvc-->>Liner: 3a. 401 Unauthorized
        Liner-->>Client: 4a. 302 重定向到登录
        Client->>AuthSvc: 5a. 开始 OAuth 流程
        AuthSvc->>IdP: 6a. 重定向到 IdP
        IdP-->>AuthSvc: 7a. 回调 + 授权码
        AuthSvc-->>Client: 8a. 种 Cookie + 重定向回原地址
        Client->>Liner: 9a. 携带 Cookie 重试请求
        Liner->>AuthSvc: 10a. 再次前置认证
    end

    AuthSvc-->>Liner: 3b. 200 OK + 用户信息头
    Note over Liner: X-Auth-User: alice<br/>X-Auth-Email: alice@example.com
    Liner->>Backend: 4b. 转发请求 + 用户信息头
    Backend-->>Liner: 5b. 响应
    Liner-->>Client: 6b. 返回响应
```

### 系统架构图

```mermaid
graph TB
    subgraph "入口层 - Liner"
        Forward["HTTPForwardHandler<br/>(正向代理)"]
        Web["HTTPWebHandler"]
        WebProxy["HTTPWebProxyHandler<br/>(反向代理)"]
        WebShell["HTTPWebShellHandler<br/>(终端)"]
        Tunnel["HTTPTunnelHandler<br/>(隧道)"]
    end

    subgraph "认证层"
        AuthConfig["顶级 Auth 配置"]
        GoogleAuth["google-oauth<br/>(ForwardAuth)"]
        InternalAuth["internal-auth<br/>(ForwardAuth)"]
        OAuth2Proxy["OAuth2 Proxy"]
        Redis["Redis/Session"]
    end

    subgraph "身份提供商"
        Google["Google OAuth"]
        OIDC["OIDC Provider"]
    end

    subgraph "后端服务"
        API["API 服务"]
        AdminPanel["管理面板"]
    end

    Forward -->|forward_auth: google-oauth| AuthConfig
    WebProxy -->|forward_auth: google-oauth| AuthConfig
    WebShell -->|forward_auth: internal-auth| AuthConfig
    Tunnel -->|forward_auth: internal-auth| AuthConfig

    AuthConfig --> GoogleAuth
    AuthConfig --> InternalAuth
    GoogleAuth --> OAuth2Proxy
    InternalAuth --> OAuth2Proxy
    OAuth2Proxy --> Redis
    OAuth2Proxy --> Google
    OAuth2Proxy --> OIDC

    Forward --> API
    WebProxy --> API
    WebShell --> AdminPanel

    style AuthConfig fill:#4CAF50,color:#fff
    style GoogleAuth fill:#2196F3,color:#fff
    style InternalAuth fill:#2196F3,color:#fff
```

---

## 详细设计

### 1. 配置模型设计

采用 **顶级 Auth 配置 + 命名引用** 模式（类似 Traefik 的 middleware 设计）：

#### 1.1 配置结构定义

```go
// config.go

// 顶级 Auth 配置
type AuthConfig struct {
    ForwardAuth map[string]ForwardAuthConfig `json:"forward_auth" yaml:"forward_auth"`
}

// ForwardAuth 配置结构
type ForwardAuthConfig struct {
    Address     string            `json:"address" yaml:"address"`           // 认证端点 URL
    TrustForwardHeader bool       `json:"trust_forward_header" yaml:"trust_forward_header"`
    AuthResponseHeaders []string  `json:"auth_response_headers" yaml:"auth_response_headers"`
    AuthRequestHeaders  []string  `json:"auth_request_headers" yaml:"auth_request_headers"`
}

// 修改 Config 结构体，添加顶级 Auth
type Config struct {
    // ... 现有字段
    Auth AuthConfig `json:"auth" yaml:"auth"`  // 新增
    // ...
}
```

#### 1.2 YAML 配置示例

```yaml
# 顶级 Auth 配置 - 定义所有认证方式
auth:
  forward_auth:
    # 1. Google OAuth 认证 - 用于公开服务
    google-oauth:
      address: "http://oauth2-proxy:4180/oauth2/auth"
      trust_forward_header: true
      auth_response_headers:
        - "X-Auth-Request-User"
        - "X-Auth-Request-Email"
        - "X-Auth-Request-Groups"
        - "X-Auth-Request-Access-Token"
      auth_request_headers:
        - "Cookie"
        - "Authorization"

    # 2. 内部认证 - 用于管理服务
    internal-auth:
      address: "http://authn-server:9000/verify"
      trust_forward_header: true
      auth_response_headers:
        - "X-User-Id"
        - "X-User-Role"
        - "X-User-Permissions"
```

### 2. 多场景应用

#### 2.1 场景一：HTTP 正向代理 (HTTPForwardHandler)

```yaml
http:
  - listen:
      - "0.0.0.0:8080"
    forward:
      # 引用顶级 auth 配置
      forward_auth: "google-oauth"
      policy: |
        {{ if .User.Username }}direct{{ else }}require_auth{{ end }}
      dialer: "local"
```

**适用场景**：企业出口代理，需要验证员工身份后才能访问外网。

---

#### 2.2 场景二：反向代理 (HTTPWebProxyHandler)

```yaml
https:
  - listen:
      - "0.0.0.0:443"
    server_name:
      - "app.example.com"
    keyfile: "/etc/ssl/private.key"
    certfile: "/etc/ssl/cert.pem"
    web:
      - location: "/"
        proxy:
          pass: "http://backend:8080"
          # 引用顶级 auth 配置
          forward_auth: "google-oauth"
```

**适用场景**：保护内部 Web 应用，仅允许经过 OAuth 认证的用户访问。

---

#### 2.3 场景三：WebShell (HTTPWebShellHandler)

```yaml
https:
  - listen:
      - "0.0.0.0:443"
    server_name:
      - "shell.example.com"
    keyfile: "/etc/ssl/private.key"
    certfile: "/etc/ssl/cert.pem"
    web:
      - location: "/terminal/"
        shell:
          enabled: true
          # 引用顶级 auth 配置
          forward_auth: "internal-auth"
          command: "/bin/bash"
          home: "/home/admin"
```

**适用场景**：在线终端服务，仅允许管理员访问。

---

#### 2.4 场景四：内网穿透 (HTTPTunnelHandler)

```yaml
https:
  - listen:
      - "0.0.0.0:443"
    server_name:
      - "tunnel.example.com"
    keyfile: "/etc/ssl/private.key"
    certfile: "/etc/ssl/cert.pem"
    tunnel:
      enabled: true
      # 引用顶级 auth 配置
      forward_auth: "internal-auth"
      allow_listens:
        - "240.0.0.0/8"  # MemoryDialer 保留地址
```

**适用场景**：内网穿透服务，需要验证隧道用户身份。

---

### 3. 完整配置示例

以下是使用 mcp-liner 工具生成的完整配置示例：

```yaml
# liner-forward-auth.yaml
# Forward Authentication 完整配置示例

global:
  log_level: "info"
  dns_server: "https://1.1.1.1/dns-query"
  geoip_dir: "/var/lib/liner/geoip"

#######################################
# 顶级认证配置 - 所有认证方式定义在这里
#######################################
auth:
  forward_auth:
    # Google OAuth 认证
    google-oauth:
      address: "http://127.0.0.1:4180/oauth2/auth"
      trust_forward_header: true
      auth_response_headers:
        - "X-Auth-Request-User"
        - "X-Auth-Request-Email"
        - "X-Auth-Request-Groups"
      auth_request_headers:
        - "Cookie"
        - "Authorization"
        - "X-Real-IP"
        - "X-Forwarded-For"

    # 内部认证服务
    internal-auth:
      address: "http://127.0.0.1:9000/api/verify"
      trust_forward_header: true
      auth_response_headers:
        - "X-User-Id"
        - "X-User-Role"

    # GitHub OAuth 认证
    github-oauth:
      address: "http://127.0.0.1:4181/oauth2/auth"
      trust_forward_header: true
      auth_response_headers:
        - "X-Auth-Request-User"
        - "X-Auth-Request-Email"

#######################################
# 正向代理 - 企业出口
#######################################
http:
  - listen:
      - "0.0.0.0:8080"
    forward:
      forward_auth: "google-oauth"
      policy: |
        {{ if .User.Username }}
          {{ if geoip_cn .Request.Host }}direct{{ else }}proxy{{ end }}
        {{ else }}
          require_auth
        {{ end }}
      dialer: "local"
      log: true

#######################################
# HTTPS 服务
#######################################
https:
  #------------------------------------
  # 1. Web 应用 - Google OAuth
  #------------------------------------
  - listen:
      - "0.0.0.0:443"
    server_name:
      - "app.example.com"
    keyfile: "/etc/ssl/app.key"
    certfile: "/etc/ssl/app.crt"
    web:
      - location: "/"
        proxy:
          pass: "http://app-backend:3000"
          forward_auth: "google-oauth"

      - location: "/api/"
        proxy:
          pass: "http://api-backend:8080"
          forward_auth: "google-oauth"
          strip_prefix: "/api"

  #------------------------------------
  # 2. 管理面板 - 内部认证
  #------------------------------------
  - listen:
      - "0.0.0.0:443"
    server_name:
      - "admin.example.com"
    keyfile: "/etc/ssl/admin.key"
    certfile: "/etc/ssl/admin.crt"
    web:
      - location: "/"
        proxy:
          pass: "http://admin-panel:8000"
          forward_auth: "internal-auth"

      - location: "/terminal/"
        shell:
          enabled: true
          forward_auth: "internal-auth"
          command: "/bin/bash"
          home: "/home/admin"

  #------------------------------------
  # 3. 隧道服务 - 内部认证
  #------------------------------------
  - listen:
      - "0.0.0.0:443"
    server_name:
      - "tunnel.example.com"
    keyfile: "/etc/ssl/tunnel.key"
    certfile: "/etc/ssl/tunnel.crt"
    tunnel:
      enabled: true
      forward_auth: "internal-auth"
      allow_listens:
        - "240.0.0.0/8"
      enable_keep_alive: true
      log: true

  #------------------------------------
  # 4. 开发者门户 - GitHub OAuth
  #------------------------------------
  - listen:
      - "0.0.0.0:443"
    server_name:
      - "dev.example.com"
    keyfile: "/etc/ssl/dev.key"
    certfile: "/etc/ssl/dev.crt"
    web:
      - location: "/"
        proxy:
          pass: "http://dev-portal:5000"
          forward_auth: "github-oauth"

#######################################
# Dialer 配置
#######################################
dialer:
  local: |
    local://
  proxy: |
    local://
    socks5://proxy.internal:1080
```

---

## 核心组件设计

### 组件类图

```mermaid
classDiagram
    class HTTPHandler {
        <<interface>>
        +Load() error
        +ServeHTTP(rw, req)
    }

    class ForwardAuthMiddleware {
        -name string
        -config *ForwardAuthConfig
        -transport *http.Transport
        -next HTTPHandler
        +Load() error
        +ServeHTTP(rw, req)
        -doAuthRequest(req) (*http.Response, error)
        -handleUnauthorized(rw, req)
        -injectHeaders(req, authResp)
    }

    class HTTPForwardHandler {
        +Config HTTPConfig
        +forward_auth string
        -userchecker AuthUserChecker
    }

    class HTTPWebProxyHandler {
        +Pass string
        +forward_auth string
        -userchecker AuthUserChecker
    }

    class HTTPWebShellHandler {
        +AuthTable string
        +forward_auth string
        -userchecker AuthUserChecker
    }

    class HTTPTunnelHandler {
        +Config HTTPConfig
        +forward_auth string
        -userchecker AuthUserChecker
    }

    HTTPHandler <|.. ForwardAuthMiddleware
    HTTPHandler <|.. HTTPForwardHandler
    HTTPHandler <|.. HTTPWebProxyHandler
    HTTPHandler <|.. HTTPWebShellHandler
    HTTPHandler <|.. HTTPTunnelHandler

    ForwardAuthMiddleware --> HTTPForwardHandler : wraps
    ForwardAuthMiddleware --> HTTPWebProxyHandler : wraps
    ForwardAuthMiddleware --> HTTPWebShellHandler : wraps
    ForwardAuthMiddleware --> HTTPTunnelHandler : wraps
```

### 认证请求流程

```mermaid
flowchart TD
    A[收到请求] --> B{forward_auth<br/>已配置?}
    B -->|否| Z[执行原有认证逻辑<br/>auth_table]
    B -->|是| C[查找 Auth 配置]

    C --> D{配置存在?}
    D -->|否| E[返回 500 配置错误]
    D -->|是| F[构造认证请求]

    F --> G["发送 GET 到 address<br/>携带 auth_request_headers"]
    G --> H{响应状态?}

    H -->|200 OK| I[提取 auth_response_headers]
    I --> J[注入用户信息头到原请求]
    J --> K[设置 AuthUserInfo]
    K --> Z

    H -->|401/403| L[返回认证重定向或错误]
    H -->|其他错误| M[返回 502 Bad Gateway]

    style C fill:#E3F2FD
    style I fill:#E8F5E9
    style L fill:#FFF3E0
```

---

## 与现有代码集成

### 修改点清单

| 文件 | 修改内容 |
|------|----------|
| `config.go` | 添加 `AuthConfig` 和 `ForwardAuthConfig` 结构体 |
| `config.go` | 在 `Config` 中添加 `Auth AuthConfig` 字段 |
| `config.go` | 在各 Handler 配置中添加 `ForwardAuth string` 字段 |
| `handler_http_forward.go` | 支持 `forward_auth` 配置，优先于 `auth_table` |
| `handler_http_web_proxy.go` | 支持 `forward_auth` 配置 |
| `handler_http_web_shell.go` | 支持 `forward_auth` 配置 |
| `handler_http_tunnel.go` | 支持 `forward_auth` 配置 |
| `handler_http_web_forward_auth.go` | **新增**：ForwardAuth 中间件实现 |

### 复用现有组件

| 组件 | 复用方式 |
|------|----------|
| `AuthUserInfo` | 从 Forward Auth 响应头填充 |
| `AuthUserChecker` | 保留作为降级/备选方案 |
| `HTTPRequestInfo` | 传递认证上下文 |
| `*http.Transport` | 复用全局 Transport 发送认证请求 |

---

## 部署架构

### OAuth2 Proxy 配置示例

```yaml
# oauth2-proxy.cfg
provider = "google"
client_id = "xxxxx.apps.googleusercontent.com"
client_secret = "xxxxx"
cookie_secret = "xxxxx-random-secret-xxxxx"
email_domains = ["example.com"]

# 仅响应模式，不做反向代理
upstream = "static://200"
skip_provider_button = true

# 会话存储
session_store_type = "redis"
redis_connection_url = "redis://redis:6379"

# 传递用户信息
set_xauthrequest = true
pass_user_headers = true
```

### Docker Compose 部署示例

```yaml
version: "3.8"
services:
  liner:
    image: phuslu/liner:latest
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - ./liner.yaml:/etc/liner/config.yaml
      - ./certs:/etc/ssl
    depends_on:
      - oauth2-proxy
      - redis

  oauth2-proxy:
    image: quay.io/oauth2-proxy/oauth2-proxy:latest
    command:
      - --config=/etc/oauth2-proxy.cfg
    volumes:
      - ./oauth2-proxy.cfg:/etc/oauth2-proxy.cfg
    environment:
      - OAUTH2_PROXY_CLIENT_ID=${GOOGLE_CLIENT_ID}
      - OAUTH2_PROXY_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
      - OAUTH2_PROXY_COOKIE_SECRET=${COOKIE_SECRET}
    depends_on:
      - redis

  redis:
    image: redis:alpine
    volumes:
      - redis-data:/data

volumes:
  redis-data:
```

---

## 性能与安全考量

### 性能优化

| 优化项 | 实现方式 |
|--------|----------|
| **连接复用** | 复用 `http.Transport` 连接池 |
| **超时控制** | 认证请求独立超时（5s） |
| **短路失败** | 认证服务不可用时快速 503 |
| **无状态** | Liner 不存储 Session |

### 安全考量

| 安全项 | 措施 |
|--------|------|
| **内网通信** | 认证服务仅内网暴露 |
| **头注入防护** | 清除客户端伪造的 `X-Auth-*` 头 |
| **HTTPS** | 生产环境强制使用 |

---

## 实现检查清单

| 阶段 | 任务 | 状态 |
|------|------|------|
| **配置** | 添加 `AuthConfig` 结构体 | ⬜ |
| **配置** | 各 Handler 配置添加 `forward_auth` 字段 | ⬜ |
| **实现** | 创建 `handler_http_web_forward_auth.go` | ⬜ |
| **集成** | `HTTPForwardHandler` 支持 forward_auth | ⬜ |
| **集成** | `HTTPWebProxyHandler` 支持 forward_auth | ⬜ |
| **集成** | `HTTPWebShellHandler` 支持 forward_auth | ⬜ |
| **集成** | `HTTPTunnelHandler` 支持 forward_auth | ⬜ |
| **测试** | 单元测试 | ⬜ |
| **测试** | OAuth2 Proxy 集成测试 | ⬜ |
| **文档** | 更新 `AGENTS.md` | ⬜ |

---

## 总结

Forward Authentication 方案通过以下设计满足各项目标：

1. **顶级 Auth 配置 + 命名引用**：避免重复定义，配置复用率高
2. **多场景覆盖**：支持正向代理、反向代理、WebShell、Tunnel 等全部场景
3. **最小侵入**：仅新增中间件，不破坏现有认证逻辑（`auth_table` 作为降级选项）
4. **轻量高效**：复用现有 HTTP Transport，无新依赖
5. **省内存**：状态外置到 OAuth2 Proxy + Redis

该方案将认证逻辑解耦到独立服务，使 liner 专注于高性能代理转发，同时获得企业级身份认证能力（OAuth2、OIDC、SAML、LDAP 等）的支持。
