Storing Credentials with QgsAuthManager
Keep plugin credentials out of settings and source: create a QGIS authentication configuration, store only its id, apply it with updateNetworkRequest, and…
TL;DR: never store a password yourself — put it in a QGIS authentication configuration, keep only the configuration id in your plugin’s settings, and let QgsApplication.authManager() apply it to requests. This page is part of the plugin settings and configuration management guide.
Complete Runnable Code
"""Use a stored authentication configuration for a plugin's requests."""
from qgis.core import QgsApplication, QgsAuthMethodConfig, QgsSettings
from qgis.PyQt.QtNetwork import QNetworkRequest
SECTION = "myplugin"
def auth_available() -> bool:
"""True when the authentication database is usable in this session."""
manager = QgsApplication.authManager()
return not manager.isDisabled() and manager.masterPasswordIsSet()
def create_basic_config(name: str, username: str, password: str) -> str:
"""Store a username and password, returning the id to keep. Interactive use only."""
if not auth_available():
raise RuntimeError("the authentication database is not available")
config = QgsAuthMethodConfig()
config.setName(name)
config.setMethod("Basic")
config.setConfig("username", username)
config.setConfig("password", password)
if not QgsApplication.authManager().storeAuthenticationConfig(config):
raise RuntimeError("could not store the authentication configuration")
return config.id() # this is the only thing you keep
def remember_config_id(config_id: str) -> None:
"""Store the id — not the secret — in the plugin's settings."""
QgsSettings().setValue("%s/auth_cfg" % SECTION, config_id)
def apply_to_request(request: QNetworkRequest) -> bool:
"""Attach the stored credentials to `request`. Returns False when unavailable."""
config_id = QgsSettings().value("%s/auth_cfg" % SECTION, "", type=str)
if not config_id:
return True # the service is unauthenticated
if not auth_available():
return False
return QgsApplication.authManager().updateNetworkRequest(request, config_id)
Architecture Breakdown
The configuration id is the whole interface
storeAuthenticationConfig() writes the secret into an encrypted SQLite database inside the user’s profile and hands back an opaque identifier — seven characters, no information content. That id is what your plugin stores, logs, and puts in a project file if it needs to. The secret itself never passes through your code again.
This is why the pattern is worth adopting even for a plugin talking to one internal service. The day somebody attaches their QGIS profile to a support ticket, or commits a project file to a shared repository, the difference between an id and a password is the difference between a non-event and an incident.
updateNetworkRequest() — applying it without knowing what it is
The manager mutates the request in place, adding whatever the configuration requires: an Authorization header for basic credentials, a token, a client certificate. Your code does not branch on the method, which means a user can change from a password to a certificate without any change to the plugin.
The return value matters. False means the credentials were not applied — most often because the master password has not been entered — and treating that as success produces an unauthenticated request and a confusing 401.
The master password gate
The authentication database is encrypted with a master password the user sets once. Until it is entered in a session, no configuration can be read. In the desktop this produces a prompt, which is exactly right; in a headless run there is nobody to prompt.
For automation, setMasterPassword() can be called at start-up with a value read from the environment or a secrets manager. That moves the problem rather than solving it, which is the honest situation: something has to hold the first secret, and the question is only whether it is the CI system’s secret store or a file in a repository.
What Not to Do Instead
Every alternative to the authentication database looks convenient right up to the moment somebody else reads the file, and the four below account for essentially all leaked credentials in plugin code.
Each row of that table is something that has shipped in a real plugin. The source-code case is the most common and the most damaging, because it publishes the credential to everyone who installs the plugin, and revoking it means a release.
The QgsSettings case feels safer and is not: the settings store is a plain-text file readable by anything running as that user, routinely copied when profiles are migrated, and frequently attached to bug reports. Storing a password there is a slower version of the same mistake.
Letting the User Choose the Configuration
Creating configurations from code is occasionally right — a plugin that provisions access to its own service, for instance — but the common case is that the user already has the credential, or should be the one entering it. QGIS ships a selector widget for exactly this, and using it saves you from building a credential form at all.
QgsAuthConfigSelect presents the list of stored configurations plus a button to create a new one, and reports the chosen id through a signal. Dropping it into a settings page means your plugin never sees a password, never renders one into a widget, and never has to decide how to mask it.
It also produces better behaviour for the user: the credential they configured for one plugin is available to the next, because it belongs to them and the service rather than to any particular tool. A plugin that insists on its own private copy of the same password is asking them to maintain two.
Production Best Practices
- Store the id, never the secret, and treat any code path that has a plaintext password in a variable as temporary.
- Check the return value of
updateNetworkRequest(). A silently unauthenticated request fails much later and much less clearly. - Handle the no-master-password case explicitly, with a message that tells the user what to do rather than a generic failure.
- Never fall back to an unauthenticated request when credentials are unavailable. Fail instead — a silent downgrade is a security bug.
- Do not log the configuration id alongside the service URL as a matter of habit; it is not secret, but it is an unnecessary breadcrumb.
- Offer a way to clear the stored id in your settings page, so a user can disconnect without editing files.
Frequently Asked Questions
What happens if the user has not set a master password?
Nothing can be read from the authentication database, and masterPasswordIsSet() returns False. In the desktop, calling into the manager prompts the user to set or enter one, which is usually acceptable at the moment they first configure the plugin.
What you should not do is treat the failure as “no credentials configured” and continue. That sends an unauthenticated request, gets a 401, and produces a support conversation about the wrong problem entirely.
Can I use this from a headless script?
Yes, with the master password supplied programmatically at start-up: QgsApplication.authManager().setMasterPassword(value, verify=True). Read the value from the environment or the CI system’s secret store — never from a file in the repository.
Note that the authentication database lives in the user profile, so a container needs the profile mounted or provisioned, and a fresh container has no configurations at all.
Does the configuration id change?
No. It is assigned when the configuration is created and stays stable for its lifetime, which is what makes it safe to store in settings or a project. If the user deletes and recreates the configuration they get a new id, so your plugin should handle a stored id that no longer resolves — treat it as “not configured” rather than crashing.
Can several plugins share one configuration?
Yes, and it is often the right thing: the credential belongs to the user and the service, not to your plugin. Let the user pick an existing configuration from the standard selector widget rather than always creating a new one, so a single stored credential can serve every tool that talks to that service.
What happens on a machine where the configuration does not exist?
The stored id resolves to nothing, and updateNetworkRequest() returns False. This is the normal situation when a project or a settings export moves between machines, and it should produce a clear message asking the user to select or create a configuration — not a stack trace, and not a silent unauthenticated request.
It is worth testing deliberately, because it is the first thing every new user of your plugin experiences.
What about API keys that are not passwords?
The same machinery covers them. An API key can go in a Basic or Header-based configuration depending on how the service expects it, and it is exactly as much of a secret as a password. The temptation to treat a key as “just configuration” and put it in settings is what makes keys the most commonly leaked credential of all.
Related
- Plugin Settings and Configuration Management — the parent guide covering where each value belongs
- Network Requests and Remote Data in Plugins — the request side of an authenticated call
- Adding a Plugin Page to the QGIS Options Dialog — where a user picks the configuration to use