Skip to content

Conversation

@digikwal
Copy link

Error 401 and 400 when connecting Infisical self-hosted to GitLab self-hosted

Fixes #4569


Summary

This PR fixes an issue where the GitLab OAuth flow always defaulted to https://gitlab.com, even when the user entered a self-hosted GitLab instance URL.

During the OAuth redirect, the frontend serializes form data into localStorage.
However, GitLabFormData did not explicitly define credentials.instanceUrl, so the value was dropped during serialization/deserialization.
As a result, the backend received no instanceUrl, and the OAuth exchange attempted to authenticate against gitlab.com, causing:

Failed to exchange OAuth code: 401 invalid_client

Root Cause

  • Credentials is pulled from the GitLab connection type,
  • But ONLY the fields used after the OAuth flow (code, refreshToken, accessToken, etc.).
  • InstanceUrl is NOT part of TGitLabConnection.credentials until after validation.

So when the frontend parses the callback payload using this type, Zod or TS validation strips unknown fields, including:

credentials.instanceUrl → removed

This matches our observation:

  • The OAuth redirect validly uses our self-hosted GitLab URL
  • But the POST to /api/v1/app-connections/gitlab always lacks instanceUrl
  • Backend falls back to gitlab.com
  • Token exchange fails with: "invalid_client" (because the client ID is for our host, not gitlab.com)

This means:

  • GitLabFormData was defined as:

    Pick<TGitLabConnection, "name" | "method" | "description" | "credentials">
  • TGitLabConnection["credentials"] is a discriminated union, and in the OAuth case does not include instanceUrl.

  • When the form data was stored in localStorage, the missing field caused:

    • credentials.instanceUrlundefined
    • backend fell back to default GitLab URL → https://gitlab.com/

This caused all OAuth token exchanges to fail for self-hosted GitLab.


Fix

GitLabFormData is rewritten to explicitly include the OAuth credential structure:

export type GitLabFormData = BaseFormData &
  Pick<TGitLabConnection, "name" | "method" | "description"> & {
    credentials: {
      code: string;
      instanceUrl?: string;
    };
  };

This ensures:

  • The instance URL entered on the form is stored.
  • The callback handler receives it.
  • The backend calls the correct instanceUrl/oauth/token.

Testing

Before fix

  1. Enter self-hosted GitLab URL: https://ops-gitlab.example.com

  2. Start OAuth flow.

  3. Callback logs show:

    gitlabInstanceUrl: https://gitlab.com
    
  4. OAuth token exchange fails with 401.

After fix

  1. Enter same self-hosted GitLab URL.

  2. Start OAuth flow.

  3. Callback logs show:

    gitlabInstanceUrl: https://ops-gitlab.example.com
    
  4. OAuth token exchange succeeds.

  5. Connection is created.


Impact

  • Fixes OAuth authentication for all self-hosted GitLab deployments.
  • No breaking changes.
  • Affects GitLab OAuth flow and PAT-based connections.

@maidul98
Copy link
Collaborator

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 11, 2025

Greptile Overview

Greptile Summary

Fixed OAuth authentication for self-hosted GitLab instances by explicitly preserving instanceUrl in the form data type definition. Previously, the type used Pick<TGitLabConnection, "credentials">, which only included post-validation OAuth credential fields (accessToken, refreshToken) but not instanceUrl, causing it to be stripped during localStorage serialization and defaulting to gitlab.com.

Key Changes:

  • Redefined GitLabFormData.credentials to explicitly include code and instanceUrl fields needed during the OAuth flow
  • instanceUrl is now properly persisted through localStorage and passed to backend OAuth token exchange
  • Backend already had security validation via blockLocalAndPrivateIpAddresses() at gitlab-connection-fns.ts:46

Impact:

  • Fixes 401 "invalid_client" errors when authenticating self-hosted GitLab instances via OAuth
  • No breaking changes to existing functionality

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The fix is surgical and correct - it explicitly defines the OAuth credential structure needed during the callback flow, ensuring instanceUrl is preserved through localStorage serialization. The backend already validates URLs via blockLocalAndPrivateIpAddresses(), and the change aligns perfectly with the existing TGitLabConnection type structure. No security concerns or edge cases identified.
  • No files require special attention

Important Files Changed

File Analysis

Filename Score Overview
frontend/src/pages/organization/AppConnections/OauthCallbackPage/OauthCallbackPage.types.ts 5/5 Fixed type definition to explicitly preserve instanceUrl during OAuth callback flow for self-hosted GitLab instances

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, no comments

Edit Code Review Agent Settings | Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error 401 and 400 when connecting infisical self-hosted to gitlab self-hosted

2 participants