* github-sync: only swallow permanent revocation failures during unlink 404 (token gone) and 422 (token issued under a mismatched client_id, the confirmed cause of #35108) are treated as safe to proceed past; any other revocation error now aborts the unlink instead of silently deleting the local credential row, so a token that might still be live isn't orphaned by a transient GitHub failure. * web: surface genuine github-sync unlink failures instead of a 500 When GithubSyncApiHandler.unlink rejects for a reason other than the permanent 404/422 cases now handled by github-sync, show an error notification on the settings page instead of crashing the request, mirroring the existing projectSyncSuccessMessage session-flash pattern. * github-sync: detect expired credentials in user status check getStatus now validates a stored token against GitHub (GET /user) before reporting it as linked. A GithubInvalidCredentialsError (401) now surfaces as expired: true alongside enabled: true, distinguishing a dead token from a genuinely working link. Any other validation failure fails open and reports enabled: true as before. * web: propagate github-sync expired status through to the client Thread the new expired field from github-sync's status endpoint through GithubSyncController.getUserStatus and the settings-page injectUserSettings middleware, and extend the corresponding TypeScript types on both the app and frontend side. * web: show a re-link path on the settings widget when github credentials expire The widget now checks expired status: it shows an error indicator with a "connection expired" message and renders the Link action instead of a dead-end Unlink button, since relinking upserts a fresh token regardless of the stale row. * web: keep unlink available for expired github credentials Some users just want to remove an expired connection rather than relink it. Keep Unlink as the primary action regardless of expired status, and surface the relink path as an inline link next to the expired message instead of replacing the primary action. * web: correct the github unlink confirmation warning Unlinking never touches a project's githubSyncProjectStates row, regardless of whether the credential was healthy or expired - verified by tracing unlinkAccount/deleteUser and confirming no other code path deletes project sync state on account unlink. Sync pauses until relink, but no project loses its GitHub connection, so the warning text was simply wrong; corrected rather than adding a separate expired-specific message. * web: add Storybook story for expired github-sync credentials Covers the new expired state alongside the existing NoFeature, Unlinked, Linked and Error stories for the settings widget. * simplify: collapse duplicated branches and extract session-flash helper - Merge the near-identical 404/422 checks in _revokeToken into one status-set check. - Use optional chaining instead of null-guard ternaries in GithubSyncMiddleware. - Extract popSessionValue() in UserPagesController to de-duplicate the read-then-delete session flash-message pattern, now used 3 times. * review-self: close test coverage gaps found in self-review - AuthenticationController: cover 401/403 revocation responses and a plain network error with no .response, alongside existing 404/422/500. - GithubSyncController: assert projectSyncErrorMessage stays unset on the unlink success path. - UserPagesController: cover projectSyncErrorMessage set-then-cleared and absent-stays-undefined cases. - Add GithubSyncMiddleware.test.mjs (previously untested): not-logged-in, successful status fetch, and callback-error branches. - Cypress: cover cancelling the unlink confirmation modal while expired. - Storybook: add ErrorWhileUnlinked story for the disabled-Link + error-indicator combination. * review-self: fix audit-log ordering, cache getStatus validation safely - GithubSyncApiHandler: write the link-github/unlink-github audit log entry only after the corresponding github-sync API call succeeds, instead of before (resolves the discrepancy tracked in issue 3354). - UserController/UserManager: cache getStatus's live token-validation result for 5 minutes to cut down on redundant GitHub API calls, with an optimistic-lock write (matched against the encrypted token that was actually validated) so a relink can never be overwritten by a stale in-flight validation of the old token. - github-integration-linking-widget: dedupe the relink href against the linkPath already passed to IntegrationLinkingWidget. * web: surface expired github credentials proactively in the sync modal The modal previously only showed the reauthorize prompt after a real GitHub API call failed with 401 mid-action. It now also checks the expired field already returned by the status endpoint and shows the same prompt immediately on open, before ever rendering the export form against a token known to be dead. Extracted the existing reauthorize markup into GithubCredentialsExpired so both call sites share it. * github-sync: avoid double-fetching the credential doc in getStatus getStatus called UserManager.getUser, then getAuthToken which internally fetched the same document again. Add getAuthTokenFromUser to decrypt from an already-fetched doc and use it in getStatus, removing the redundant Mongo query and the now-unreachable GithubUserNotLinkedError handling (the user doc's existence was already confirmed by the first fetch). * github-sync: make expired-token handling reactive, drop proactive getStatus validation Reverts getStatus in the github-sync service to a pure DB check — no live GitHub API call, no 5-minute cache, no token_expired/token_validated_at fields. The expired state is already surfaced reactively: the unmerged-commits fetch on modal open returns 401 when the token is dead (handled by the existing case 401 branch), and ExportToGithub handles 401 from the orgs call. The proactive check added complexity (caching, optimistic-lock writes) without a meaningful UX gain over the reactive paths. Removes the expired indicator from the settings widget and the proactive GithubCredentialsExpired screen from the modal's pre-flight check. The github_sync_expired locale key is removed as it is no longer referenced. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * github-sync: show reauthorize prompt when push/pull fails with expired credentials When a merge (push or pull) returns 401, GithubSyncMergeAction now renders GithubCredentialsExpired with the Reauthorize button instead of the raw "GithubInvalidCredentialsError" string that was previously falling through to the default error branch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * github-sync: wrap all async controller handlers with expressify Without expressify, an unhandled throw from an async route handler (e.g. a token decryption failure in the github-sync service producing a non-4xx error that _reportError re-throws) becomes an unhandled rejection. Express never sends a response and the modal is left stuck on loading. expressify catches the error and passes it to next(), so Express's error handler sends a 500 and the frontend can surface it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * removing redundant tests * removing getAuthTokenFromUser function * removing redundant key --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> GitOrigin-RevId: 0d9a314bf70bea9e6037593f2a6f8b8d1f2c6e71
An open-source online real-time collaborative LaTeX editor.
Wiki • Server Pro • Contributing • Mailing List • Authors • License
Figure 1: A screenshot of a project being edited in Overleaf Community Edition.
Community Edition
Overleaf is an open-source online real-time collaborative LaTeX editor. We run a hosted version at www.overleaf.com, but you can also run your own local version, and contribute to the development of Overleaf.
Caution
Overleaf Community Edition is intended for use in environments where all users are trusted. Community Edition is not appropriate for scenarios where isolation of users is required due to Sandbox Compiles not being available. When not using Sandboxed Compiles, users have full read and write access to the
sharelatexcontainer resources (filesystem, network, environment variables) when running LaTeX compiles.
For more information on Sandbox Compiles check out our documentation.
Enterprise
If you want help installing and maintaining Overleaf in your lab or workplace, we offer an officially supported version called Overleaf Server Pro. It also includes more features for security (SSO with LDAP or SAML), administration and collaboration (e.g. tracked changes). Find out more!
Keeping up to date
Sign up to the mailing list to get updates on Overleaf releases and development.
Installation
We have detailed installation instructions in the Overleaf Toolkit.
Upgrading
If you are upgrading from a previous version of Overleaf, please see the Release Notes section on the Wiki for all of the versions between your current version and the version you are upgrading to.
Overleaf Docker Image
This repo contains two dockerfiles, Dockerfile-base, which builds the
sharelatex/sharelatex-base image, and Dockerfile which builds the
sharelatex/sharelatex (or "community") image.
The Base image generally contains the basic dependencies like wget, plus texlive.
We split this out because it's a pretty heavy set of
dependencies, and it's nice to not have to rebuild all of that every time.
The sharelatex/sharelatex image extends the base image and adds the actual Overleaf code
and services.
Use make build-base and make build-community from server-ce/ to build these images.
We use the Phusion base-image
(which is extended by our base image) to provide us with a VM-like container
in which to run the Overleaf services. Baseimage uses the runit service
manager to manage services, and we add our init-scripts from the server-ce/runit
folder.
Contributing
Please see the CONTRIBUTING file for information on contributing to the development of Overleaf.
Authors
License
The code in this repository is released under the GNU AFFERO GENERAL PUBLIC LICENSE, version 3. A copy can be found in the LICENSE file.
Copyright (c) Overleaf, 2014-2025.
