Building a better login
with the Credential Management API
@james_allardice
james.allardice@uk.tesco.com
Evolution of login
Username/password forms
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3738322/Screen_Shot_2017-04-25_at_17.36.09.png)
Stored credentials
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3738327/Screen_Shot_2017-04-25_at_17.36.57.png)
Autofill
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3738330/Screen_Shot_2017-04-25_at_17.37.58.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3680471/Screenshot_2017-04-07_08.01.23.png)
You can sync passwords across devices.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3680478/Screenshot_2017-04-07_08.10.05.png)
Federated identity providers
The Credential Management API
- Spec work started early 2015 [1]
- Championed by Mike West at Google [2]
-
Provides 2 key mechanisms
- Help the user authenticate by providing access to credentials
- Help the browser store credentials provided by the user
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3677793/Screenshot_2017-04-06_16.00.13.png)
Assertion about an entity which enables a trust decision
Browser support
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3646215/Screen_Shot_2017-03-29_at_12.23.48.png)
Demo!
Accessing stored credentials
<form method="post" action="/login" id="login-form">
<div>
<label for="username">Username:</label>
<input
type="text"
id="username"
name="username"
>
</div>
<div>
<label for="password">Password:</label>
<input
type="password"
id="password"
name="password"
>
</div>
<input type="submit" value="Login">
</form>
Signing in
const form = document.getElementById('login-form');
form.addEventListener('submit', (e) => {
e.preventDefault();
sendLoginRequest(new FormData(form));
});
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3676624/Screenshot_2017-04-06_08.14.41.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3676631/Screenshot_2017-04-06_08.16.00.png)
Signing in
function sendLoginRequest(formData)
const fetchOpts = {
method: 'POST',
headers: {
Accept: 'application/json',
},
};
addCredentials(fetchOpts, formData);
makeRequest('/api/login', fetchOpts);
}
Signing in
function addCredentials(fetchOpts, formData) {
if (navigator.credentials) {
const credentials = new PasswordCredential({
id: formData.get('username'),
password: formData.get('password'),
});
credentials.additionalData = formData;
fetchOpts.credentials = credentials;
} else {
fetchOpts.body = formData;
}
return fetchOpts;
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3539743/Screenshot_2017-02-28_15.18.14.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3680495/Screenshot_2017-04-07_08.24.23.png)
Signing in
function makeRequest(url, fetchOpts) {
return fetch(url, fetchOpts)
.then((res) => res.json())
.then((json) => validateResponse(json, formData))
.then((redirectUrl) => {
window.location = redirectUrl;
});
}
Storing valid credentials
// Validate the response from the auth service. Returns a URL to redirect
// the user to.
function validateResponse(res, credentials)
if (res.valid) {
// Logged in successfully. If the browser supports the Credential
// Management API we can attempt to save the provided login details.
if (navigator.credentials) {
return navigator.credentials.store(credentials)
.then(() => res.redirectUrl);
}
return res.redirectUrl;
}
throw new Error('Login failed.');
}
Accessing stored credentials
// If the browser supports the Credential Management API we can attempt to
// get a stored password credential. If no credential is stored, of if the
// call fails for any reason, the user can still log in like normal.
if (navigator.credentials) {
navigator.credentials.get({
password: true,
})
.then((credentials) => {
if (credentials) {
return sendLoginRequest(credentials);
}
return null;
});
}
Signing out
if (navigator.credentials) {
navigator.credentials.requireUserMediation()
.then(() => {
// Redirect to login page.
});
}
Security
- Page must be served from a secure origin (HTTPS)
- Credentials for other origins not available
- Stored passwords not exposed to JavaScript
- Stored passwords are encrypted
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3590631/Screenshot_2017-03-14_21.42.47.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3590679/DB_Browser_for_SQLite_-__Users_jallardice_dev_scratch_chrome-db.png)
Thank you!
@james_allardice
james.allardice@uk.tesco.com
We are hiring here in Kraków.
Find out more or apply at https://carvallar.io
![](https://s3.amazonaws.com/media-p.slid.es/uploads/49778/images/3738416/Screen_Shot_2017-04-25_at_17.53.50.png)
Building a better login (Meet.js Krakow, April 2017)
By James Allardice
Building a better login (Meet.js Krakow, April 2017)
- 1,080