HTML5 Credential Management API

Another new API that has been added to JavaScript this year, is the CredentialManagementAPI. Browser support ist still limited to Chrome (since Chrome 51), but with other browsers expected to catch up soon.

The API can manage various user accounts for the same site and present the user with an easy options list. That is especially useful for sites on mobile devices, where typing login data often causes every secong user to abandon a site.

Password-based login with the Credential Management API

The Credential Management API works for both password based sign-in as well as federated sign-in via an authentication provider. A simple example for password-based sign-up form could look like this:

<form id="signup" method="post">
    <input name="email" type="text" autocomplete="username">
    <input name="display-name" type="text" autocomplete="name">
    <input name="password" type="password" autocomplete="new-password">
    <input type="submit" value="Sign Up">
</form>

Just a regular sign-up form, with added autocomplete attributes, to tell the browser to autocomplete the form fields, if it already has that data on the user.

The JavaScript controller intercepts the form submit, sends the submit request and receives the response from the server. If the sign-in was successful, the login data is saved to the user's credentials storage, so that it can be used later for fully automatic sign-ins.

const s = document.querySelector('#signup');

s.addEventListener('submit', evt => {
    evt.preventDefault();

    sendRequest(evt.target).then(profile => {
        if (navigator.credentials) {
            const cred = new PasswordCredential(evt.target);
            return navigator.credentials.store(cred);
        } else {
            return Promise.resolve(profile);
        }
    }).then(profile => {
        updateUI(profile);
    }).catch(error => {
        showError('Sign-in failed!');
    });
});

Federated login with the Credential Management API

The Credential Management API can do the same for federated sign-up as well. For example, to remember a user who signs up with a Google Account

const g = document.querySelector('#g-signin');

g.addEventListener('click', evt => {
    gSignIn().then(profile => {
        if (navigator.credentials) {
            var c = new FederatedCredential({
                id: profile.id,
                provider: 'https://accounts.google.com',
                name: profile.name,
                iconURL: profile.iconURL
            });
            return navigator.credentials.store(c);
        } else {
            return Promise.resolve(profile);
        }
    }).then(profile => {
        updateUI();
    }).catch(error => {
        showError('Sign-in failed!');
    });
});

Now that the user is successfully signed up and the credentials are stored in the browser's credentials storage, future sign-ins can be automated. The credentials storage can be shared between all devices of the user (for example via their Google Account, in case the are using Chrome). So, if a user did authenticate on a desktop computer, they will also be signed in on their phone.

Credential Management API automatic sign-in

Once the user signed up, they can be signed in automatically when returning to the site. The Credential Management API can handle password and federated sign-ins transparently.

if (navigator.credentials) {
    if (!user.isSignedIn()) {
        navigator.credentials.get({
            password: true,
            defedated: {
                provider: ['https://accounts.google.com']
            },
            unmediated: true,
        }).then(c => {
            if (c) {
                switch (c.type) {
                    case 'password':
                        return sendRequest(c);
                        break;
                    case 'federated':
                        return gSignIn(c);
                        break;
                }
            } else {
                return Promise.resolve();
            }
        }).then(profile => {
            if (profile) {
                updateUI(profile);
            }
        }).catch(error => {
            showError('Sign-in failed!');
        });
    }
}

The option unmediated tells the browser whether to ask the user before proceeding with automatic sign-in. In the example, if c is not defined, then profile will be empty and nothing will happen for the user. They will need to tab on a "Sign In" button to sign-in.

Using the Account Chooser UI

In case a user has multiple accounts, the web site can use the Credential Management API to display an account chooser. The user then simply clicks on the account they wish to sign in with.

var s = document.querySelector('#signin');

s.addEventListener('click', evt => {
    if (navigator.credentials) {
        navigator.credentials.get({
            password: true,
            federated: {
                provider: ['https://accounts.google.com'],
            },
            unmediated: false,  // <-- mediate, using account chooser
        }).then(c => {
            if (c) {
                switch (c.type) {
                    case 'password':
                        return sendRequest(c);
                        break;
                    case 'federated':
                        return gSignIn(c);
                        break;
                }
            } else {
                return Promise.resolve();
            }
        }).then(profile => {
            if (profile) {
                updateUI(profile);
            } else {
                location.href = '/signin/';
            }
        }).catch(error => {
            location.href = '/signin/';
        });
    }
});

Sign-out with the Credential Management API

Finally, the user must be able to sign out of their account. In that case, the next time they visit the site, automatic sign-in should be disabled.

signoutUser();
if (navigator.credentials) {
    navigator.credentials.requireUserMediation();
}

As of November 2016, the Credential Management API is stil relatively new and just works in some browser. But via feature dicovery like if (navigator.credentials), an existing website can use progressive enhancement to add the functionality without breaking the site for older browsers that don't support it.