<!-- review: finished -->

<a id="oidc-config"></a>

# OIDC 身份验证设置

本指南说明如何使用 Google 作为身份提供商
和 Angie Web 服务器通过 Lua 脚本设置 OpenID Connect (OIDC) 身份验证。

该实现使用 OAuth2/OIDC 身份验证保护内部端点,
并演示了一种基于电子邮件域限制访问的方法。
这只是一种示例方法;您可以按照自己喜欢的方式实现访问控制,
例如维护特定用户的允许列表、检查提供商响应中的域成员资格
或组属性,或使用来自您私有 IAM 系统的自定义声明。

<a id="architecture"></a>

## 架构

此处建议的 OIDC 设置包括:

- Angie - 支持 Lua 模块以进行 OIDC 处理
- [lua-resty-openidc](https://github.com/zmartzone/lua-resty-openidc) - 用于 OIDC 身份验证的 OpenResty Lua 库
- Google OAuth2 - 用于用户身份验证的身份提供商
- Docker Compose - 在本示例中仅用于快速启动;
  在生产环境中使用您喜欢的任何部署方法

<a id="prerequisites"></a>

## 前提条件

在配置 OIDC 身份验证之前,请确保您具备:

1. 支持 [Lua 模块](https://cn.angie.software//angie/docs/installation/external-modules/lua.md#external-lua) 的 Angie Web 服务器
2. Docker 和 Docker Compose(用于部署)
3. Google Cloud Console 项目
4. 来自 Google 的 OAuth2 凭据

<a id="google-oauth2-setup"></a>

## Google OAuth2 设置

要将 Google 配置为您的 OIDC 提供商:

1. 导航到 [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
2. 创建新项目或选择现有项目
3. 为您的项目配置 OAuth 同意屏幕(外部或内部)并发布它,以便用户可以进行身份验证
4. 创建 OAuth2 凭据:
   - 应用类型: Web 应用程序
   - 已授权的重定向 URI: `http://localhost/auth/callback`
5. 保存您的 `client_id` 和 `client_secret` 以供配置使用

#### NOTE
标准 Google Identity Services 已经支持 OIDC;不需要旧版 Google+ API。
仅当您的应用程序需要其数据时才启用其他 Google API。

<a id="configuration-setup"></a>

## 配置设置

让我们从 OIDC 设置所需的配置文件开始。

<a id="docker-compose-configuration"></a>

### Docker Compose 配置

Docker 部署使用以下配置文件:

```yaml
services:
  angie:
    image: docker.angie.software/angie:templated
    environment:
      ANGIE_LOAD_MODULES: "lua"
    ports:
      - 80:80
    volumes:
      - ./files/etc/angie/http.d:/etc/angie/http.d
```

此配置执行以下操作:

- 使用支持 Lua 模块的 [模板化 Angie 镜像](https://cn.angie.software//angie/docs/installation/docker.md#docker-templated)
- 加载 [Lua 模块](https://cn.angie.software//angie/docs/installation/external-modules/lua.md#external-lua) 以实现 OIDC 功能
- 映射端口 80 以进行 HTTP 访问
- 从本地目录挂载配置文件

对于即插即用的演示,
下载 [`OIDC 快速启动包`](https://cn.angie.software//angie/docs/configuration/oidc-sample-config.zip),
在 `files/etc/angie/http.d/oidc.lua` 中设置您的 `client_id` 和 `client_secret`,
一切都将开箱即用。

<a id="oidc-authentication-script"></a>

### OIDC 身份验证脚本

创建一个 OIDC 身份验证脚本,
使用 `lua-resty-openidc` 库处理身份验证逻辑:

```lua
access_by_lua_block {
    local res, err = require("resty.openidc").authenticate({
        redirect_uri = "http://localhost/auth/callback",
        discovery = "https://accounts.google.com/.well-known/openid-configuration",
        logout_path = "/auth/logout",
        redirect_after_logout_uri = "/auth/logged-out",
        revoke_tokens_on_logout = true,
        client_id = "YOUR_CLIENT_ID",
        client_secret = "YOUR_CLIENT_SECRET"
    })
}
```

配置参数:

- `redirect_uri`: 成功身份验证后的回调 URL
- `discovery`: Google 的 OIDC 发现端点
- `logout_path`: 用户注销路径
- `redirect_after_logout_uri`: 注销后的重定向目标
- `revoke_tokens_on_logout`: 注销时撤销令牌以确保安全
- `client_id` 和 `client_secret`: 您的 Google OAuth2 凭据

<a id="angie-configuration"></a>

### Angie 配置

使用必要的 [location](https://cn.angie.software//angie/docs/configuration/modules/http/index.md#location) 块配置 Angie
以进行 OIDC 身份验证。

<a id="protected-resources"></a>

#### 受保护的资源

要使用 OIDC 身份验证保护资源:

```nginx
location /internal/ {
    include /etc/angie/http.d/oidc.lua;
    proxy_pass http://127.0.0.1/status/;
}
```

此配置执行以下操作:

- 使用 OIDC 身份验证保护 `/internal/` 路径
- 将经过身份验证的请求代理到 `/status/` 的 [内部 API](https://cn.angie.software//angie/docs/configuration/modules/http/http_api.md#http-api)

<a id="authentication-endpoints"></a>

#### 身份验证端点

配置 OAuth2 流程端点:

```nginx
location /auth/callback {
    include /etc/angie/http.d/oidc.lua;
}

location /auth/logout {
    include /etc/angie/http.d/oidc.lua;
}

location /auth/logged-out {
    default_type text/plain;
    return 200 "You have been logged out. Bye!";
}
```

端点功能:

- `/auth/callback`: 处理来自 Google 的 OAuth2 回调
- `/auth/logout`: 启动用户注销
- `/auth/logged-out`: 成功注销后的着陆页

<a id="internal-api-access"></a>

#### 内部 API 访问

配置对内部 API 的受限访问:

```nginx
location /status/ {
    api     /status/;
    allow   127.0.0.1;
    deny    all;
}
```

这提供了:

- 访问 Angie 的 [状态 API](https://cn.angie.software//angie/docs/configuration/modules/http/http_api.md#http-api)
- 仅限本地主机访问 (127.0.0.1)
- 通过 `/internal/` 访问时的 OIDC 保护

<a id="deployment-steps"></a>

## 部署步骤

按照以下步骤部署 OIDC 身份验证:

<a id="configuration-update"></a>

### 配置更新

1. 在您的 OIDC Lua 脚本中更新 OAuth2 凭据:

   替换 `oidc.lua` 中的占位符值:
   - 将 `client_id` 替换为您的 Google OAuth2 客户端 ID
   - 将 `client_secret` 替换为您的 Google OAuth2 客户端密钥

<a id="service-startup"></a>

### 服务启动

1. 启动 Docker 服务:
   ```console
   $ docker-compose up -d
   ```
2. 验证部署:
   - 导航到 `http://localhost/internal/`
   - 您应该被重定向到 Google 进行身份验证
   - 成功登录后,您将访问受保护的内容

<a id="security-configuration"></a>

## 安全配置

<a id="email-domain-restriction"></a>

### 电子邮件域限制

实现域验证以按电子邮件域限制访问:

```lua
if not string.match(res.user.email, "gmail.com$") then
    ngx.exit(ngx.HTTP_FORBIDDEN)
end
```

对于生产环境,请考虑以下事项:

- 将 `gmail.com` 替换为您组织的域
- 实现允许的电子邮件地址白名单
- 添加基于角色的访问控制

<a id="token-management"></a>

### 令牌管理

OIDC 实现包括安全功能:

- 注销时自动撤销令牌 (`revoke_tokens_on_logout = true`)
- lua-resty-openidc 库安全地管理会话
- 所有身份验证流程都遵循 OAuth2/OIDC 安全最佳实践

#### WARNING
对于生产部署:

- 始终对 OAuth2 回调使用 HTTPS
- 安全地存储客户端密钥,切勿存储在版本控制中
- 实施适当的会话超时和续订策略
- 监控身份验证日志以发现安全事件

<a id="authentication-flow"></a>

## 身份验证流程

OIDC 身份验证流程遵循标准 OAuth2/OIDC 程序:

1. 用户访问受保护的 URL(例如 `http://localhost/internal/`)
2. 如果未经身份验证,用户将被重定向到 Google OAuth2
3. 用户使用 Google 凭据进行身份验证
4. Google 使用授权码重定向回 `/auth/callback`
5. 服务器将代码交换为访问令牌和 ID 令牌
6. 验证用户信息(包括电子邮件域检查)
7. 授予用户访问受保护资源的权限

注销过程确保安全的会话终止:

1. 用户导航到 `http://localhost/auth/logout`
2. 在 Google 的 OAuth2 端点撤销令牌
3. 清除本地会话数据
4. 用户被重定向到 `/auth/logged-out`

<a id="advanced-configuration"></a>

## 高级配置

要限制对特定组织域的访问:

```lua
if not string.match(res.user.email, "yourcompany.com$") then
    ngx.exit(ngx.HTTP_FORBIDDEN)
end
```

对于具有多个允许域的组织:

```lua
local allowed_domains = {"company1.com", "company2.com", "gmail.com"}
local email_valid = false

for _, domain in ipairs(allowed_domains) do
    if string.match(res.user.email, domain .. "$") then
        email_valid = true
        break
    end
end

if not email_valid then
    ngx.exit(ngx.HTTP_FORBIDDEN)
end
```

<a id="user-information-access"></a>

### 用户信息访问

从 OIDC 提供商访问其他用户声明:

```lua
-- 从 ID 令牌访问用户信息
local user_name = res.user.name
local user_picture = res.user.picture
local user_locale = res.user.locale
local user_email = res.user.email
```

这些值可用于日志记录、个性化或其他访问控制决策。
