Authentik

Authentik

目录

提示:已经在运行 Apache2?改用 mod_auth_openidc

如果 Apache2 是你的 Web 服务器,推荐使用 mod_auth_openidc——它原生处理 OIDC 流程,无需单独的 OAuth2 Proxy 容器。参见 Apache2 配合 mod_auth_openidc。

本指南介绍如何将 Authentik 配置为身份提供方,并使用 OAuth2 Proxy 作为认证反向代理。

架构

Browser  →  Webserver (nginx / Apache2)  →  OAuth2 Proxy  →  Wavelog
                                                  ↕
                                          Authentik (JWKS)

OAuth2 Proxy 负责与 Authentik 之间的 OIDC 流程,并将 JWT 访问令牌以 HTTP 请求头的形式转发给 Wavelog。Wavelog 使用 Authentik 的 JWKS 端点校验该令牌。


步骤 1:创建 OAuth2 提供方

在 Authentik 管理界面中:

  1. 进入 应用 → 提供方 → 创建
  2. 选择 OAuth2/OpenID 提供方
  3. 配置:
    • 名称: wavelog
    • 授权流程: 选择你的默认授权流程
    • 客户端类型: Confidential
    • 重定向 URI: https://wavelog.example.org/oauth2/callback
  4. 保存

保存后,从提供方详情页复制 客户端 ID 和 客户端密钥。


步骤 2:创建应用

  1. 进入 应用 → 应用 → 创建
  2. 配置:
    • 名称: Wavelog
    • Slug: wavelog
    • 提供方: 选择步骤 1 中创建的 OAuth2 提供方
  3. 保存

步骤 3:添加呼号声明

默认情况下,Authentik 不会在令牌中包含自定义用户属性。你需要将呼号存储为用户属性,并创建一个 Scope Mapping 将其加入 JWT。

3a. 添加用户属性

  1. 进入 目录 → 用户 → 选择一个用户 → 编辑
  2. 在 属性 字段(JSON 编辑器)中添加:

    json { "callsign": "N0CALL" }

  3. 保存

注意

Authentik 将自定义属性存储为 JSON 数据块。管理界面中没有专门用于自定义属性的表单字段——这是 Authentik 的标准行为。属性会被正确存储,并按下文 Scope Mapping 的配置出现在 JWT 中。

3b. 创建 Scope Mapping

  1. 进入 定制 → 属性映射 → 创建
  2. 选择 Scope Mapping
  3. 配置:

    • 名称: Wavelog Callsign
    • Scope 名称: wavelog_callsign
    • 表达式:

      python return { "callsign": request.user.attributes.get("callsign", None) }

  4. 保存

提示

用同样的方法,通过创建额外的 Scope Mapping 将其他用户属性(例如 locator)映射为 JWT 声明。

3c. 将 Scope Mapping 添加到提供方

  1. 返回你的 OAuth2 提供方(Applications → Providers → 选择 wavelog)
  2. 编辑 → 高级协议设置 → 作用域
  3. 在列表中添加 Wavelog Callsign
  4. 保存

步骤 4:配置 OAuth2 Proxy

OAuth2 Proxy 充当认证反向代理。它负责与 Authentik 之间的 OIDC 登录流程,并将 JWT 访问令牌转发给 Wavelog。

重要

OAuth2 Proxy 只能保护 /index.php/header_auth/login。Wavelog 的其他所有路径都必须直通,不得要求认证。

Docker Compose

services:
  oauth2-proxy:
    container_name: oauth2-proxy
    image: quay.io/oauth2-proxy/oauth2-proxy:latest
    command: --config /oauth2-proxy.cfg
    hostname: oauth2-proxy
    volumes:
      - "./oauth2-proxy.cfg:/oauth2-proxy.cfg"
    restart: unless-stopped
    networks:
      - proxy-net

  nginx:
    container_name: nginx-proxy
    image: nginx:alpine
    volumes:
      - "./nginx.conf:/etc/nginx/conf.d/default.conf:ro"
      - "./ssl:/etc/nginx/ssl:ro"
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - oauth2-proxy
    restart: unless-stopped
    networks:
      - proxy-net

networks:
  proxy-net:
    driver: bridge

oauth2-proxy.cfg

详细说明请参阅 OAuth2 Proxy 官方文档。

http_address = "0.0.0.0:4180"

cookie_secret = "your-random-cookie-secret" # see below how to generate

provider = "oidc"
email_domains = ["*"]
client_id = "your-client-id"
client_secret = "your-client-secret"
redirect_url = "https://wavelog.example.org/oauth2/callback"
oidc_issuer_url = "https://authentik.example.org/application/o/wavelog/"
scope = "openid email profile wavelog_callsign"
insecure_oidc_allow_unverified_email = true
code_challenge_method = "S256"
pass_user_headers = true
set_xauthrequest = true
pass_access_token = true

# See https://oauth2-proxy.github.io/oauth2-proxy/configuration/session_storage
cookie_refresh = "4m"
cookie_expire = "30m"

skip_provider_button = true
whitelist_domains = "authentik.example.org"

使用以下命令生成 cookie 密钥:

openssl rand -base64 32 | tr -- '+/' '-_'

注意(与 Keycloak 的主要差异)

  • 使用 provider = "oidc" 而非 keycloak-oidc——Authentik 使用通用 OIDC 提供方
  • scope 必须显式包含 wavelog_callsign,才能请求该自定义作用域
  • 必须设置 insecure_oidc_allow_unverified_email = true,因为 Authentik 默认不会将用户邮箱标记为已验证。若要移除该设置,请进入 目录 → 用户,编辑该用户并手动启用 邮箱已验证
  • oidc_issuer_url 遵循 Authentik 的格式:https://<authentik-domain>/application/o/<application-slug>/

步骤 5:配置 Web 服务器

nginx

server {
    listen 80;
    server_name wavelog.example.org;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name wavelog.example.org;

    ssl_certificate     /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.key;

    # oauth2-proxy endpoints — OIDC flow (callback, sign-in, etc.)
    location /oauth2/ {
        proxy_pass       http://oauth2-proxy:4180;
        proxy_set_header Host                    $host;
        proxy_set_header X-Real-IP               $remote_addr;
        proxy_set_header X-Scheme                $scheme;
        proxy_set_header X-Auth-Request-Redirect $request_uri;

        # Recommended buffer settings for larger JWTs to prevent 502 errors
        proxy_buffer_size          16k;
        proxy_buffers              4 16k;
        proxy_busy_buffers_size    16k;
    }

    # Auth subrequest endpoint (internal, used by auth_request only)
    location = /oauth2/auth {
        proxy_pass             http://oauth2-proxy:4180;
        proxy_pass_request_body off;
        proxy_set_header Content-Length   "";
        proxy_set_header X-Original-URI  $request_uri;
        proxy_set_header X-Real-IP       $remote_addr;
        proxy_set_header X-Scheme        $scheme;
        proxy_buffer_size          16k;
        proxy_buffers              4 16k;
        proxy_busy_buffers_size    16k;
    }

    # SSO login endpoint — oauth2-proxy authenticates, JWT is forwarded to Wavelog
    location ^~ /index.php/header_auth/ {
        auth_request /oauth2/auth;

        # Not authenticated → start Authentik login directly (no splash screen)
        error_page 401 = @sso_start;

        # Forward JWT access token from oauth2-proxy to Wavelog
        auth_request_set $access_token $upstream_http_x_auth_request_access_token;
        proxy_set_header X-Authentik-Jwt $access_token;

        proxy_pass http://wavelog:80;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Trigger Authentik login flow
    location @sso_start {
        return 302 /oauth2/start?rd=/index.php/header_auth/login;
    }

    # All other routes — no authentication required, Wavelog manages sessions itself
    location / {
        proxy_pass http://wavelog:80;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

重要

转发给 Wavelog 的请求头是 X-Authentik-Jwt(而非 Keycloak 所用的 X-Forwarded-Access-Token)。该名称必须与 sso.php 中的 auth_header_accesstoken 一致(参见步骤 6)。


步骤 6:配置 Wavelog

application/config/config.php:

<?php
$config['auth_header_enable'] = true;

application/config/sso.php(从 sso.sample.php 复制):

<?php
$config['auth_header_create']              = true;
$config['auth_header_allow_direct_login']  = false;
$config['auth_header_hide_password_field'] = true;

// Authentik JWT is forwarded by nginx as X-Authentik-Jwt
$config['auth_header_accesstoken']         = "HTTP_X_AUTHENTIK_JWT";
$config['auth_header_text']                = "Login with SSO";

// Redirect to Authentik end-session on logout
$config['auth_header_url_logout'] = 'https://authentik.example.org/application/o/wavelog/end-session/';

// Authentik JWKS endpoint — enables cryptographic JWT verification
$config['auth_header_jwks_uri'] = 'https://authentik.example.org/application/o/wavelog/jwks/';

$config['auth_headers_claim_config'] = [
    'user_name'      => [
        'claim'               => 'preferred_username',
        'override_on_update'  => true,
        'allow_manual_change' => false,
    ],
    'user_email'     => [
        'claim'               => 'email',
        'override_on_update'  => true,
        'allow_manual_change' => false,
    ],
    'user_callsign'  => [
        'claim'               => 'callsign',
        'override_on_update'  => true,
        'allow_manual_change' => false,
    ],
    'user_locator'   => [
        'claim'               => 'locator',
        'override_on_update'  => true,
        'allow_manual_change' => true,
    ],
    'user_firstname' => [
        'claim'               => 'given_name',
        'override_on_update'  => true,
        'allow_manual_change' => true,
    ],
    'user_lastname'  => [
        'claim'               => 'family_name',
        'override_on_update'  => true,
        'allow_manual_change' => true,
    ],
];

将 authentik.example.org 和 wavelog(应用 slug)替换为你的实际值。


步骤 7:验证

  1. 在浏览器中打开 Wavelog——应出现 SSO 登录按钮
  2. 点击该按钮并完成 Authentik 登录
  3. 你应被重定向到 Wavelog 仪表盘

如果出现问题,可在 sso.php 和 config.php 中启用调试日志,以便检查 JWT 声明:

<?php
// sso.php
$config['auth_header_debug_jwt'] = true;

// config.php
$config['log_threshold'] = 2;

在 application/logs/log-YYYY-MM-DD.php 中查看解码后的 JWT 载荷。确认 callsign、email 和 preferred_username 均存在,且与你的声明映射一致。

警告

排查完成后请再次关闭 auth_header_debug_jwt。


故障排查

email in id_token isn't verified : Authentik 默认不会将邮箱标记为已验证。可以在 oauth2-proxy.cfg 中添加 insecure_oidc_allow_unverified_email = true(上文示例中已包含),或进入 目录 → 用户,编辑该用户并启用 邮箱已验证。

callsign 声明未出现在 JWT 中 : 令牌中未包含 wavelog_callsign 作用域。请确认:

  1. 在 定制 → 属性映射 下存在 Scope Mapping Wavelog Callsign
  2. 该作用域已在 高级协议设置 → 作用域 下分配给提供方
  3. oauth2-proxy.cfg 中的 scope 显式包含 wavelog_callsign
  4. 用户已在 目录 → 用户 → 属性 下设置了 callsign 属性

SSO Authentication: Missing access token header : nginx 没有转发令牌请求头。请确认:

  1. /index.php/header_auth/ 配置块中存在 auth_request_set $access_token $upstream_http_x_auth_request_access_token;
  2. 同一配置块中设置了 proxy_set_header X-Authentik-Jwt $access_token;
  3. sso.php 中设置了 auth_header_accesstoken = "HTTP_X_AUTHENTIK_JWT"
Wavelog 手册(中文版) / Authentik 0 字 0 行 bg8lng
2026-10-21T12:42:36.078751972Z 2026-10-21T12:43:42.966884880Z