Building a better login
with the credential management API
James Allardice
Evolution of login
- Username or email address
- Browsers saving passwords
- Autofill
- Browsers saving passwords in the cloud
- Federated identity e.g. Facebook, Twitter
- Credential Management API
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
"Credential"?
- "Assertion about an entity which enables a trust decision" [1]
- Username and password
- Federated credential (trusted third party)
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));
});
Signing in
function sendLoginRequest(formData)
const fetchOpts = {
method: 'POST',
headers: {
Accept: 'application/json',
},
};
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 fetch('/api/login', 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
Questions?
Building a better login
By James Allardice
Building a better login
- 1,576