diff --git a/packages/server/src/utils/providers/gitlab.ts b/packages/server/src/utils/providers/gitlab.ts index 6cb777b75..1221fd5a4 100644 --- a/packages/server/src/utils/providers/gitlab.ts +++ b/packages/server/src/utils/providers/gitlab.ts @@ -11,7 +11,7 @@ import { TRPCError } from "@trpc/server"; import { quote } from "shell-quote"; import type { z } from "zod"; -export const refreshGitlabToken = async (gitlabProviderId: string) => { +const refreshGitlabTokenIfExpired = async (gitlabProviderId: string) => { const gitlabProvider = await findGitlabById(gitlabProviderId); const currentTime = Math.floor(Date.now() / 1000); @@ -56,6 +56,23 @@ export const refreshGitlabToken = async (gitlabProviderId: string) => { return data; }; +// GitLab invalidates a refresh token when it is used, so concurrent callers must share one refresh +const pendingRefreshes = new Map< + string, + ReturnType +>(); + +export const refreshGitlabToken = (gitlabProviderId: string) => { + let refresh = pendingRefreshes.get(gitlabProviderId); + if (!refresh) { + refresh = refreshGitlabTokenIfExpired(gitlabProviderId).finally(() => { + pendingRefreshes.delete(gitlabProviderId); + }); + pendingRefreshes.set(gitlabProviderId, refresh); + } + return refresh; +}; + export const haveGitlabRequirements = (gitlabProvider: Gitlab) => { return !!(gitlabProvider?.accessToken && gitlabProvider?.refreshToken); };