Failed to authenticate with login 'fade' using Gitlab OAuth

Environment

Version: 10.6.0.92116 (via .zip)
Reverse Proxy: haproxy
Gitlab: 16.11.6
MFA: Yes
API Scope: read_user

Problem

I’m trying to login to SonarQube using Gitlab OAuth. I think everything is setup correctly, but I’m getting this error:

Failed to authenticate with login 'fade'

I did change the externalProvider and login and externalIdentity are matching:

{
  "paging": {
    "pageIndex": 1,
    "pageSize": 50,
    "total": 1
  },
  "users": [
    {
      "login": "fade",
      "name": "Fabian Dellwing",
      "active": true,
      "email": "<redacted>",
      "groups": [
        "sonar-administrators",
        "sonar-users"
      ],
      "tokensCount": 1,
      "local": false,
      "externalIdentity": "fade",
      "externalProvider": "gitlab",
      "avatar": "bf453be4813e295f546bb2f95779ce0a",
      "lastConnectionDate": "2024-07-17T10:55:09+0000",
      "managed": false
    }
  ]
}

Debuglog is not much of a help either, it just says the same as the UI:

2024.07.17 11:07:57 DEBUG web[5c9a976c-5cba-4ed8-b531-178e30c1c651][auth.event] login failure [cause|Failed to authenticate with login 'fade'][method|OAUTH2][provider|EXTERNAL|GitLab][IP|127.0.0.1|][login|fade]

Hi,

Welcome to the community!

I suspect SonarQube is giving you all the information it has in that log line.

What, if anything, do you see on the GitLab side?

 
Ann

Gitlab is out of the loop at that point. The authentication succeeded and has redirected back to SonarQube. SonarQube should at this point use the code it gets to login my user, but it doesn’t.

P.S. We use Gitlab OAuth in many places and never had any problem, so I would guarantee that this is not a Gitlab issue.

Hi,

Could you share how you know that the authentication succeeded?

 
Thx,
Ann

Gitlab will not redirect me back to SonarQube if authentication fails, it will tell me that authentication failed. That is exactly how OAuth works. I mean authentication is handled by Gitlab, I have no idea what SonarQube does for it to be even possible to fail there.

So, I got TRACE logs from SonarQube:

2024.07.18 13:51:48 TRACE web[f97cbe6b-384e-4d89-be61-8c055b16835f][o.s.s.p.w.UserSessionFilter] Thread[http-nio-0.0.0.0-9000-exec-90,5,main] serves /oauth2/callback/gitlab
2024.07.18 13:51:48 TRACE web[f97cbe6b-384e-4d89-be61-8c055b16835f][sql] time=1ms | sql=select p.prop_key as "key", p.is_empty as empty, p.text_value as textValue, p.clob_value as clobValue, p.entity_uuid as entityUuid, p.user_uuid as userUuid from properties p where p.prop_key=? and p.entity_uuid is null and p.user_uuid is null | params=sonar.forceRedirectOnDefaultAdminCredentials
2024.07.18 13:51:48 TRACE web[f97cbe6b-384e-4d89-be61-8c055b16835f][sql] time=1ms | sql=select p.prop_key as "key", p.is_empty as empty, p.text_value as textValue, p.clob_value as clobValue, p.entity_uuid as entityUuid, p.user_uuid as userUuid from properties p where p.prop_key=? and p.entity_uuid is null and p.user_uuid is null | params=sonar.auth.gitlab.enabled
2024.07.18 13:51:48 TRACE web[f97cbe6b-384e-4d89-be61-8c055b16835f][sql] time=0ms | sql=select p.prop_key as "key", p.is_empty as empty, p.text_value as textValue, p.clob_value as clobValue, p.entity_uuid as entityUuid, p.user_uuid as userUuid from properties p where p.prop_key=? and p.entity_uuid is null and p.user_uuid is null | params=sonar.auth.gitlab.applicationId.secured
2024.07.18 13:51:48 TRACE web[f97cbe6b-384e-4d89-be61-8c055b16835f][sql] time=0ms | sql=select p.prop_key as "key", p.is_empty as empty, p.text_value as textValue, p.clob_value as clobValue, p.entity_uuid as entityUuid, p.user_uuid as userUuid from properties p where p.prop_key=? and p.entity_uuid is null and p.user_uuid is null | params=sonar.auth.gitlab.secret.secured
2024.07.18 13:51:48 TRACE web[f97cbe6b-384e-4d89-be61-8c055b16835f][sql] time=0ms | sql=select p.prop_key as "key", p.is_empty as empty, p.text_value as textValue, p.clob_value as clobValue, p.entity_uuid as entityUuid, p.user_uuid as userUuid from properties p where p.prop_key=? and p.entity_uuid is null and p.user_uuid is null | params=sonar.core.serverBaseURL
2024.07.18 13:51:48 TRACE web[f97cbe6b-384e-4d89-be61-8c055b16835f][sql] time=0ms | sql=select p.prop_key as "key", p.is_empty as empty, p.text_value as textValue, p.clob_value as clobValue, p.entity_uuid as entityUuid, p.user_uuid as userUuid from properties p where p.prop_key=? and p.entity_uuid is null and p.user_uuid is null | params=sonar.auth.gitlab.groupsSync
2024.07.18 13:51:48 TRACE web[f97cbe6b-384e-4d89-be61-8c055b16835f][sql] time=1ms | sql=select p.prop_key as "key", p.is_empty as empty, p.text_value as textValue, p.clob_value as clobValue, p.entity_uuid as entityUuid, p.user_uuid as userUuid from properties p where p.prop_key=? and p.entity_uuid is null and p.user_uuid is null | params=sonar.auth.gitlab.url
2024.07.18 13:51:48 TRACE web[f97cbe6b-384e-4d89-be61-8c055b16835f][sql] time=2ms | sql=SELECT sa.scm_account as "scm_account", u.uuid as uuid, u.login as login, u.name as name, u.email as email, u.active as "active", u.salt as "salt", u.crypted_password as "cryptedPassword", u.hash_method as "hashMethod", u.external_id as "externalId", u.external_login as "externalLogin", u.external_identity_provider as "externalIdentityProvider", u.user_local as "local", u.reset_password as "resetPassword", u.homepage_type as "homepageType", u.homepage_parameter as "homepageParameter", u.last_connection_date as "lastConnectionDate", u.last_sonarlint_connection as "lastSonarlintConnectionDate", u.created_at as "createdAt", u.updated_at as "updatedAt" FROM users u left outer join scm_accounts sa on sa.user_uuid = u.uuid WHERE u.external_id=? AND u.external_identity_provider=? | params=34, gitlab
2024.07.18 13:51:48 TRACE web[f97cbe6b-384e-4d89-be61-8c055b16835f][sql] time=2ms | sql=SELECT sa.scm_account as "scm_account", u.uuid as uuid, u.login as login, u.name as name, u.email as email, u.active as "active", u.salt as "salt", u.crypted_password as "cryptedPassword", u.hash_method as "hashMethod", u.external_id as "externalId", u.external_login as "externalLogin", u.external_identity_provider as "externalIdentityProvider", u.user_local as "local", u.reset_password as "resetPassword", u.homepage_type as "homepageType", u.homepage_parameter as "homepageParameter", u.last_connection_date as "lastConnectionDate", u.last_sonarlint_connection as "lastSonarlintConnectionDate", u.created_at as "createdAt", u.updated_at as "updatedAt" FROM users u left outer join scm_accounts sa on sa.user_uuid = u.uuid WHERE u.external_login=? AND u.external_identity_provider=? | params=fade, gitlab
2024.07.18 13:51:48 DEBUG web[f97cbe6b-384e-4d89-be61-8c055b16835f][auth.event] login failure [cause|Failed to authenticate with login 'fade'][method|OAUTH2][provider|EXTERNAL|GitLab][IP|127.0.0.1|][login|fade]

Is there any way how to check what the value of external_id is? It is not in the output of /api/users/search.

Hi,

I’m pretty sure this is your external_id:

Is it possible for you to run those DB queries directly?

 
Ann

From my understanding that is my external_login (as seen in the 2nd SQL query).

I also already tried setting my externalIdentity to 34 without success.

I’ll see if I can get access to the SQL database directly.

So, I was able to solve the problem after directly accessing the database.

Running this query:

SELECT sa.scm_account as "scm_account", u.uuid as uuid, u.login as login, u.name as name, u.email as email, u.active as "active", u.salt as "salt", u.crypted_password as "cryptedPassword", u.hash_method as "hashMethod", u.external_id as "externalId", u.external_login as "externalLogin", u.external_identity_provider as "externalIdentityProvider", u.user_local as "local", u.reset_password as "resetPassword", u.homepage_type as "homepageType", u.homepage_parameter as "homepageParameter", u.last_connection_date as "lastConnectionDate", u.last_sonarlint_connection as "lastSonarlintConnectionDate", u.created_at as "createdAt", u.updated_at as "updatedAt" FROM users u left outer join scm_accounts sa on sa.user_uuid = u.uuid WHERE u.external_login='fade' AND u.external_identity_provider='gitlab';

gives us this clear result:

 scm_account |    uuid    | login |      name       |   email    | active | salt | cryptedPassword | hashMethod | externalId | externalLogin | externalIdentityProvider | local | resetPassword | homepageType | homepageParameter | lastConnectionDate | lastSonarlintConnectionDate |   createdAt   |   updatedAt   
-------------+--------------------------------------+-------+-----------------+----------------------------------+--------+------+-----------------+------------+------------+---------------+--------------------------+-------+---------------+--------------+-------------------+--------------------+-----------------------------+---------------+---------------
             | <redacted> | fade  | Fabian Dellwing | <redacted> | t      |      |                 | PBKDF2     | fade       | fade          | gitlab                   | f     | f             |              |                   |      1721310511377 |                             | 1721206438485 | 1721310927655
(1 row)

The problem is very obvious. The field external_id is not matching up with the ID from Gitlab (34). After setting this field to 34:

sonar=> UPDATE users set external_id = 34 where login = 'fade';
UPDATE 1

Login now works as expected.


They aftermath:

This documentation is completely useless: GitLab authentication

It misses so many important information:

  • That you need to run an API call to migrate users to a new authentication method. This fact is only mentioned very briefly in the LDAP documentation.
  • Even if one would find that in the LDAP documentation, that paragraph is so short that without further searching (and landing in this forum) no one has any idea what parameters that API takes.
  • If you are able to find what it takes to migrate ones authentication method and successfully do it, even then it still does not work because of some external_id field in the database that you can’t even see with api/users/search so you have no idea it even exists.
  • All of this should be accessible directly from the user management itself. It is a simple popup with a dropdown and 2 input fields. Than you can do a very short documentation because UI works intuitively.

Hi,

I’m glad you worked through this and thanks for sharing your steps.

As you’ve noticed, ur docs are focused more on from-scratch setup, othan migration. There is this guide, which I assumed you’d already found based on your initial comment about having updated externalProvider &etc.

And I’ll flag this for more attention internally. Thanks for the feedback.

 
Ann