> ## 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.

# Xác thực OIDC

<Info>
  Tính năng này được phát triển bởi [yu-i-i/overleaf-cep](https://github.com/yu-i-i/overleaf-cep). Tại đây chúng tôi cung cấp một số tài liệu để bạn cấu hình.
</Info>

### Cấu hình

Về mặt nội bộ, module OIDC của Overleaf sử dụng thư viện [passport-openidconnect](https://github.com/jaredhanson/passport-openidconnect). Nếu bạn gặp vấn đề khi cấu hình OpenID Connect, bạn nên đọc README của `passport-openidconnect` để nắm được cấu hình mà thư viện này mong đợi.

Biến môi trường `EXTERNAL_AUTH` là bắt buộc để bật module xác thực OIDC. Biến môi trường này chỉ định những phương thức xác thực bên ngoài nào được kích hoạt. Giá trị của biến này là một danh sách. Nếu danh sách bao gồm `oidc` thì xác thực OIDC sẽ được kích hoạt.

Ví dụ: `EXTERNAL_AUTH=ldap oidc`

Khi sử dụng phương thức xác thực OIDC, người dùng sẽ được chuyển hướng đến trang xác thực của Nhà cung cấp định danh (IdP). Nếu IdP xác thực người dùng thành công, cơ sở dữ liệu người dùng Overleaf sẽ được kiểm tra để tìm bản ghi có trường `thirdPartyIdentifiers` với cấu trúc như sau:

```text theme={null}
thirdPartyIdentifiers: [
  {
    externalUserId: "...",
    externalData: null,
    providerId: "..."
  }
]
```

`externalUserId` phải khớp với ID người dùng trong hồ sơ do máy chủ IdP trả về (xem biến môi trường `OVERLEAF_OIDC_USER_ID_FIELD`), và `providerId` phải khớp với ID của nhà cung cấp OIDC (xem `OVERLEAF_OIDC_PROVIDER_ID`).

Nếu không tìm thấy bản ghi phù hợp, cơ sở dữ liệu sẽ được tìm kiếm để tìm người dùng có địa chỉ email chính khớp với email trong hồ sơ người dùng của IdP:

* Nếu tìm thấy người dùng như vậy, trường `thirdPartyIdentifiers` sẽ được cập nhật.
* Nếu không tìm thấy người dùng phù hợp và tính năng tạo tài khoản JIT không bị tắt, một người dùng mới sẽ được tạo với địa chỉ email và `thirdPartyIdentifiers` lấy từ hồ sơ IdP.

Trong cả hai trường hợp, người dùng được coi là đã được 'liên kết' với người dùng OIDC bên ngoài. Người dùng có thể hủy liên kết với nhà cung cấp OIDC trên trang `/user/settings`.

#### Tìm các giá trị bằng tài liệu discovery

Mọi Nhà cung cấp OpenID (OP) đều công bố một tài liệu discovery tại `<issuer>/.well-known/openid-configuration`. Hãy sao chép các giá trị từ tài liệu này thay vì gõ tay; chỉ cần sai một ký tự là đủ khiến việc đăng nhập thất bại.

<Steps>
  <Step title="Tìm URL discovery">
    OP của bạn hiển thị URL này trên trang của client (provider) mà bạn đã tạo cho Overleaf. Trong Authentik, mở **Applications > Providers**, chọn provider và tìm **OpenID Configuration URL** và **OpenID Configuration Issuer**:

    <Frame caption="Authentik: URL discovery và issuer của một provider (phiên bản thử nghiệm)">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/oidc-authentik-provider.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=e3cb5603d959665472b53e39b58b2775" alt="" width="1280" height="633" data-path="images/on-premises/oidc-authentik-provider.png" />
    </Frame>

    URL thường có dạng như sau:

    * Keycloak: `https://keycloak.example.com/realms/<realm>/.well-known/openid-configuration`
    * Authentik: `https://authentik.example.com/application/o/<application-slug>/.well-known/openid-configuration`
  </Step>

  <Step title="Đọc các giá trị">
    Mở URL trong trình duyệt, hoặc chạy lệnh sau trên máy chủ Overleaf:

    ```shell theme={null}
    curl -s https://authentik.example.com/application/o/overleaf/.well-known/openid-configuration \
      | jq '{issuer, authorization_endpoint, token_endpoint, userinfo_endpoint, end_session_endpoint}'
    ```

    Phản hồi của Authentik có dạng như sau:

    ```json theme={null}
    {
      "issuer": "https://authentik.example.com/application/o/overleaf/",
      "authorization_endpoint": "https://authentik.example.com/application/o/authorize/",
      "token_endpoint": "https://authentik.example.com/application/o/token/",
      "userinfo_endpoint": "https://authentik.example.com/application/o/userinfo/",
      "end_session_endpoint": "https://authentik.example.com/application/o/overleaf/end-session/"
    }
    ```

    Authentik cũng liệt kê các URL này ở phía dưới trang provider:

    <Frame caption="Authentik: các endpoint của một provider (phiên bản thử nghiệm)">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/oidc-authentik-endpoints.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=55d130cbbbf27fd11aebafa1ab3f090c" alt="" width="1280" height="633" data-path="images/on-premises/oidc-authentik-endpoints.png" />
    </Frame>
  </Step>

  <Step title="Sao chép chúng vào `variables.env`">
    | Trường trong tài liệu discovery | Biến môi trường |
    | - | - |
    | `issuer` | `OVERLEAF_OIDC_ISSUER` |
    | `authorization_endpoint` | `OVERLEAF_OIDC_AUTHORIZATION_URL` |
    | `token_endpoint` | `OVERLEAF_OIDC_TOKEN_URL` |
    | `userinfo_endpoint` | `OVERLEAF_OIDC_USER_INFO_URL` |
    | `end_session_endpoint` | `OVERLEAF_OIDC_LOGOUT_URL` |

    ```dotenv theme={null}
    OVERLEAF_OIDC_ISSUER=https://authentik.example.com/application/o/overleaf/
    OVERLEAF_OIDC_AUTHORIZATION_URL=https://authentik.example.com/application/o/authorize/
    OVERLEAF_OIDC_TOKEN_URL=https://authentik.example.com/application/o/token/
    OVERLEAF_OIDC_USER_INFO_URL=https://authentik.example.com/application/o/userinfo/
    OVERLEAF_OIDC_LOGOUT_URL=https://authentik.example.com/application/o/overleaf/end-session/
    ```
  </Step>

  <Step title="Kiểm tra Overleaf có thể kết nối tới OP">
    Overleaf gọi các endpoint token và userinfo từ bên trong container của nó, vì vậy OP phải truy cập được từ đó, chứ không chỉ từ trình duyệt của bạn:

    ```shell theme={null}
    docker exec sharelatex curl -sS -o /dev/null -w "%{http_code}\n" \
      https://authentik.example.com/application/o/overleaf/.well-known/openid-configuration
    ```

    Lệnh này phải in ra `200`.
  </Step>
</Steps>

<Warning>
  Hãy sao chép `issuer` chính xác, bao gồm cả dấu gạch chéo ở cuối. Overleaf so sánh từng ký tự của nó với issuer trong ID token; bất kỳ khác biệt nào cũng khiến mọi lần đăng nhập OIDC thất bại với lỗi:

  `{"message":{"message":"ID token not issued by expected OpenID provider."}}`

  Trong Authentik, issuer thuộc về application (`.../application/o/<application-slug>/`). Đó không phải là địa chỉ của máy chủ Authentik, mặc dù các URL authorize, token và userinfo được dùng chung cho tất cả các application.
</Warning>

#### Biến môi trường

Giá trị của năm biến bắt buộc sau đây có thể được tìm thấy thông qua endpoint `.well-known/openid-configuration` của Nhà cung cấp OpenID (OP) của bạn, xem ở trên.

* `OVERLEAF_OIDC_ISSUER` <strong>(bắt buộc)</strong>
* `OVERLEAF_OIDC_AUTHORIZATION_URL` <strong>(bắt buộc)</strong>
* `OVERLEAF_OIDC_TOKEN_URL` <strong>(bắt buộc)</strong>
* `OVERLEAF_OIDC_USER_INFO_URL` <strong>(bắt buộc)</strong>
* `OVERLEAF_OIDC_LOGOUT_URL` <strong>(bắt buộc)</strong>

Giá trị của hai biến bắt buộc sau đây sẽ do quản trị viên OP của bạn cung cấp

* `OVERLEAF_OIDC_CLIENT_ID` <strong>(bắt buộc)</strong>
* `OVERLEAF_OIDC_CLIENT_SECRET` <strong>(bắt buộc)</strong>
* `OVERLEAF_OIDC_SCOPE`
  * Mặc định: `openid profile email`
* `OVERLEAF_OIDC_PROVIDER_ID`
  * ID tùy ý của OP, mặc định là `oidc`.
* `OVERLEAF_OIDC_PROVIDER_NAME`
  * Tên của OP, được dùng trong phần `Linked Accounts` của trang `/user/settings`, mặc định là `OIDC Provider`.
* `OVERLEAF_OIDC_IDENTITY_SERVICE_NAME`
  * Tên hiển thị của dịch vụ định danh, được dùng trên trang đăng nhập (mặc định: `Log in with $OVERLEAF_OIDC_PROVIDER_NAME`).
* `OVERLEAF_OIDC_PROVIDER_DESCRIPTION`
  * Mô tả về OP, được dùng trong phần `Linked Accounts` (mặc định: `Log in with $OVERLEAF_OIDC_PROVIDER_NAME`).
* `OVERLEAF_OIDC_PROVIDER_INFO_LINK`
  * URL `Learn more` trong phần mô tả OP; mặc định: không có liên kết `Learn more` trong phần mô tả.
* `OVERLEAF_OIDC_PROVIDER_HIDE_NOT_LINKED`
  * Không hiển thị OP trên trang `/user/settings` nếu tài khoản của người dùng chưa được liên kết với OP, mặc định là `false`.
* `OVERLEAF_OIDC_USER_ID_FIELD`
  * Giá trị của thuộc tính này sẽ được Overleaf dùng làm ID người dùng bên ngoài, mặc định là `id`. Các giá trị hợp lý khác có thể dùng là `email` và `username` (tương ứng với claim OIDC `preferred_username`).
* `OVERLEAF_OIDC_ALLOWED_EMAIL_DOMAINS`
  * Giới hạn việc tạo tài khoản Just-in-Time (JIT) cho người dùng xác thực qua OIDC. Nếu được đặt thành một danh sách tên miền phân tách bằng dấu phẩy, tài khoản mới sẽ chỉ được tạo nếu tên miền trong địa chỉ email của người dùng khớp với một trong các tên miền được liệt kê. Nếu tên miền không khớp, quản trị viên phải tạo tài khoản người dùng thủ công bằng địa chỉ email của người dùng OIDC, với một mật khẩu ngẫu nhiên mạnh hoặc tốt hơn là hoàn toàn không có trường `hashedPassword`. Tên miền có thể bắt đầu bằng ký tự đại diện `*.` để khớp với các tên miền con.
    * Ví dụ: Để cho phép tạo tài khoản JIT cho người dùng có địa chỉ email như `name@example.com` và `name@math.example.com`:\
      `OVERLEAF_OIDC_ALLOWED_EMAIL_DOMAINS=example.com, *.example.com`
    * Ví dụ: Để tắt hoàn toàn việc tạo tài khoản JIT:\
      `OVERLEAF_OIDC_ALLOWED_EMAIL_DOMAINS=`
* `OVERLEAF_OIDC_UPDATE_USER_DETAILS_ON_LOGIN`
  * Nếu được đặt thành `true`, các trường `first_name` và `last_name` của người dùng sẽ được cập nhật khi đăng nhập, và biểu mẫu thông tin người dùng trên trang `/user/settings` sẽ bị vô hiệu hóa.
* `OVERLEAF_OIDC_IS_ADMIN_FIELD` và `OVERLEAF_OIDC_IS_ADMIN_FIELD_VALUE`
  * Khi cả hai biến môi trường này đều được đặt, quá trình đăng nhập sẽ cập nhật `user.isAdmin = true` nếu hồ sơ do OP trả về chứa thuộc tính được chỉ định bởi `OVERLEAF_OIDC_IS_ADMIN_FIELD` và giá trị của nó hoặc khớp với `OVERLEAF_OIDC_IS_ADMIN_FIELD_VALUE`, hoặc là một mảng chứa `OVERLEAF_OIDC_IS_ADMIN_FIELD_VALUE` (ví dụ claim `groups`); nếu không, `user.isAdmin` sẽ được đặt thành `false`. Nếu `OVERLEAF_OIDC_IS_ADMIN_FIELD` là `email` thì giá trị của thuộc tính `emails[0].value` sẽ được dùng để kiểm tra khớp.

URL chuyển hướng cho Nhà cung cấp OpenID của bạn là `https://my-overleaf-instance.com/oidc/login/callback`.

<Accordion title="Tệp variables.env mẫu">
  ```dotenv title="variables.env" wrap 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

  #################
  ## OIDC for CE ##
  #################

  EXTERNAL_AUTH=oidc

  OVERLEAF_OIDC_PROVIDER_ID=oidc
  OVERLEAF_OIDC_ISSUER=https://keycloak.provider.com/realms/example
  OVERLEAF_OIDC_AUTHORIZATION_URL=https://keycloak.provider.com/realms/example/protocol/openid-connect/auth
  OVERLEAF_OIDC_TOKEN_URL=https://keycloak.provider.com/realms/example/protocol/openid-connect/token
  OVERLEAF_OIDC_USER_INFO_URL=https://keycloak.provider.com/realms/example/protocol/openid-connect/userinfo
  OVERLEAF_OIDC_LOGOUT_URL=https://keycloak.provider.com/realms/example/protocol/openid-connect/logout
  OVERLEAF_OIDC_CLIENT_ID=Overleaf-OIDC
  OVERLEAF_OIDC_CLIENT_SECRET=DoNotUseThisATGgaAcTgCcATgGATTACAagGtTCaGcGTAG
  OVERLEAF_OIDC_IDENTITY_SERVICE_NAME='Log in with Keycloak OIDC Provider'
  OVERLEAF_OIDC_PROVIDER_NAME=OIDC Keycloak Provider
  OVERLEAF_OIDC_PROVIDER_INFO_LINK=https://openid.net
  OVERLEAF_OIDC_IS_ADMIN_FIELD=email
  OVERLEAF_OIDC_IS_ADMIN_FIELD_VALUE=overleaf.admin@example.com
  OVERLEAF_OIDC_UPDATE_USER_DETAILS_ON_LOGIN=false
  ```
</Accordion>

## Hướng dẫn từng bước: goauthentik

Phần này hướng dẫn một cấu hình đã được kiểm thử với [goauthentik](https://goauthentik.io/). Hãy thay `https://overleaf.example.com` bằng `OVERLEAF_SITE_URL` của bạn và `https://authentik.example.com` bằng địa chỉ Authentik của bạn.

<Steps>
  <Step title="Tạo provider">
    Trong Authentik, mở **Applications > Providers**, nhấp **New Provider**, chọn **OAuth2/OpenID Provider** và nhấp **Next**.

    * **Client Type**: `Confidential`.
    * **Redirect URIs** (trong **Protocol settings**): thêm `https://overleaf.example.com/oidc/login/callback` với chế độ khớp `Strict`.
    * Sao chép ngay **Client ID** và **Client Secret** vào `OVERLEAF_OIDC_CLIENT_ID` và `OVERLEAF_OIDC_CLIENT_SECRET`.

    <Frame caption="Authentik: Client ID và Client Secret của một provider mới">
      <img src="https://mintcdn.com/ayakaleaf-pro/TjqwH4XzxYAkHStq/images/on-premises/oidc-authentik-create.png?fit=max&auto=format&n=TjqwH4XzxYAkHStq&q=85&s=f127a92eaf3063714e9af515fe97c263" alt="" width="1120" height="808" data-path="images/on-premises/oidc-authentik-create.png" />
    </Frame>

    <Warning>
      Authentik chỉ hiển thị client secret khi bạn tạo provider. Về sau, biểu mẫu chỉnh sửa chỉ cung cấp **Modify**, thao tác này sẽ thay secret bằng một secret mới.
    </Warning>
  </Step>

  <Step title="Tạo application">
    Mở **Applications > Applications**, tạo một application mới, đặt tên và slug cho nó, ví dụ `overleaf`, rồi chọn provider. Slug sẽ trở thành một phần của issuer: `https://authentik.example.com/application/o/overleaf/`.
  </Step>

  <Step title="Sao chép các URL">
    Làm theo phần [Tìm các giá trị bằng tài liệu discovery](#tìm-các-giá-trị-bằng-tài-liệu-discovery) ở trên để điền năm URL.
  </Step>

  <Step title="Ánh xạ quản trị viên (tùy chọn)">
    Authentik gửi các nhóm của người dùng dưới dạng claim `groups`, là một mảng. Để các thành viên của nhóm Authentik `Admins` trở thành quản trị viên Overleaf:

    ```dotenv theme={null}
    OVERLEAF_OIDC_IS_ADMIN_FIELD=groups
    OVERLEAF_OIDC_IS_ADMIN_FIELD_VALUE=Admins
    ```

    <Warning>
      Cờ quản trị viên được cập nhật ở mỗi lần đăng nhập OIDC. Nếu trường hoặc giá trị sai, mọi quản trị viên đăng nhập qua OIDC sẽ mất quyền quản trị, kể cả quản trị viên được tạo trong launchpad. Hãy thử nghiệm việc ánh xạ bằng một tài khoản quản trị viên thứ hai trước.
    </Warning>
  </Step>
</Steps>

<Accordion title="Tệp variables.env đã kiểm thử cho goauthentik">
  ```dotenv title="variables.env" wrap theme={null}
  EXTERNAL_AUTH=oidc
  OVERLEAF_OIDC_PROVIDER_ID=authentik
  OVERLEAF_OIDC_IDENTITY_SERVICE_NAME=Log in with Authentik
  OVERLEAF_OIDC_ISSUER=https://authentik.example.com/application/o/overleaf/
  OVERLEAF_OIDC_AUTHORIZATION_URL=https://authentik.example.com/application/o/authorize/
  OVERLEAF_OIDC_TOKEN_URL=https://authentik.example.com/application/o/token/
  OVERLEAF_OIDC_USER_INFO_URL=https://authentik.example.com/application/o/userinfo/
  OVERLEAF_OIDC_LOGOUT_URL=https://authentik.example.com/application/o/overleaf/end-session/
  OVERLEAF_OIDC_CLIENT_ID=<Client ID>
  OVERLEAF_OIDC_CLIENT_SECRET=<Client Secret>
  OVERLEAF_OIDC_USER_ID_FIELD=username
  OVERLEAF_OIDC_IS_ADMIN_FIELD=groups
  OVERLEAF_OIDC_IS_ADMIN_FIELD_VALUE=Admins
  OVERLEAF_OIDC_UPDATE_USER_DETAILS_ON_LOGIN=true
  ```
</Accordion>


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