Working with the Login Manager
Extensions often need to securely store passwords to external sites, web applications, and so on. To do so securely, they can use nsILoginManager
, which provides for secure storage of sensitive password information and nsILoginInfo
, which provides a way of storing login information.
Getting nsILoginManager
To get a component implementing nsILoginManager
, use the following:
var passwordManager = Components.classes["@mozilla.org/login-manager;1"].getService( Components.interfaces.nsILoginManager );
Most Login Manager functions take an nsILoginInfo
object as a parameter. An nsILoginInfo
object contains the following attributes: hostname, form submit URL, HTTP realm, username, username field, password, and password field. The hostname, username and password attributes are mandatory, while the other fields are set based on whether the login is for a web page form or an HTTP/FTP authentication site login. See the nsILoginInfo
attribute definitions for more details. Defining an nsILoginInfo
object is simple:
var nsLoginInfo = new Components.Constructor( "@mozilla.org/login-manager/loginInfo;1", Components.interfaces.nsILoginInfo, "init" ); var loginInfo = new nsLoginInfo( hostname, formSubmitURL, httprealm, username, password, usernameField, passwordField );
Examples
Creating a login for a web page
var formLoginInfo = new nsLoginInfo( 'http://www.example.com', 'http://login.example.com', null, 'joe', 'SeCrEt123', 'uname', 'pword' );
This login would correspond to a HTML form such as:
<form action="http://login.example.com/foo/authenticate.cgi"> <div>Please log in.</div> <label>Username:</label> <input type="text" name="uname"> <label>Password:</label> <input type="password" name="pword"> </form>
Creating a site authentication login
var authLoginInfo = new nsLoginInfo( 'http://www.example.com', null, 'ExampleCo Login', 'alice', 'SeCrEt321', "", "" );
This would correspond to a login on http://www.example.com when the server sends a reply such as:
HTTP/1.0 401 Authorization Required Server: Apache/1.3.27 WWW-Authenticate: Basic realm="ExampleCo Login"
Creating a local extension login
var extLoginInfo = new nsLoginInfo( 'chrome://firefoo', null, 'User Registration', 'bob', '123sEcReT', "", "" );
From a component creating a new info block is done slightly differently:
var nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init"); var extLoginInfo = new nsLoginInfo('chrome://firefoo', null, 'User Registration', 'bob', '123sEcReT', '', ''); //var extLoginInfo = new nsLoginInfo(aHostname, aFormSubmitURL, aHttpRealm, aUsername, aPassword, aUsernameField, aPasswordField)
The Login Manager treats this as if it was a web site login. You should use your extension's chrome:// URL to prevent conflicts with other extensions, and a realm string which briefly denotes the login's purpose.
Storing a password
To store a password in the Login Manager, you first need to create an nsILoginInfo
object as defined above. Then you simply need to call the nsILoginManager
method addLogin()
.
myLoginManager.addLogin(loginInfo);
httprealm
and formSubmitURL
parameters are NULL
. One must be specified when storing a password. The hostname
, username
and password
parameters are also mandatory.Retrieving a password
Retrieving a password from the Login Manager is slightly more difficult. In order to locate a password, the hostname
, formSubmitURL
and httprealm
must match exactly what is stored for the password to be found. The only exception is that if the stored formSubmitURL
is blank, in which case the formSubmitURL
parameter is ignored. Note that the hostname
and formSubmitURL
arguments should not include the path from the full URL. The example below should serve as a starting point for matching form logins:
var hostname = 'http://www.example.com'; var formSubmitURL = 'http://www.example.com'; // not http://www.example.com/foo/auth.cgi var httprealm = null; var username = 'user'; var password; try { // Get Login Manager var myLoginManager = Components.classes["@mozilla.org/login-manager;1"]. getService(Components.interfaces.nsILoginManager); // Find users for the given parameters var logins = myLoginManager.findLogins({}, hostname, formSubmitURL, httprealm); // Find user from returned array of nsILoginInfo objects for (var i = 0; i < logins.length; i++) { if (logins[i].username == username) { password = logins[i].password; break; } } } catch(ex) { // This will only happen if there is no nsILoginManager component class }
Note that the user will be prompted for their master password if they have chosen to set one to secure their passwords.
Removing a password
Removing a password is simple:
myLoginManager.removeLogin(loginInfo);
When removing a password the specified nsILoginInfo
object must exactly match what was stored or an exception will be thrown. This includes the password attribute. Here's an example on how to remove the password without actually knowing what the password is:
// example values var hostname = 'http://www.example.com'; var formSubmitURL = 'http://www.example.com'; var httprealm = null; var username = 'user'; try { // Get Login Manager var passwordManager = Components.classes["@mozilla.org/login-manager;1"]. getService(Components.interfaces.nsILoginManager); // Find users for this extension var logins = passwordManager.findLogins({}, hostname, formSubmitURL, httprealm); for (var i = 0; i < logins.length; i++) { if (logins[i].username == username) { passwordManager.removeLogin(logins[i]); break; } } } catch(ex) { // This will only happen if there is no nsILoginManager component class }
Changing stored login information
Changing a password is rather simple. Since all this does is make a removeLogin()
call followed by an addLogin()
call, it has the same caveats as both of them: namely that the oldLogin
must match an existing login exactly (see above) and that the newLogin
attributes must be set correctly.:
myLoginManager.modifyLogin(oldLogin, newLogin);
Login Manager notifications
The Login Manager notifications were added in Firefox 3.5.
Firefox 3.5 and later send assorted notifications when various Login Manager related events occur, including when form autofill does not occur for various reasons, as well as when changes are made to the Login Manager's database. See the Login Manager section of the article on observer notifications for details.
Debugging
The login manager implementation has the ability to send debug messages to the Error Console, which can provide some visibility into what it's doing. To enable the debug logging, see http://wiki.mozilla.org/Firefox:Pass...ager_Debugging.
Supporting older versions of Gecko
If you want your extension to support both Gecko 1.9 (Firefox 3, Thunderbird 3, SeaMonkey 2) and older versions it will need to implement both the nsILoginManager
and nsIPasswordManager
components. A simple method to do this is as follows:
if ("@mozilla.org/passwordmanager;1" in Components.classes) { // Password Manager exists so this is not Firefox 3 (could be Firefox 2, Netscape, SeaMonkey, etc). // Password Manager code } else if ("@mozilla.org/login-manager;1" in Components.classes) { // Login Manager exists so this is Firefox 3 // Login Manager code }