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

# Gitea Sync User Credentials Collection

`giteaSyncUserCredentials` holds one document per user who linked a Gitea account for Gitea Sync. It stores the user's OAuth tokens, encrypted. It belongs to the `gitea-sync` web module (`services/web/modules/gitea-sync`). The schema is in `services/web/modules/gitea-sync/app/models/giteaSyncUserCredentials.mjs`, and all access goes through `services/web/modules/gitea-sync/app/src/TokenManager.mjs`.

#### Fields

| Field | Type | Meaning | Source |
| - | - | - | - |
| `_id` | ObjectId | Set by Mongoose when the upsert inserts. | |
| `userId` | ObjectId | The Overleaf user. Required. The schema says `unique: true`, but no index is built (see Indexes); the upsert by `userId` keeps one document per user. | `giteaSyncUserCredentials.mjs:8`, `TokenManager.mjs:89-96` |
| `gitea` | String | The JSON `{ accessToken, refreshToken, expiresAt }`, encrypted with `@overleaf/access-token-encryptor`. `expiresAt` is in milliseconds since the epoch, or `null`; `refreshToken` may be `null`. The password comes from `GITEA_TOKEN_CIPHER_PASSWORD`; the cipher label is `GITEA_TOKEN_CIPHER_LABEL`, default `2026.10-v3`. Required. | `giteaSyncUserCredentials.mjs:9-10`, `TokenManager.mjs:17-30`, `services/web/modules/gitea-sync/app/src/GiteaApiClient.mjs:198-209` |
| `__v` | Number | The Mongoose version key. The schema does not turn it off. | Mongoose default |

#### Who Writes It

| Code | What it does |
| - | - |
| `TokenManager.mjs:89-96` (`saveUserToken`) | Upserts the encrypted credentials for the user. Called from the OAuth callback (`services/web/modules/gitea-sync/app/src/GiteaSyncController.mjs:146`). Unlike GitLab Sync, linking again does not revoke the replaced token. |
| `TokenManager.mjs:53-79` (`refreshUserToken`) | When the access token is less than 5 minutes from `expiresAt` (`:12`), it gets new tokens with the refresh token and saves them (`:76`). It runs under the lock `giteaSyncToken` per user (`:14,54`), because Gitea replaces the refresh token on use. |
| `TokenManager.mjs:100-102` (`removeUserToken`) | Deletes the document. Gitea has no API to revoke an OAuth token, so the user revokes the grant in Gitea (`:98-99`). Called on unlink (`GiteaSyncController.mjs:173`) and when a deleted user expires (`services/web/modules/gitea-sync/index.mjs:56-58`). |

#### Who Reads It

| Code | What it does |
| - | - |
| `TokenManager.mjs:40-44,82-86` (`getUserCredentials`, `getUserToken`) | Decrypts the credentials for every Gitea API call, refreshing first when the token is about to expire: `services/web/modules/gitea-sync/app/src/GiteaSyncHandler.mjs` (for example `:27`) and `services/web/modules/gitea-sync/app/src/GitMerge.mjs:52`. With no document, or credentials that cannot be decrypted or refreshed, it throws `InvalidTokenError`, and the user counts as not linked (`GiteaSyncHandler.mjs:25-37`). |
| `services/web/modules/admin-tools/app/src/AdminUserListManager.mjs:395` | Checks whether the user has a document, for the integrations shown in the admin panel. |

#### Indexes

| Name | Keys | Options | Migration |
| - | - | - | - |
| `_id_` | `_id: 1` | | default |

Mongoose `autoIndex` is off (`services/web/app/src/infrastructure/Mongoose.mjs:7`), so the schema's `unique: true` on `userId` creates nothing. No migration creates indexes for this collection.

#### Notes

* Changing `GITEA_TOKEN_CIPHER_PASSWORD` makes the stored credentials unreadable. Those users then count as not linked until they link again.
* A deleted user keeps the document until the user expires.


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