Repository: conan-io/conan

Issue:
Compatibility with keyring (libsecret) for user login credentials
It would be nice if I could store my credentials for a remote into a keychain such as `libsecret` like I can do with `git`.
[Question] How to use two factor authentication (2FA) like Google Authenticator with Conan?
Hi @memsharded and @lasote ,

I have a question concerning 2FA with conan.
Especially in enterprise environments a 2 factor authentication is often used to secure the access to various applications (like JIRA, Artifactory, etc.).
I didn't find any information about 2FA@conan.

Are there any experiences with Google Authenticator or similar applications (Microsoft Authenticator, FreeOTP, Keepass/KeyOTP, etc.) and how to use them with conan? 
If not, is there any feature planned?

Thanks in advance.
Best Aalmann




Discussion:
I have changed the title, as keychain is more related to iOS apps, which does not relate here.

There is a python interface to libsecret thanks to Gnome: https://developer.gnome.org/libsecret/0.16/py-examples.html, which might be useful for this.

It sounds a bit very specific solution for one platform, I don't know if that will work in other Linuxes, for example, this should be checked.

Also, what would be the interface? Users would be able to opt-in to this? I don't think it should be enabled by default. How the interface would look like?


A section in Conan config `credential` with an entry `helper` which points to an executable that can receive commands to store and retrieve credentials. Absent by default which leads to no attempt by Conan to store/retrieve credentials.

It seems that if the command line interface is well defined for Conan credential helpers, it could be multiplatform and not even specific to keyring implementations of credential management.
> It seems that if the command line interface is well defined for Conan credential helpers, it could be multiplatform and not even specific to keyring implementations of credential management.

No, I don't mean the interface to the library :)

I meant the user interface. How the user should tell conan, in the command line, in ``conan.conf``, with an env-var... how to opt-in to store the credentials in the keyring. What would be the process to remove them from the keyring, if any (also, from the user point of view, not from the code)
Opt in is just specifying a credential helper.

I would say using the same interface as Git would be a good starting point: https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage
Ok, thanks for the feedback. That would be the ``conan.conf`` file then (which can be set with ``conan config`` too).

But I have checked it and bad news:
- There is no python package in PyPI for it: https://pypi.org/search/?q=libsecret. Not having it is almost a blocker, we cannot add dependencies to Conan that are not python packages, otherwise redistribution, creation of the installers, etc., becomes a nightmare.
- The python binding that comes with the linux system package in https://developer.gnome.org/libsecret/0.16/py-examples.html, seems to come licensed by a mixture of licenses, but including GPL terms. So conan (MIT) cannot use it.

Lets wait for feedback, to check if someone comes up with another solution that makes implementation reasonable. 

Without these, I think it would be a lot of work for the value, and my recommendation would be to not implement this feature at this moment.


Hi colleagues, 
Have you checked if `keyring` module fits your needs? (https://pypi.org/project/keyring/)
The use case that @Lawrencemm mentions can be covered up since it is able to use many different backends (depending on the OS but, from MacOS Keychain to Windows credential manager) or even adapt a customized one (https://pypi.org/project/keyring/#write-your-own-keyring-backend). BTW This module is licensed under MIT License terms, so may works. 

It is not that clear to me if he was really asking to provide also external connectivity to one developed by him. If so, i don't either see how to provide an interface for it.

Hi @alacasta 

Thanks for that pointer, very interesting, and it could facilitate things.
Still, I am seeing that in order to use it in mainstream systems as Ubuntu, the following is needed:

```bash
$ sudo apt install python3-venv libdbus-glib-1-dev
...
$ pip install secretstorage dbus-python
$ pip install keyring
```

So it would be very difficult to provide a fully automated pip install, but the users would still need to install those things from apt.

Maybe this would be a good candidate for a conan plugin (or maybe a hook? cc @jgsogo @danimtb)
Hi @memsharded, 

Not really sure if all this stuff is needed. As far as I remember from my experience using it on Ubuntu 16 was that worked out of the box. I think that all these recommendations were suggested for using it on Virutual Environments. Isn't it?
I think that is enough for this year so, happy new year Conan colleagues, you are doing an incredible job!!
In Windows it worked ok with the Windows Vault backend. In Linux:

```bash
>>> import keyring
>>> keyring.set_password("conan-test", "myuser", "mypassword")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/memsharded/venvs/conan27/local/lib/python2.7/site-packages/keyring/core.py", line 63, in set_password
    _keyring_backend.set_password(service_name, username, password)
  File "/home/memsharded/venvs/conan27/local/lib/python2.7/site-packages/keyring/backends/fail.py", line 23, in get_password
    raise RuntimeError(msg)
RuntimeError: No recommended backend was available. Install the keyrings.alt package if you want to use the non-recommended backends. See README.rst for details.
```

Also, I see there is no interface to get a user-password pair, only to query the password for a given user, but the user still needs to be defined. So not possible to automatically get the username, only a cached password.

With the above, if this will be considered, I now balance 99% towards doing this in a plugin/hook
And happy new year too @alacasta ! :) :) 
Hi! Happy New Year! 🎆 

[IMO] If there are some packages/libraries/applications that need to be installed (`apt`, `pip`, `msi`,... or licenses) we should think about a plugin, not a hook. At this moment hooks are functions that get executed associated with different stages or steps related to the recipe workflow, and getting the credentials for a remote it is not about the package workflow (although it is needed).

Thinking about plugins, we haven't decided anything yet (#3778), this one could be an interesting use-case to experiment with plugins as it won't interfere with the Conan model, neither with the recipes. But I'm not sure if this functionality will get enough priority in our queue...



Hi all, 
I am not very experienced in Conan (but I'd like to be though) and even less in the plugins/hooks technology. I am not aware about how the future come for increasing Conan capabilities by means of them but for the time being, I have tried to propose a (hook) solution at
https://github.com/alacasta/hooks/blob/master/hooks/credentials_manager.py

Of course this is just a proof of concept, but I'd really like to contribute with a solution if you decide to go in some direction adding official support to this functionality. 

Thanks!

_[tl;dr]_

The idea behind is
- If someone wants to use the Credential Manager for his password, a user is needed to be previously set to that remote
```
conan user username -r foo
```
This allows to discriminate between those cases where anonymous login is allowed.
- Once the user is linked to that repo, then the hook makes the magic but some preconditions are previously checked:
  - `keyring` and `getpass` are needed. They can both being installed using pip
  - `keyring` needs to have a backend already set. Otherwise, an error will be prompted asking to set it up.

Some considerations:
- I know that the issue is still under triaging phase. I made this attemt just for fun and for helping about the possibilities behind. I didn't open PR since no tests have been added either.
- IMO is clear that is not the way since i needed to somehow access to many Conan internals by means of the Clients' API. Probably if exporting additional info to Hooks, accessing to Conan API will not be required.

@memsharded @jgsogo I found a typo in the Hook doc (https://docs.conan.io/en/latest/extending/hooks.html) 
-  In the Storage, activation and sharing section, there is a `hook.conf` example where it is indicated this as valid

```
 ...
 [hooks]
 attribute_checker
 conan-center
 my_custom_hook/hook.py
```

however, there is a note claiming that .py extension should not be used, so probably it should be

```
 ...
 [hooks]
 attribute_checker
 conan-center
 my_custom_hook/hook
```
Thanks a lot for the effort and POC, @alacasta! It looks like connecting this functionality is easier that I was expecting.

As you said, there is too much access to Conan internals, and also there isn't a clear hook to call the _ _register_using_native_keyring_ function. It is a smell pointing that hooks (at least how they are implemented now, or the ones that are available right now) are not the way to go because they are too tight to the recipe workflow.

The hard part of this issue is opening the door to an architecture for plugins that allow extending some parts of Conan without recipes depending on those plugins to work (a plugin about credentials won't break recipes, so it is a good example to make an experiment).

I feel like that plugins are the way to go (it is just my opinion), I agree that another approach would be to add more triggers that let the user to _connect_ hooks to more functionalities, but I feel we would not be doing the right thing.
I don't really like the idea of plugins myself, it's a vague concept, you can't have arbitrary functionality added to something and expect it to compose and live well in the long term, just my two cents.

@memsharded I'm not sure why the conclusion of your last point is that plugins should be used. Why not just catch that exception and issue a warning from Conan if there's no backend installed.
@alacasta fixed docs :heart: 
@Lawrencemm I understand your concern about the plugins. We need to discuss how we want the plugins mechanism but I agree it should provide a way to guarantee easy and complete installation, like the hooks with the `conan config install`. Even if we decide to install plugins with `pip`, the error message when the plugin is not there should be clear enough to let you know how to install it.
> @memsharded I'm not sure why the conclusion of your last point is that plugins should be used. Why not just catch that exception and issue a warning from Conan if there's no backend installed.

A couple of reasons:
- Every third party dependency is a potential problem. Yes, they sometimes fail to install in some systems, with different python versions. We have had this issue at least twice with conan dependencies. Even if the issues are reported and addressed to the library maintainers, it takes time to fix them, time that users are blocked and we cannot do anything. All that is at install time, even before conan is launched, so capturing imports errors is not enough.
- Implementing this feature as built-in won't allow the flexibility that the library itself provides. This is an advance feature, users wanting to use it, will probably have different needs, backends, etc. Implementing it as a plugin/hook allows users to very easily customize it. Built-in makes that very difficult.
In order for this feature to move forward, we should discuss first about the approach, if we are considering the "plugins" one. I am setting the "whiteboard" label to put it in the discussion queue.
I hope the discussion around system-level dependencies like `libsecret` hasn't derailed this issue unnecessarily. I thought it was common for people to have to add a keyring backend on their OS (usually Mac and Windows have them pre-installed) if one is not present for these things to work.

For reference:
- git: as stated above it's assumed with git that some backends may need to be installed or provided as custom helpers. E.g. see https://blog.scottlowe.org/2016/11/21/gnome-keyring-git-credential-helper/
- pip: the docs are quite happy just telling people to install even `keyring` separately: https://github.com/pypa/pip/pull/8636/files so they will also have to provide the native OS backend if not present.
- random example (a Jira python CLI): https://github.com/alisaifee/jira-cli/pull/12 also assumes the user has provided a backend.

I'm sure there's more out there.
The reason I'm bumping this is that Artifactory, GitLab and Nexus all now support Conan packages, and with private registries setting up all kinds of package manager config can get quite messy.

Edit: personally the pip approach seems quite reasonable, and I'm wondering if following their approach would allow people to set 1 keyring entry for a single host/instance like GitLab serving different repos/packages (e.g. git, pip, conan etc).
Hello,
Thank you for all the job done. Is there any update concerning password storage on conan.

Gitlab has provided for some time the possibility to push conan packages. For authentication, you can generate [access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html). The value is only accessible when you create it.

When using pip, everything goes smoothly as it is stored by keyring on your system.

When using conan, I didn't find a way to save it encrypted anywhere. The `conan user ...` command is requiring often (after restart or after some time) to re-enter the password. As you need to generate access token from gitlab every time you use such command, either you regenerate manually a token from gitlab's settings or you save the credentials into a plain text. Saving the credentials in plain text is not very good security-wise. With such information, you could access to all the recipies (thus the source code) and other package system such as python package. Do you have an idea how I could store safely the conan credentials on my system

Thank you.
In python-gitlab, a recent PR added a very naive helper mechanism that simply expects a credential helper (can be anything that prints out tokens to stdout - keyring, pass, vault, bitwarden, etc). This behaves a bit more like git and docker credential helpers.

https://github.com/python-gitlab/python-gitlab/pull/1359/files

Just thought I'd add this here if it's any use - this would require no additional python dependencies, the user would bring their own helpers.
We have recently added ``credentials.json`` file (with jinja templates too), and made some refactors, and we have read about the new helpers in other platforms, so we think it should be relatively doable to add a plugin/helper solution (in the form of a python extension that users provide, and can be managed with ``conan config install`` too) that receives the remote name and URL and returns the credentials to Conan. If you are still interested in this, please let me know.

Hi @Aalmann!

Conan supports custom authentications: https://docs.conan.io/en/latest/uploading_packages/running_your_server.html#authentication

I've created a plugin for LDAP (as example): https://github.com/uilianries/conan-ldap-authentication

To implement a plugin for OAuth I think won't be complicated, based on my past work using [google-auth-oauthlib](https://github.com/GoogleCloudPlatform/google-auth-library-python-oauthlib). It uses [requests-oauthlib](https://github.com/requests/requests-oauthlib) to authenticate, so your plugin could check the token generated by the OAuth server.
 
According [Artifactory's docs](https://www.jfrog.com/confluence/display/RTF/OAuth+Integration) it has support for OAuth, but I don't know about 2FA.
I am not sure if I understand the question. Are you talking about the conan_server?
Because you comment you already have 2FA in Artifactory. 

If anything, it would be similar to Github: https://help.github.com/articles/accessing-github-using-two-factor-authentication/#using-two-factor-authentication-with-the-command-line

Artifactory already has user tokens, I think they might be used as password from the client side too, but need to check.
No not conan_server (and I think I didn't wrote it :smile:).

Our setup is: 
* Server side: Artifactory as binary server with user/password authentication (Atlassian Crowd directory integration) + "Timebased One Time Password" (e.g. Google Auth)
* Conan client installed via pip on user machine plus Google Authenticator as auth-token generator

So the question is: How to configure Conan client for using that setup.
I have no idea about it. But with Artifactory tokens, you can use the token like a password and Artifactory will accept it. So if with google auth code you can then get a token or something similar you might be able to use it. 

I think is not so simple, for Google 2FA you will need to create a Google Dev account and a new application there to provide a QR code to be used with Google Authenticator, and of course, Artifactory token will not work, bacause the token is based on Google side.

This feature should be developed by Artifatory team, where they could provide a QR Code on web interface to be added on Google Authenticator app. So the temporary token provided by the app should be used for Conan authentication. However, in my experience, performing 2FA authentication over CI jobs could cause headache for days.

But the question is, why do you need this? 2FA is really good for distributed applications that you can access around the world, like gmail, github, ... but Conan is a client and usually runs only in your company.
> Conan client installed via pip on user machine plus Google Authenticator as auth-token generator

Again, you will to create a plugin with all Google stuff. Fortunately, Google has a good python API and a big community to help. You will need to execute some steps in your plugin:

- The google authenticator will need a token distributed by server side, usually as QR Code, to be used as seed. So the first step is receive this token and store in somewhere. This could be solved at first time that you run `conan user`, the client should check if your authenticator is configured.
- When running `conan user` you will need to add your user and password, but as google authenticator generates temporary tokens, your plugin needs to check if your connection has expired (but you could set to keep connected as well) and reconnect if needed.
- You will need to check the provider's documentation to understand its API. I saw few providers supporting 2FA by REST, usually I've used Soup to parse the page and send the temporary token.
@lasote 
So the workflow would be: Login via Browser to handle user/password and Google Auth, grab the Artifactory token and use it as password? Isn't each session (conan and browser) different and so a problem for (re-)using the token at command line?

@uilianries 
Why we need this: VPN connections and everything is only available in corporate networks/infrastructure was "yesterday" (... sound weird 😃 ...). Today the most of the/our collaboration services (JIRA, Confluence, Bitbucket, Artifactory, etc.) are available via internet, to provide a better partner and team integration. But to better secure the access a second factor is required.
Currently this 2FA setup in combination with Artifactory is in planning/testing phase and I don't know the concrete implementation. But I want to be prepared and I want to be able to rise the hand if there is an issue with conan and the 2FA setup.
But what do you mean with create a "plugin"? I don't know any plugin mechanism in conan client (but in server). The only "extension points" I know are hooks and python requires and only hooks may be useful for that (pre_download, pre_upload).
> @lasote
So the workflow would be: Login via Browser to handle user/password and Google Auth, grab the Artifactory token and use it as password? Isn't each session (conan and browser) different and so a problem for (re-)using the token at command line?

I don't know if it makes sense. I would need an example of a client tool using Google Auth to login against a server. But eventually (irrespective of the authentication process with the server) you could get a token, so I thought that maybe it would be Artifactory responsibility to generate an access token for Conan. Once the token is created it will be valid until it expires, but from my perspective, it has nothing to do anymore with Google Auth. But again, I have no idea about this.
I almost forgot about VPN case, now your case is more clear. When I said about "plugin" actually it means docs.conan.io/en/latest/uploading_packages/running_your_server.html#authentication It works like an extension, as you commented.


@uilianries 
But this is only available for conan_server and we don't use it. We use Artifactory. 

@lasote 
Sadly I can't provide you such an example. 
We're in the same boat.  Have artifactory (ec2 instance in AWS), don't want vpn, need 2fa.  Is that even possible?
Artifactory provides OAuth authentication: https://www.jfrog.com/confluence/display/RTF/OAuth+Integration

I know it's not same thing, but at least you can integrate to other services like Google. The problem that I see using 2FA is providing an extra box to show a token.



Patch:
diff --git a/conan/cli/commands/remote.py b/conan/cli/commands/remote.py
index 8cd43711003..35cffa9b8b3 100644
--- a/conan/cli/commands/remote.py
+++ b/conan/cli/commands/remote.py
@@ -196,14 +196,16 @@ def remote_login(conan_api, parser, subparser, *args):
         raise ConanException("There are no remotes matching the '{}' pattern".format(args.remote))
 
     creds = RemoteCredentials(conan_api.cache_folder, conan_api.config.global_conf)
-    user, password = creds.auth(args.remote, args.username, args.password)
-    if args.username is not None and args.username != user:
-        raise ConanException(f"User '{args.username}' doesn't match user '{user}' in "
-                             f"credentials.json or environment variables")
 
     ret = OrderedDict()
     for r in remotes:
         previous_info = conan_api.remotes.user_info(r)
+
+        user, password = creds.auth(r, args.username, args.password)
+        if args.username is not None and args.username != user:
+            raise ConanException(f"User '{args.username}' doesn't match user '{user}' in "
+                                 f"credentials.json or environment variables")
+
         conan_api.remotes.user_login(r, user, password)
         info = conan_api.remotes.user_info(r)
         ret[r.name] = {"previous_info": previous_info, "info": info}
diff --git a/conan/internal/cache/home_paths.py b/conan/internal/cache/home_paths.py
index ba2402ded2a..30e79002b34 100644
--- a/conan/internal/cache/home_paths.py
+++ b/conan/internal/cache/home_paths.py
@@ -55,6 +55,14 @@ def profiles_path(self):
     def profile_plugin_path(self):
         return os.path.join(self._home, _EXTENSIONS_FOLDER, _PLUGINS, "profile.py")
 
+    @property
+    def auth_remote_plugin_path(self):
+        return os.path.join(self._home, _EXTENSIONS_FOLDER, _PLUGINS, "auth_remote.py")
+
+    @property
+    def auth_source_plugin_path(self):
+        return os.path.join(self._home, _EXTENSIONS_FOLDER, _PLUGINS, "auth_source.py")
+
     @property
     def sign_plugin_path(self):
         return os.path.join(self._home, _EXTENSIONS_FOLDER, _PLUGINS, "sign", "sign.py")
diff --git a/conans/client/rest/auth_manager.py b/conans/client/rest/auth_manager.py
index cf1ab8dded9..35b5c9ddce0 100644
--- a/conans/client/rest/auth_manager.py
+++ b/conans/client/rest/auth_manager.py
@@ -71,7 +71,7 @@ def _retry_with_new_token(self, user, remote, method_name, *args, **kwargs):
         credentials are stored in localdb and rest method is called"""
         for _ in range(LOGIN_RETRIES):
             creds = RemoteCredentials(self._cache_folder, self._global_conf)
-            input_user, input_password = creds.auth(remote.name)
+            input_user, input_password = creds.auth(remote)
             try:
                 self._authenticate(remote, input_user, input_password)
             except AuthenticationException:
diff --git a/conans/client/rest/conan_requester.py b/conans/client/rest/conan_requester.py
index 4903ac181f8..f59fac373d0 100644
--- a/conans/client/rest/conan_requester.py
+++ b/conans/client/rest/conan_requester.py
@@ -9,8 +9,11 @@
 from jinja2 import Template
 from requests.adapters import HTTPAdapter
 
+from conan.internal.cache.home_paths import HomePaths
+
 from conans import __version__ as client_version
-from conans.errors import ConanException
+from conans.client.loader import load_python_file
+from conans.errors import ConanException, scoped_traceback
 
 # Capture SSL warnings as pointed out here:
 # https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning
@@ -30,8 +33,11 @@ class _SourceURLCredentials:
     """
     def __init__(self, cache_folder):
         self._urls = {}
+        self.auth_source_plugin = None
         if not cache_folder:
             return
+        auth_source_plugin_path = HomePaths(cache_folder).auth_source_plugin_path
+        self.auth_source_plugin = _load_auth_source_plugin(auth_source_plugin_path)
         creds_path = os.path.join(cache_folder, "source_credentials.json")
         if not os.path.exists(creds_path):
             return
@@ -61,6 +67,22 @@ def _get_auth(credentials):
             raise ConanException(f"Error loading 'source_credentials.json' {creds_path}: {repr(e)}")
 
     def add_auth(self, url, kwargs):
+        # First, try to use "auth_source_plugin"
+        if self.auth_source_plugin:
+            try:
+                c = self.auth_source_plugin(url)
+            except Exception as e:
+                msg = f"Error while processing 'auth_source_remote.py' plugin"
+                msg = scoped_traceback(msg, e, scope="/extensions/plugins")
+                raise ConanException(msg)
+            if c:
+                if c.get("token"):
+                    kwargs["headers"]["Authorization"] = f"Bearer {c.get('token')}"
+                if c.get("user") and c.get("password"):
+                    kwargs["auth"] = (c.get("user"), c.get("password"))
+                return
+
+        # Then, try to find the credentials in "_urls"
         for u, creds in self._urls.items():
             if url.startswith(u):
                 token = creds.get("token")
@@ -183,3 +205,9 @@ def _call_method(self, method, url, **kwargs):
             if popped:
                 os.environ.clear()
                 os.environ.update(old_env)
+
+def _load_auth_source_plugin(auth_source_plugin_path):
+    if os.path.exists(auth_source_plugin_path):
+        mod, _ = load_python_file(auth_source_plugin_path)
+        if hasattr(mod, "auth_source_plugin"):
+            return mod.auth_source_plugin
diff --git a/conans/client/rest/remote_credentials.py b/conans/client/rest/remote_credentials.py
index 33dbe638d08..d9ad7ddafdd 100644
--- a/conans/client/rest/remote_credentials.py
+++ b/conans/client/rest/remote_credentials.py
@@ -4,16 +4,19 @@
 
 from jinja2 import Template
 
+from conan.internal.cache.home_paths import HomePaths
+
+from conans.client.loader import load_python_file
 from conan.api.output import ConanOutput
 from conans.client.userio import UserInput
-from conans.errors import ConanException
+from conans.errors import ConanException, scoped_traceback
 from conans.util.files import load
 
-
 class RemoteCredentials:
     def __init__(self, cache_folder, global_conf):
         self._global_conf = global_conf
         self._urls = {}
+        self.auth_remote_plugin = _load_auth_remote_plugin(HomePaths(cache_folder).auth_remote_plugin_path)
         creds_path = os.path.join(cache_folder, "credentials.json")
         if not os.path.exists(creds_path):
             return
@@ -32,8 +35,20 @@ def auth(self, remote, user=None, password=None):
         if user is not None and password is not None:
             return user, password
 
-        # First prioritize the cache "credentials.json" file
-        creds = self._urls.get(remote)
+        # First get the auth_remote_plugin
+
+        if self.auth_remote_plugin is not None:
+            try:
+                plugin_user, plugin_password = self.auth_remote_plugin(remote, user=user)
+            except Exception as e:
+                msg = f"Error while processing 'auth_remote.py' plugin"
+                msg = scoped_traceback(msg, e, scope="/extensions/plugins")
+                raise ConanException(msg)
+            if plugin_user and plugin_password:
+                return plugin_user, plugin_password
+
+        # Then prioritize the cache "credentials.json" file
+        creds = self._urls.get(remote.name)
         if creds is not None:
             try:
                 return creds["user"], creds["password"]
@@ -41,7 +56,7 @@ def auth(self, remote, user=None, password=None):
                 raise ConanException(f"Authentication error, wrong credentials.json: {e}")
 
         # Then, check environment definition
-        env_user, env_passwd = self._get_env(remote, user)
+        env_user, env_passwd = self._get_env(remote.name, user)
         if env_passwd is not None:
             if env_user is None:
                 raise ConanException("Found password in env-var, but not defined user")
@@ -49,7 +64,7 @@ def auth(self, remote, user=None, password=None):
 
         # If not found, then interactive prompt
         ui = UserInput(self._global_conf.get("core:non_interactive", check_type=bool))
-        input_user, input_password = ui.request_login(remote, user)
+        input_user, input_password = ui.request_login(remote.name, user)
         return input_user, input_password
 
     @staticmethod
@@ -66,3 +81,9 @@ def _get_env(remote, user):
         if passwd:
             ConanOutput().info("Got password '******' from environment")
         return user, passwd
+
+def _load_auth_remote_plugin(auth_remote_plugin_path):
+    if os.path.exists(auth_remote_plugin_path):
+        mod, _ = load_python_file(auth_remote_plugin_path)
+        if hasattr(mod, "auth_remote_plugin"):
+            return mod.auth_remote_plugin


Test patch:
diff --git a/test/integration/configuration/conf/test_auth_source_plugin.py b/test/integration/configuration/conf/test_auth_source_plugin.py
new file mode 100644
index 00000000000..c87116a63c3
--- /dev/null
+++ b/test/integration/configuration/conf/test_auth_source_plugin.py
@@ -0,0 +1,82 @@
+import json
+import os
+import textwrap
+
+import pytest
+
+from conan.test.utils.file_server import TestFileServer
+from conan.test.utils.test_files import temp_folder
+from conan.test.utils.tools import TestClient
+from conans.util.files import save
+
+
+class TestAuthSourcePlugin:
+    @pytest.fixture
+    def setup_test_client(self):
+        self.client = TestClient(default_server_user=True, light=True)
+        self.file_server = TestFileServer()
+        self.client.servers["file_server"] = self.file_server
+        self.download_cache_folder = temp_folder()
+        self.client.save_home({"global.conf": "tools.files.download:retry=0"})
+
+        save(os.path.join(self.file_server.store, "myfile.txt"), "Bye, world!")
+
+        conanfile = textwrap.dedent(f"""
+                           from conan import ConanFile
+                           from conan.tools.files import download
+                           class Pkg2(ConanFile):
+                               name = "pkg"
+                               version = "1.0"
+                               def source(self):
+                                   download(self, "{self.file_server.fake_url}/basic-auth/myfile.txt", "myfile.txt")
+                           """)
+        self.client.save({"conanfile.py": conanfile})
+
+        return self.client, self.file_server.fake_url
+
+    """ Test when the plugin fails, we want a clear message and a helpful trace
+    """
+    def test_error_source_plugin(self, setup_test_client):
+        c, url = setup_test_client
+        auth_plugin = textwrap.dedent("""\
+            def auth_source_plugin(url):
+                raise Exception("Test Error")
+            """)
+        save(os.path.join(c.cache.plugins_path, "auth_source.py"), auth_plugin)
+        c.run("source conanfile.py", assert_error=True)
+        assert "Test Error" in c.out
+
+    """ Test when the plugin give a correct and wrong password, we want a message about the success
+        or fail in login
+    """
+    @pytest.mark.parametrize("password", ["password", "bad-password"])
+    def test_auth_source_plugin_direct_credentials(self, password, setup_test_client):
+        should_fail = password == "bad-password"
+        c, url = setup_test_client
+        auth_plugin = textwrap.dedent(f"""\
+            def auth_source_plugin(url):
+                return { json.dumps({'user': 'user', 'password': password}) }
+            """)
+        save(os.path.join(c.cache.plugins_path, "auth_source.py"), auth_plugin)
+        c.run("source conanfile.py", assert_error=should_fail)
+        if should_fail:
+            assert "AuthenticationException" in c.out
+        else:
+            assert os.path.exists(os.path.join(c.current_folder, "myfile.txt"))
+
+    """ Test when the plugin do not give any user or password, we want the code to continue with
+        the rest of the input methods
+    """
+    def test_auth_source_plugin_fallback(self, setup_test_client):
+        c, url = setup_test_client
+        auth_plugin = textwrap.dedent("""\
+                def auth_source_plugin(url):
+                    return None
+                """)
+        save(os.path.join(c.cache.plugins_path, "auth_source.py"), auth_plugin)
+        source_credentials = json.dumps({"credentials": [{"url": url, "token": "password"}]})
+        save(os.path.join(c.cache_folder, "source_credentials.json"), source_credentials)
+        c.run("source conanfile.py")
+        # As the auth plugin is not returning any password the code is falling back to the rest of
+        # the input methods in this case provided by source_credentials.json.
+        assert os.path.exists(os.path.join(c.current_folder, "myfile.txt"))
diff --git a/test/integration/configuration/test_auth_remote_plugin.py b/test/integration/configuration/test_auth_remote_plugin.py
new file mode 100644
index 00000000000..ab62507ccfc
--- /dev/null
+++ b/test/integration/configuration/test_auth_remote_plugin.py
@@ -0,0 +1,59 @@
+import os
+import textwrap
+
+import pytest
+
+from conan.test.utils.tools import TestClient
+from conans.util.files import save
+
+
+class TestAuthRemotePlugin:
+    """ Test when the plugin fails, we want a clear message and a helpful trace
+    """
+    def test_error_auth_remote_plugin(self):
+        c = TestClient(default_server_user=True)
+        auth_plugin = textwrap.dedent("""\
+            def auth_remote_plugin(remote, user=None):
+                raise Exception("Test Error")
+            """)
+        save(os.path.join(c.cache.plugins_path, "auth_remote.py"), auth_plugin)
+        c.run("remote logout default")
+        c.run("remote login default", assert_error=True)
+        assert "Error while processing 'auth_remote.py' plugin" in c.out
+        assert "ERROR: Error while processing 'auth_remote.py' plugin, line " in c.out
+
+    """ Test when the plugin give a correct and wrong password, we want a message about the success
+        or fail in login
+    """
+    @pytest.mark.parametrize("password", ["password", "bad-password"])
+    def test_auth_remote_plugin_direct_credentials(self, password):
+        should_fail = password == "bad-password"
+        c = TestClient(default_server_user=True)
+        auth_plugin = textwrap.dedent(f"""\
+            def auth_remote_plugin(remote, user=None):
+                return "admin", "{password}"
+            """)
+        save(os.path.join(c.cache.plugins_path, "auth_remote.py"), auth_plugin)
+        c.run("remote logout default")
+        c.run("remote login default", assert_error=should_fail)
+        if should_fail:
+            assert "ERROR: Wrong user or password. [Remote: default]" in c.out
+        else:
+            assert "Changed user of remote 'default' from 'None' (anonymous) to 'admin' (authenticated)" in c.out
+
+
+    """ Test when the plugin do not give any user or password, we want the code to continue with
+        the rest of the input methods
+    """
+    def test_auth_remote_plugin_fallback(self):
+        c = TestClient(default_server_user=True)
+        auth_plugin = textwrap.dedent("""\
+                def auth_remote_plugin(remote, user=None):
+                    return None, None
+                """)
+        save(os.path.join(c.cache.plugins_path, "auth_remote.py"), auth_plugin)
+        c.run("remote logout default")
+        c.run("remote login default")
+        # As the auth plugin is not returning any password the code is falling back to the rest of
+        # the input methods in this case the stdin provided by TestClient.
+        assert "Changed user of remote 'default' from 'None' (anonymous) to 'admin' (authenticated)" in c.out