> ## Documentation Index
> Fetch the complete documentation index at: https://ayakaleaf-pro.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# SAML 认证

<Info>
  此功能由 [yu-i-i/overleaf-cep](https://github.com/yu-i-i/overleaf-cep) 开发。这里我们提供一些文档，供你进行配置时参考。
</Info>

### 配置

Overleaf SAML 模块在内部使用 [passport-saml](https://github.com/node-saml/passport-saml) 库，以下大多数配置选项都会直接传递给 `passport-saml`。如果你在配置 SAML 时遇到问题，建议阅读 `passport-saml` 的 README，以了解它所需的配置。

启用 SAML 认证模块需要设置环境变量 `EXTERNAL_AUTH`。该环境变量指定启用哪些外部认证方式，其值是一个列表。如果列表中包含 `saml`，则会启用 SAML 认证。

例如：`EXTERNAL_AUTH=ldap saml`

使用 SAML 认证方式时，用户会被重定向到身份提供商（IdP）的认证站点。如果 IdP 成功认证了该用户，系统会在 Overleaf 用户数据库中查找包含如下结构的 `samlIdentifiers` 字段的记录：

```json theme={null}
samlIdentifiers: [
  {
    externalUserId: "...",
    providerId: "1",
    userIdAttribute: "..."
  }
]
```

`externalUserId` 必须与 IdP 服务器返回的用户资料中由 `userIdAttribute` 指定的属性值相匹配。

如果未找到匹配的记录，系统会在数据库中查找主邮箱地址与 IdP 用户资料中的邮箱相匹配的用户：

* 如果找到了这样的用户，则删除其 `hashedPassword` 字段以禁用本地认证，并添加 `samlIdentifiers` 字段。
* 如果没有找到匹配的用户，则会使用 IdP 资料中的邮箱地址和 `samlIdentifiers` 创建一个新用户。

<strong>注意：</strong> 目前仅支持一个 SAML IdP。`samlIdentifiers` 中的 `providerId` 字段固定为 `'1'`。

#### 环境变量

* `OVERLEAF_SAML_IDENTITY_SERVICE_NAME`
  * 身份服务的显示名称，用于登录页面（默认值：`Log in with SAML IdP`）。
* `OVERLEAF_SAML_USER_ID_FIELD`
  * Overleaf 会将该属性的值用作外部用户 ID，默认为 `nameID`。
* `OVERLEAF_SAML_EMAIL_FIELD`
  * 用户资料中邮箱字段的名称，默认为 `nameID`。
* `OVERLEAF_SAML_FIRST_NAME_FIELD`
  * 用户资料中 firstName 字段的名称，默认为 `givenName`。
* `OVERLEAF_SAML_LAST_NAME_FIELD`
  * 用户资料中 lastName 字段的名称，默认为 `lastName`
* `OVERLEAF_SAML_UPDATE_USER_DETAILS_ON_LOGIN`
  * 如果设置为 `true`，则在登录时更新用户的 `first_name` 和 `last_name` 字段，并关闭 `/user/settings` 页面上的用户详细信息表单。
* `OVERLEAF_SAML_ENTRYPOINT` <strong>（必需）</strong>
  * SAML 身份服务的入口 URL。
    * 示例：`https://idp.example.com/simplesaml/saml2/idp/SSOService.php`
    * Azure 示例：`https://login.microsoftonline.com/8b26b46a-6dd3-45c7-a104-f883f4db1f6b/saml2`
* `OVERLEAF_SAML_ISSUER` <strong>（必需）</strong>
  * 颁发者（Issuer）名称。
* `OVERLEAF_SAML_AUDIENCE`
  * 预期的 SAML 响应 Audience，默认为 `OVERLEAF_SAML_ISSUER` 的值。
* `OVERLEAF_SAML_IDP_CERT` <strong>（必需）</strong>
  * 包含身份提供商公钥证书的文件路径，用于验证传入 SAML 响应的签名。如果身份提供商有多个有效的签名证书，则可以是证书路径组成的 JSON 数组。
    * 示例（单个证书）：`/var/lib/overleaf/certs/idp_cert.pem`
    * 示例（多个证书）：`["var/lib/overleaf/certs/idp_cert.pem", "/var/lib/overleaf/certs/idp_cert_old.pem"]`
* `OVERLEAF_SAML_PUBLIC_CERT`
  * 包含公共签名证书的文件路径，该证书会嵌入到认证请求中，以便 IdP 验证传入 SAML 请求的签名。当策略配置了 `OVERLEAF_SAML_PRIVATE_KEY` 时，设置[元数据端点](https://github.com/yu-i-i/overleaf-cep/wiki/Extended-CE:-SAML-Authentication#metadata-for-the-identity-provider)需要此项。可以提供证书路径组成的 JSON 数组以支持证书轮换。提供证书数组时，数组中的第一项应与当前的 `OVERLEAF_SAML_PRIVATE_KEY` 相匹配。数组中的其他项可用于在更改 `OVERLEAF_SAML_PRIVATE_KEY` 之前，向 IdP 发布即将使用的证书。
* `OVERLEAF_SAML_PRIVATE_KEY`
  * 包含 PEM 格式私钥的文件路径，该私钥与 `OVERLEAF_SAML_PUBLIC_CERT` 相匹配，用于对 passport-saml 发送的认证请求进行签名。
* `OVERLEAF_SAML_DECRYPTION_CERT`
  * 包含公钥证书的文件路径，用于[元数据端点](https://github.com/yu-i-i/overleaf-cep/wiki/Extended-CE:-SAML-Authentication#metadata-for-the-identity-provider)。
* `OVERLEAF_SAML_DECRYPTION_PVK`
  * 包含与 `OVERLEAF_SAML_DECRYPTION_CERT` 相匹配的私钥的文件路径，用于尝试解密接收到的任何加密断言。
* `OVERLEAF_SAML_SIGNATURE_ALGORITHM`
  * 可选，设置用于签名请求的签名算法，有效值为 'sha1'（默认）、'sha256'（推荐）、'sha512'（最安全，请确认你的 IdP 是否支持）。
* `OVERLEAF_SAML_ADDITIONAL_PARAMS`
  * 添加到所有请求中的额外查询参数的 JSON 字典。
* `OVERLEAF_SAML_ADDITIONAL_AUTHORIZE_PARAMS`
  * 添加到 'authorize' 请求中的额外查询参数的 JSON 字典。
    * 示例：`{"some_key": "some_value"}`
* `OVERLEAF_SAML_IDENTIFIER_FORMAT`
  * 向身份提供商请求的名称标识符格式（默认值：`urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`）。如果使用 `urn:oasis:names:tc:SAML:2.0:nameid-format:persistent`，请确保已定义 `OVERLEAF_SAML_EMAIL_FIELD` 环境变量。如果需要使用 `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`，还必须定义 `OVERLEAF_SAML_USER_ID_FIELD` 环境变量，例如可以将其设置为用户的邮箱地址。
* `OVERLEAF_SAML_ACCEPTED_CLOCK_SKEW_MS`
  * 在检查 OnBefore 和 NotOnOrAfter 断言条件的有效性时间戳时，客户端与服务器之间可接受的时钟偏差（毫秒）。设置为 -1 将完全禁用对这些条件的检查。默认值为 0。
* `OVERLEAF_SAML_ATTRIBUTE_CONSUMING_SERVICE_INDEX`
  * 添加到 AuthnRequest 中的 `AttributeConsumingServiceIndex` 属性，用于指示 IdP 在响应中附加哪个属性集（[链接](http://blog.aniljohn.com/2014/01/data-minimization-front-channel-saml-attribute-requests.html)）。
* `OVERLEAF_SAML_AUTHN_CONTEXT`
  * 用于请求认证上下文的名称标识符格式值组成的 JSON 数组。默认值：`["urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"]`。
* `OVERLEAF_SAML_FORCE_AUTHN`
  * 如果为 `true`，服务提供商发出的初始 SAML 请求会指定 IdP 强制用户重新认证，即使用户已拥有有效会话。
* `OVERLEAF_SAML_DISABLE_REQUESTED_AUTHN_CONTEXT`
  * 如果为 `true`，则不请求特定的认证上下文。例如，你可以将其设置为 `true`，以允许无密码登录（`urn:oasis:names:tc:SAML:2.0:ac:classes:X509`）等其他上下文。对其他上下文的支持取决于你的 IdP。
* `OVERLEAF_SAML_AUTHN_REQUEST_BINDING`
  * 如果设置为 `HTTP-POST`，将通过 HTTP POST 绑定向 IdP 请求认证，否则默认使用 HTTP-Redirect。
* `OVERLEAF_SAML_VALIDATE_IN_RESPONSE_TO`
  * 如果为 `always`，则会验证传入 SAML 响应中的 InResponseTo。
  * 如果为 `never`，则不会验证 InResponseTo（默认）。
  * 如果为 `ifPresent`，则仅当传入的 SAML 响应中存在 InResponseTo 时才对其进行验证。
* `OVERLEAF_SAML_WANT_ASSERTIONS_SIGNED` 和 `OVERLEAF_SAML_WANT_AUTHN_RESPONSE_SIGNED`
  * 当设置为 `true`（默认）时，Overleaf 期望 SAML 断言以及整个 SAML 认证响应分别由 IdP 签名。当这两个选项均为 `false` 时，断言或响应中至少有一个必须已签名。
* `OVERLEAF_SAML_REQUEST_ID_EXPIRATION_PERIOD_MS`
  * 定义为 SAML 请求生成的请求 ID 的过期时间，超过该时间后，如果在 SAML 响应的 `InResponseTo` 字段中出现该 ID，则视为无效。默认值：28800000（8 小时）。
* `OVERLEAF_SAML_LOGOUT_URL`
  * 发送注销请求时调用的基础地址（默认值：`entryPoint`）。
    * 示例：`https://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php`
* `OVERLEAF_SAML_ADDITIONAL_LOGOUT_PARAMS`
  * 添加到 'logout' 请求中的额外查询参数的 JSON 字典。
* `OVERLEAF_SAML_IS_ADMIN_FIELD` 和 `OVERLEAF_SAML_IS_ADMIN_FIELD_VALUE`
  * 当这两个环境变量都已设置时，如果 SAML IdP 返回的用户资料包含 `OVERLEAF_SAML_IS_ADMIN_FIELD` 指定的属性，且其值与 `OVERLEAF_SAML_IS_ADMIN_FIELD_VALUE` 匹配，或者是包含 `OVERLEAF_SAML_IS_ADMIN_FIELD_VALUE` 的数组，登录过程会将 `user.isAdmin` 更新为 `true`，否则将 `user.isAdmin` 设置为 `false`。如果这两个变量中有任意一个未设置，则管理员状态仅在 Launchpad 中创建管理员用户时被设置为 `true`。

**身份提供商的元数据**

当前版本的 Overleaf CE 包含一个用于获取服务提供商元数据的端点：`http://my-overleaf-instance.com/saml/meta`

需要配置身份提供商，使其将 Overleaf 服务器识别为"服务提供商"。具体操作方法请查阅你的 SAML 服务器文档。

以下是合适的服务提供商元数据示例：

<Accordion title="ol-meta.xml">
  ```text theme={null}
  <?xml version="1.0"?>
  <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
                    xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
                    entityID="MyOverleaf"
                    ID="_b508c83b7dda452f5b269383fb391107116f8f57">
    <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="true" WantAssertionsSigned="true">
      <KeyDescriptor use="signing">
        <ds:KeyInfo>
          <ds:X509Data>
            <ds:X509Certificate>MII...
  [skipped]
  </ds:X509Certificate>
          </ds:X509Data>
        </ds:KeyInfo>
      </KeyDescriptor>
      <KeyDescriptor use="encryption">
        <ds:KeyInfo>
          <ds:X509Data>
            <ds:X509Certificate>MII...
  [skipped]
  </ds:X509Certificate>
          </ds:X509Data>
        </ds:KeyInfo>
        <EncryptionMethod Algorithm="http://www.w3.org/2009/xmlenc11#aes256-gcm"/>
        <EncryptionMethod Algorithm="http://www.w3.org/2009/xmlenc11#aes128-gcm"/>
        <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
        <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
      </KeyDescriptor>
      <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                           Location="https://my-overleaf-instance.com/saml/logout/callback"/>
      <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
      <AssertionConsumerService index="1"
                                isDefault="true"
                                Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                                Location="https://my-overleaf-instance.com/saml/login/callback"/>
    </SPSSODescriptor>
  </EntityDescriptor>

  ```
</Accordion>

请注意证书、`AssertionConsumerService.Location`、`SingleLogoutService.Location` 和 `EntityDescriptor.entityID`，并在你的 IdP 配置中进行相应设置，或将元数据文件发送给 IdP 管理员。

<Accordion title="variables.env 示例文件（最简）">
  ```text theme={null}
  OVERLEAF_APP_NAME="Our Overleaf Instance"

  ENABLED_LINKED_FILE_TYPES=project_file,project_output_file,url

  # Enables Thumbnail generation using ImageMagick
  ENABLE_CONVERSIONS=true

  # Disables email confirmation requirement
  EMAIL_CONFIRMATION_DISABLED=true

  ## Nginx
  # NGINX_WORKER_PROCESSES=4
  # NGINX_WORKER_CONNECTIONS=768

  ## Set for TLS via nginx-proxy
  # OVERLEAF_BEHIND_PROXY=true
  # OVERLEAF_SECURE_COOKIE=true

  OVERLEAF_SITE_URL=http://my-overleaf-instance.com
  OVERLEAF_NAV_TITLE=Our Overleaf Instance
  # OVERLEAF_HEADER_IMAGE_URL=http://somewhere.com/mylogo.png
  OVERLEAF_ADMIN_EMAIL=support@example.com

  OVERLEAF_LEFT_FOOTER=[{"text": "Contact your support team", "url": "mailto:support@example.com"}]
  OVERLEAF_RIGHT_FOOTER=[{"text":"Hello, I am on the Right", "url":"https://github.com/yu-i-i/overleaf-cep"}]

  OVERLEAF_EMAIL_FROM_ADDRESS=team@example.com
  OVERLEAF_EMAIL_SMTP_HOST=smtp.example.com
  OVERLEAF_EMAIL_SMTP_PORT=587
  OVERLEAF_EMAIL_SMTP_SECURE=false
  # OVERLEAF_EMAIL_SMTP_USER=
  # OVERLEAF_EMAIL_SMTP_PASS=
  # OVERLEAF_EMAIL_SMTP_NAME=
  OVERLEAF_EMAIL_SMTP_LOGGER=false
  OVERLEAF_EMAIL_SMTP_TLS_REJECT_UNAUTH=true
  OVERLEAF_EMAIL_SMTP_IGNORE_TLS=false
  OVERLEAF_CUSTOM_EMAIL_FOOTER=This system is run by department x

  OVERLEAF_PROXY_LEARN=true
  NAV_HIDE_POWERED_BY=true

  #################
  ##     SAML    ##
  #################

  EXTERNAL_AUTH=saml
  OVERLEAF_SAML_ISSUER=MyOverleaf
  OVERLEAF_SAML_IDENTITY_SERVICE_NAME='Log in with SAML Provider'
  OVERLEAF_SAML_EMAIL_FIELD=Email
  OVERLEAF_SAML_FIRST_NAME_FIELD=DisplayName
  OVERLEAF_SAML_LAST_NAME_FIELD=DisplayName
  OVERLEAF_SAML_ENTRYPOINT=http://localhost:18000/login/saml/authorize/admin/SAML_Overleaf
  OVERLEAF_SAML_IDP_CERT=/var/lib/overleaf/certs/idp_cert.pem
  OVERLEAF_SAML_SIGNATURE_ALGORITHM=
  ```
</Accordion>

## 分步指南：goauthentik

本节介绍一套已在 [goauthentik](https://goauthentik.io/) 上测试通过的配置。请将 `https://overleaf.example.com` 替换为你的 `OVERLEAF_SITE_URL`，并将 `https://authentik.example.com` 替换为你的 Authentik 地址。

<Steps>
  <Step title="创建提供商和应用">
    在 Authentik 中，打开 **Applications > Applications** 并点击 **New Application**。该向导会同时创建应用及其提供商。

    1\. 为应用设置名称和 slug，例如 `overleaf`，然后点击 **Next**：

    <Frame caption="Authentik：应用的名称和 slug">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/saml-authentik-app.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=e2405f63578ab00991ad5046665eab32" alt="" width="1120" height="810" data-path="images/on-premises/saml-authentik-app.png" />
    </Frame>

    2\. 选择 **SAML Provider** 并点击 **Next**：

    <Frame caption="Authentik：选择 SAML 提供商">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/saml-authentik-type.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=537780362698d5088132048754ac6481" alt="" width="1120" height="810" data-path="images/on-premises/saml-authentik-type.png" />
    </Frame>

    3\. 填写提供商信息：

    * **Authorization Flow**：`default-provider-authorization-implicit-consent`
    * **ACS URL**：`https://overleaf.example.com/saml/login/callback`
    * **Audience**：Overleaf 的名称，例如 `overleaf`。Overleaf 会将其作为 `OVERLEAF_SAML_ISSUER` 发送。

    <Frame caption="Authentik：应用的 SAML 提供商">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/saml-authentik-create.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=4f15b87b90b355e13831b95c07abe427" alt="" width="1120" height="810" data-path="images/on-premises/saml-authentik-create.png" />
    </Frame>

    4\. 打开 **Advanced protocol settings** 并设置：

    * **Signing Certificate**：一个证书，例如 `authentik Self-signed Certificate`
    * **Sign assertions** 和 **Sign responses**：均开启
    * **Service Provider Binding**：`Post`

    <Frame caption="Authentik：已测试提供商的签名和绑定设置（测试实例）">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/saml-authentik-advanced.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=39b136a219fb192ed42d337de0058166" alt="" width="1089" height="417" data-path="images/on-premises/saml-authentik-advanced.png" />
    </Frame>

    5\. 一直点击 **Next** 直到最后一页，然后提交应用。
  </Step>

  <Step title="从提供商页面复制这些值">
    再次打开该提供商。Overleaf 所需的一切都在其概览页面上：

    <Frame caption="Authentik：SAML 提供商概览（测试实例）">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/saml-authentik-provider.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=b73f3aa817b7a18286db6d810ab3b2e9" alt="" width="990" height="963" data-path="images/on-premises/saml-authentik-provider.png" />
    </Frame>

    | 提供商页面上的项 | 环境变量 |
    | - | - |
    | **SAML Endpoint** | `OVERLEAF_SAML_ENTRYPOINT` 和 `OVERLEAF_SAML_LOGOUT_URL` |
    | **Audience** | `OVERLEAF_SAML_ISSUER` |
    | **Download signing certificate**（一个文件） | `OVERLEAF_SAML_IDP_CERT`，参见下一步 |

    <Warning>
      **SAML Configuration** 下的 **EntityID/Issuer** 是 Authentik 自身的名称。不要将其填入 `OVERLEAF_SAML_ISSUER`，请使用 **Audience**。
    </Warning>
  </Step>

  <Step title="安装签名证书">
    点击 **Download signing certificate** 下的 **Download**，并将文件保存为 Toolkit 目录中的 `data/overleaf/certs/idp_cert.pem`。容器内看到的路径为 `/var/lib/overleaf/certs/idp_cert.pem`：

    ```dotenv theme={null}
    OVERLEAF_SAML_IDP_CERT=/var/lib/overleaf/certs/idp_cert.pem
    ```
  </Step>

  <Step title="映射属性">
    Authentik 会使用以下名称发送其属性：

    ```dotenv theme={null}
    OVERLEAF_SAML_USER_ID_FIELD=http://schemas.goauthentik.io/2021/02/saml/username
    OVERLEAF_SAML_EMAIL_FIELD=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
    OVERLEAF_SAML_FIRST_NAME_FIELD=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
    OVERLEAF_SAML_LAST_NAME_FIELD=http://schemas.goauthentik.io/2021/02/saml/username
    ```

    组信息以 `http://schemas.xmlsoap.org/claims/Group`（一个列表）的形式发送。要让 Authentik 组 `Admins` 的成员成为 Overleaf 的管理员：

    ```dotenv theme={null}
    OVERLEAF_SAML_IS_ADMIN_FIELD=http://schemas.xmlsoap.org/claims/Group
    OVERLEAF_SAML_IS_ADMIN_FIELD_VALUE=Admins
    ```

    <Warning>
      管理员标志会在每次 SAML 登录时更新。如果字段或值有误，所有通过 SAML 登录的管理员都会失去管理员权限。请先使用另一个管理员账户测试该映射。
    </Warning>
  </Step>
</Steps>

<Accordion title="经过测试的 goauthentik variables.env">
  ```dotenv title="variables.env" wrap theme={null}
  EXTERNAL_AUTH=saml
  OVERLEAF_SAML_IDENTITY_SERVICE_NAME=Log in with Authentik
  OVERLEAF_SAML_ENTRYPOINT=https://authentik.example.com/application/saml/overleaf/
  OVERLEAF_SAML_LOGOUT_URL=https://authentik.example.com/application/saml/overleaf/
  OVERLEAF_SAML_ISSUER=overleaf
  OVERLEAF_SAML_IDP_CERT=/var/lib/overleaf/certs/idp_cert.pem
  OVERLEAF_SAML_WANT_ASSERTIONS_SIGNED=true
  OVERLEAF_SAML_WANT_AUTHN_RESPONSE_SIGNED=true
  OVERLEAF_SAML_USER_ID_FIELD=http://schemas.goauthentik.io/2021/02/saml/username
  OVERLEAF_SAML_EMAIL_FIELD=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
  OVERLEAF_SAML_FIRST_NAME_FIELD=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
  OVERLEAF_SAML_LAST_NAME_FIELD=http://schemas.goauthentik.io/2021/02/saml/username
  OVERLEAF_SAML_IS_ADMIN_FIELD=http://schemas.xmlsoap.org/claims/Group
  OVERLEAF_SAML_IS_ADMIN_FIELD_VALUE=Admins
  OVERLEAF_SAML_UPDATE_USER_DETAILS_ON_LOGIN=true
  ```
</Accordion>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.