In this post we’re going to learn how to use JSON web tokens on the frontend with vanilla JavaScript and no libraries necessary. What you learn will be easily applicable to frameworks such as Angular, Angular2, Vue.js or similar.
In the last post we had a look at how to create an express app with jwt authentication.
Why No Framework?
I wanted to write a post simply illustrating how everything works and how easily this can be achieved without any frameworks in place. It’s just another layer of abstraction you have to learn if you’re just trying to build some sort of login for your web app.
Changes to the Backend
In order to serve an HTML page in the browser, I have changed the backend code slightly:
app.use(express.static('public'));
app.get("/", function(req, res) {
res.sendFile(process.cwd() + '/public/index.html');
});
Also I of course have created the directory public
and the files inside: index.html
and jwt-vanilla.js
.
The HTML part
We’re just going to serve a very basic and unstyled page for our educational purposes:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSON web token with vanilla js</title>
</head>
<body>
<div class="input">
<label for="username">user name:</label>
<input id="username">
<label for="password">password:</label>
<input id="password">
<button onclick="getToken()">login</button>
</div>
<div id="token"></div>
<button onclick="getSecret()">get secret message</button>
<div id="result"></div>
<script src="jwt-vanilla.js"></script>
</body>
</html>
Notice that we have two buttons that will each run a different function of the included JavaScript file, getToken
and getSecret
.
The vanilla JavaScript JWT part
In order to get our token, we create a POST request with the username and password that is accessible to our backend. Remember we set it to a static array in the last post and we have a user that’s called test
with the password test
.
// make the request to the login endpoint
function getToken() {
var loginUrl = "http://localhost:3000/login"
var xhr = new XMLHttpRequest();
var userElement = document.getElementById('username');
var passwordElement = document.getElementById('password');
var tokenElement = document.getElementById('token');
var user = userElement.value;
var password = passwordElement.value;
xhr.open('POST', loginUrl, true);
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
xhr.addEventListener('load', function() {
var responseObject = JSON.parse(this.response);
console.log(responseObject);
if (responseObject.token) {
tokenElement.innerHTML = responseObject.token;
} else {
tokenElement.innerHTML = "No token received";
}
});
var sendObject = JSON.stringify({name: user, password: password});
console.log('going to send', sendObject);
xhr.send(sendObject);
}
The function will be called when the user clicks the respective button, get user name and password from the input fields and if they are valid credentials, we’ll display the token on the HTML page. Notice that we’re setting the content type to application/json
and that we’re using JSON.parse
to read the response and log it to the console. Typically you would now save the token in localStorage
or another convenient place for future requests.
The next function we write is the one to get access to the data that should be kept private by the API and only communicated to logged in users, those who have a token:
// make the request to the secret API endpoint
function getSecret() {
var url = "http://localhost:3000/secret"
var xhr = new XMLHttpRequest();
var tokenElement = document.getElementById('token');
var resultElement = document.getElementById('result');
xhr.open('GET', url, true);
xhr.setRequestHeader("Authorization", "JWT " + tokenElement.innerHTML);
xhr.addEventListener('load', function() {
var responseObject = JSON.parse(this.response);
console.log(responseObject);
resultElement.innerHTML = this.responseText;
});
xhr.send(null);
}
This request is a GET request, like in our previous Postman example, in order to set the Authorization
header for the xhr request, we use .setRequestHeader
and set the value to JWT
and append the token value we’ve stored in the #token
element on the page.
Notice that for this request, we’re sending null
, because GET requests don’t have a body of data to be transmitted, but simply go to a URL.
If everything went well, you should see something like the following in your browser:
To save the token in your browser you can simply use:
localStorage.setItem('token', token);
and later access it with:
localStorage.getItem('token');
If you liked this post, feel free to subscribe on feedly, follow on twitter or check out some of my other posts. If you think I could have explained or done something better or more beginner friendly, please let me know in the comments!
Thank you for reading! If you have any comments, additions or questions, please leave them in the form below! You can also tweet them at me
If you want to read more like this, follow me on feedly or other rss readers
Great Example! I am reviewing some resources on JWT and the approach of taking a No-Framework Plain Vanilla JavaScript is a great way to explain JWT.
Nice Work!
Hello Jonathan, I tried to save the token in the local storage by doing the following in the POST request:
if (responseObject.token) {
//tokenElement.innerHTML = responseObject.token;
window.localStorage.setItem(‘token’, token);
} else {
tokenElement.innerHTML = “No token received”;
}
And then in the GET request, I put:
xhr.open(‘GET’, url, true);
xhr.setRequestHeader(“Authorization”, “JWT ” + window.localStorage.getItem(‘token’));
But this does not work for some reason.. Could you please tell me what could be going wrong with this?
The console log shows the following error:
VM49:1 Uncaught SyntaxError: Unexpected token U in JSON at position 0
at JSON.parse ()
at XMLHttpRequest. (jwt-vanilla.js:41)
Thank you in advance,
Sara.
Make sure to JSON.stringify the response, since you can’t save complex structures to localStorage
This post and the one before are by far the best ones I have found to explain JWT and authentication. Thank you!
Thanks! I actually made it on an anti tutorial list, but I think it’s not that bad either
An “anti tutorial list”… that’s crazy. In the week since I’ve read this, I’ve come up with a question that I cannot find the answer to and hope you can help. If I understand JWTs correctly, the token allows access to a protected endpoint when it is sent in an HTTP request header. When I access the page at “/some-protected-page” but then refresh it, I get “unauthorized” because the token is not sent in such a case. What options do we have to get around this?
That’s kind of about the architecture of your app, you should not expose API endpoints via URL, but when a path is hit, you bootstrap your frontend app and that one gets the token from localstorage or a cookie and makes the request, does that make sense?
Such a SUPER helpful article!
Hi,
this is a nice course.
Can you give another course about how to decode jwt that have been saved in local storage and dsiplaying into our page.
Thanks before
hello there! i was looking around the internet for hours but all i faced was react, react …. 🙂
thanks for going simple and clean with no framework. i really appreciate it.
thanks again.
Excellent tutorial, Jonathan. Thanks a bunch!
thanks man!
after learning about code and syntax tutorials, now im looking for patterns and how to substitute Vue.js with vanilla js…. this has been eye opening… I have coded my own login using your example and it works… localstorage etc… looking to move forward learning more, thanks!
This must be the single most helpful writeup on JWT on the planet. After plowing through dozens of cryptic tutorials finally some good content. Thanks!
how can we redirect the user to an other page after logging in ??