Sign-in on the Web — Credential Management API and Best Practices
This is a recollection of my part of the talk at Chrome Dev Summit 2016 titled “Sign-in on the Web — Credential Management and Best Practices”.
Free AI web copilot to create summaries, insights and extended knowledge, download it at here
3718
Abstract
ttps://unsplash.com/@erwimadethis?utm_source=medium&utm_medium=referral">Erwi</a> on <a href="https://unsplash.com?utm_source=medium&utm_medium=referral">Unsplash</a></figcaption></figure><p id="61a0">My journey toward emotional sobriety led me to a crucial realization: the mastery of resilience through self-compassion.</p><p id="81dd" type="7">Resilience, in my mind, was the ability to bounce back from adversity, to emerge stronger and wiser after facing life’s inevitable storms.</p><p id="4442"><b>And self-compassion, as I was about to discover, was the guiding star on this path.</b></p><p id="799f">You see, I had my fair share of setbacks. Like anyone else, I had faced disappointments, failures, and moments of despair. But what truly set me on the road to mastering resilience was my willingness to be compassionate toward myself.</p><p id="5f9d">Instead of berating myself for my perceived failures, I began to treat myself with kindness, as I would a dear friend.</p><h1 id="6ab7">#4: The Journey Begins</h1><p id="e305">I decided to embark on a quest to master resilience through self-compassion, and it was not an easy path.</p><p id="3104" type="7">The first hurdle was my own skepticism. Could something as seemingly simple as self-compassion truly make a difference in my life?</p><p id="acef"><b>It was a question that lingered in the background as I ventured into this uncharted territory.</b></p><p id="6345">I began by acknowledging my own feelings and allowing myself to experience them fully. Whether it was a wave of sadness, a rush of anger, or a surge of self-doubt, I stopped resisting and started accepting.</p><p id="cb47">It was an act of self-compassion, a commitment to being there for myself in those challenging moments.</p><h1 id="183d">#5: The Power of Self-Compassion</h1><p id="0b35">As I continued to practice self-compassion, something incredible began to happen.</p><p id="9224" type="7">The power of self-kindness was like a soothing balm for my wounded soul. It gave me the strength to face adversity with courage and grace.</p><p id="553a"><b>It allowed me to bounce back from setbacks that once felt insurmountable.</b></p><p id="cbb2">Through this self-experiment, I discovered the undeniable link between self-compassion and resilience. Self-compassion was not a sign of weakness; it was a wellspring of inner strength.</p><p id="55c4">It was the foundation upon which I could rebuild after life’s tempests, emerging not as a shattered vessel but as a fortified fortress of the self.</p><h1 id="1fa6"># 6: The Extraordinary Life Unveiled</h1><figure id="6ae4"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/0*eewkgegoEHMO_r8i"><figcaption>Photo by <a href="https://unsplash.com/@tregubov?utm_source=medium&utm_medium=referral">Mihail Tregubov</a> on <a href="https://unsplash.com?utm_source=medium&utm_medium=referral">Unsplash</a></figcaption></figure><p id="d7e4">As I delved deeper into the world of emotional sobriety and self-compassion, the extraordinary life I had longed for began to unfold before my eyes.</p><p id="428c" type="7">The transformation was not only visible to me but also to those around me. I was more open, more authentic, and more resilient.</p><p id="f4a5"><b>Self-compassion had become my faithful companion on this journey.</b></p><p id="ebf6">It allowed me to treat myself with kindness and understanding, especially in moments of self-doubt. It was the fuel that powered my resilience, enabling me to face life’s challenges head-on and bounce back with renewed vigor.</p><p id="44a0"><a href="https://medium.com/@motazmajed1994/subscribe"><b>Don’t miss future stories — subscribe now for exclusive conten
Options
t.</b></a>💙📧</p><h1 id="0c63">Conclusion:</h1><p id="8b02">Now, as I reflect on my journey, I can’t help but marvel at the incredible transformation that has taken place.</p><p id="76e5" type="7">The missing piece I sought was not external but internal.</p><p id="105a"><b>It was the power of emotional sobriety, the mastery of resilience through self-compassion, that led me to the extraordinary life I had dreamed of.</b></p><p id="b877"><b><i>This content thrives on the backing of amazing readers like you. If you enjoyed it, please share it and subscribe to get all upcoming stories sent right to your inbox.</i></b></p><p id="cec4"><i>If you’re feeling extra generous and want to support my writing, consider becoming a Medium member! For just $5 a month</i></p><div id="2ade" class="link-block"> <a href="https://medium.com/@motazmajed1994/subscribe"> <div> <div> <h2>Get an email whenever Motaz Majed "My To-Do List is Laughing at Me!!!" publishes.</h2> <div><h3>Get an email whenever Motaz Majed "My To-Do List is Laughing at Me!!!" publishes. By signing up, you will create a…</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*Qd7PFGkEN7McxX0N)"></div> </div> </div> </a> </div><p id="a7f6"><b><i>And hey, don’t forget you can also <a href="https://bmc.link/motazmajed">buy me a coffee</a>! Your support truly means a lot.</i></b></p><h1 id="8410">Next To Read ..</h1><div id="ee4d" class="link-block"> <a href="https://readmedium.com/the-trouble-with-negative-self-talk-breaking-free-from-it-9bc998a24a77"> <div> <div> <h2>The Trouble with Negative Self-Talk: Breaking Free From It</h2> <div><h3>My Self-Experiment in Challenging Self-Criticism</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*2YleBF_uZkO6Y5K7)"></div> </div> </div> </a> </div><div id="f1c6" class="link-block"> <a href="https://readmedium.com/the-trouble-with-procrastination-how-i-finally-sold-my-home-fast-66d58f5dd256"> <div> <div> <h2>The Trouble with Procrastination: How I Finally Sold My Home Fast</h2> <div><h3>How I Sold My Home Fast</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*FIc42RfDH_5MKaN1)"></div> </div> </div> </a> </div><div id="8153" class="link-block"> <a href="https://readmedium.com/the-trouble-with-limiting-beliefs-and-how-to-break-free-853d78702545"> <div> <div> <h2>The Trouble with Limiting Beliefs and How to Break Free</h2> <div><h3>Embracing Self-Compassion</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*d3M2kNSLr43r9qch)"></div> </div> </div> </a> </div><h1 id="6f14">Thanks for being amazing!</h1><p id="2465"><i>If you enjoyed this article, you can help me share this knowledge with others by:👏claps, 💬comment, and be sure to 👤+ follow.</i></p></article></body>
This is a recollection of my part of the talk at Chrome Dev Summit 2016 titled “Sign-in on the Web — Credential Management and Best Practices”.
Check the video for my colleague Sabine Borsay taking the first half of the talk describing the unique challenges we face on the web when personalizing user’s experience through sign-ins.
Unlike native apps, the web needs considerations for different attack surfaces and threat models such as XSS, CSRF, clickjacking, etc — as a result, session management has very different requirements on the web and on native.
Many web sessions expire within less than a day, sometimes even within 1 hour. Getting session management right on the web is hard, and you need to strike a balance between security and convenience. While there are different approaches, the Credential Management API is an easy way to handle this problem. (As promised in the session, in detail documentation of the Credential Management API is now available.)
See the Credential Management API in action starting from 10:08 in the video. Here’s the actual demo website and its source code. I summarize its benefits in 5 bullet points:
Following describes how you can replicate a similar user experience on your own website.

Forms that let users sign-up, sign-in or change passwords are the great chances for you to store user’s credential information. The process can be broken into 4 steps:
var f = document.querySelector('#signup');
f.addEventListener('submit', e => {
e.preventDefault(); sendRequest(e.target)
.then(profile => {
if (window.PasswordCredential) {
var c = new PasswordCredential(e.target);
return navigator.credentials.store(c);
} else {
return Promise.resolve(profile);
}
}).then(profile => {
if (profile) {
updateUI(profile);
}
}).catch(error => {
showError('Sign-in Failed');
});
});Before moving forward, check if your form includes autocomplete attributes. This helps the Credential Management API to find id and password from the form and construct a credential object.
This also helps browsers not supporting the Credential Management API to understand its semantics. Adding autocomplete is MUST. Learn more about the topic from an article by Jason Grigsby, ☁4.
<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>var f = document.querySelector('#signup');
f.addEventListener('submit', e => {
e.preventDefault();On this form, interrupt the event when user presses the submit button, and prevent the default behavior.
This way, you can prevent a page transition. By preventing a page transition, you can retain the credential information while verifying its authenticity.
sendRequest(e.target)
.then(profile => {The code above is using a pseudo code, but I recommend you to use fetch(). You will find the reason later in this post.
On the server side, you should create an API (or simply alter existing endpoint) that responds with HTTP code 200 or 401, etc so that it’s clear to the browser if the request is successful or not.
Responding with a profile information in a JSON format will be a good idea. But it’s totally up to your implementation.
if (window.PasswordCredential) {
var c = new PasswordCredential(e.target);
return navigator.credentials.store(c);
} else {
return Promise.resolve(profile);
}Once the request succeeds, you can store the credential information.
Before doing so, check if the API is available. You should always keep progressive enhancement in mind.
To store a credential, instantiate a PasswordCredential with the form element as an argument. Then call navigator.credentials.store(). If the API is not available, you can simply forward the profile information to the next step.
}).then(profile => {
if (profile) {
updateUI(profile);
}
}).catch(error => {
showError('Sign-in Failed');
});
});If everything went well, update the UI using the profile information, or proceed to the personalized page. And done.

In case your website have federated logins as options, here’s how to use the Credential Management API with them.
Federated logins are in general built on top of standard protocols such as OpenID Connect or OAuth etc. Imagine Google Sign-In or Facebook Login which are frequently used.
As I mentioned earlier, using federated login is a great option. Because:
To use federated login with the Credential Management API, here are steps.
var g = document.querySelector('#g-signin');
g.addEventListener('click', e => {
gSignIn()
.then(profile => {
if (window.FederatedCredential) {
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(profile);
}).catch(error => {
showError('Sign-in Failed');
});
};var g = document.querySelector('#g-signin');
g.addEventListener('click', e => {
gSignIn()
.then(profile => {When a user taps on a federated login button, run the identity provider specific authentication flow.
I’m not going to describe how to implement them in detail, but they are usually standard based such as OpenID Connect or OAuth. Find Google Sign-In example here.
if (window.FederatedCredential) {
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);
}Once authentication is done, you can store the identity information. The information you’ll store here is the id from the identity provider and a provider string that represents the identity provider. (name and iconURL are optional.)
Instantiate a FederatedCredential with those information, then invoke navigator.credentials.store().
The rest of the steps are similar to the previous section..

The most exciting part of using Credential Management API is the auto sign-in. Actually, this can happen anywhere on your website — not only top page but also other leaf pages. Here are steps to enable auto sign-in:
if (window.PasswordCredential || window.FederatedCredential) {
if (!user.isSignedIn()) {
navigator.credentials.get({
password: true,
federated: {
provider: [
'https://accounts.google.com'
]
},
mediation: 'silent'
}).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');
});
}
}if (window.PasswordCredential || window.FederatedCredential) {
if (!user.isSignedIn()) {
navigator.credentials.get({
password: true,
federated: {
provider: [
'https://accounts.google.com'
]
},
mediation: 'silent'
})Before getting a credential, don’t forget to check if the user is already signed in.
To get a credential information, invoke navigator.credentials.get(). You can specify the type of credentials to request by giving it a password or a federated. Always use mediation: 'silent' for auto sign-ins. This way, you can easily dismiss the process if the user:
}).then(c => {
if (c) {
switch (c.type) {
case 'password':
return sendRequest(c);
break;
case 'federated':
return gSignIn(c);
break;
}
} else {
return Promise.resolve();
}When the function resolved, check if you have received a credential object. If not, it means auto sign-in couldn’t happen. Silently dismiss the auto sign-in process.
If you did get a credential, run an authentication flow depending on the type of credential as I have described in previous sections.
}).then(profile => {
if (profile) {
updateUI(profile);
}If the authentication was successful, update the UI or forward the user to the personalized page.
}).catch(error => {
showError('Sign-in Failed');
});
}
}One important tip here is that if you succeed to obtain a credential object but failed to authenticate the user, you should show an error message.

At the timing of getting a credential object, the user should have seen a blue toast saying “Signing in”. It’s confusing for users if nothing happens after this.

When the user signs out, let’s turn off auto sign-in for future visits.
signoutUser();
if (navigator.credentials && navigator.credentials.preventSilentAccess) {
navigator.credentials.preventSilentAccess();
}To do so, let the user sign out when sign-out button is tapped. How this is executed is totally up to you, but generally you should terminate a session.
Then call navigator.credentials.preventSilentAccess(). This will ensure the auto sign-in won’t happen until next time the user enables auto sign-in. Users can do so by tapping on an account in an account chooser which is described in next section.

When a user taps on “Sign-In” button, let’s show an account chooser instead of showing a sign-in form, and let the user sign-in with just one tap.
Most of the process here is similar to what I have talked about auto sign-in. A trigger to this process should be some kind of user action. For example, tapping on “Sign-In” button.
var signin = document.querySelector('#signin');
signin.addEventListener('click', e => {
if (navigator.credentials) {
navigator.credentials.get({
password: true,
federated: {
provider: [
'https://accounts.google.com'
]
},
mediation: 'optional'
}).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';
});
}
});var signin = document.querySelector('#signin');
signin.addEventListener('click', e => {
if (navigator.credentials) {
navigator.credentials.get({
password: true,
federated: {
provider: [
'https://accounts.google.com'
]
},
mediation: 'optional'
}).then(c => {Call navigator.credentials.get(). By adding mediation: 'optional', you can show the account chooser. Once the user makes a selection of an account, you will receive a credential.
Let’s skip rest of the steps as they are mostly similar to what I’ve described in previous auto sign-in section.
}).then(profile => {
if (profile) {
updateUI(profile);
} else {
location.href = '/signin';
}
}).catch(error => {
location.href = '/signin';
});
}
});You should fallback to a sign-in form if:
Did you notice that the Credential Management API itself only consists of 3 functions? It’s quite simple, but requires few bits of best practices. That’s why I wrote this article.
There are more learning resources to get you started.
Feel free to shoot @agektmr on Twitter if you have any questions implementing this feature. Feedback to the API is also appreciated.
I’m looking forward to seeing more Credential Management API implementations in the wild!