This blog post was originally published in Tes Engineering blog here.
Last year I gave a talk at a Node Girls and Women of Security
meetup to share a few things I have learned about authentication and authorisation since joining our Security Engineering team at Tes.
You can see the video of this talk here.
This post summarises some of the key points made during the talk, alongside some sketch notes and code snippets from an example app.
Authentication and authorisation both relate to the concept of identity. Though the words are similar their meanings are different. Let’s explore how...
Authentication
At the most basic level, we can say that authentication is the process of checking the identity of a user: are you who you say you are?
The most common place that authentication is used is the Login Page of an application. During login user entered credentials are checked against what we have stored in the database. This allows us to verify that we know and trust a user is who they say they are, via, for example, a username and a password.
Although the concept of authentication is straightforward, the technical process of its implementation is typically complex, because it’s absolutely vital to keep users’ data secure. For this reason, many companies opt to use a third party company, for example Google or Auth0, to handle the authentication process for them.
Some companies choose to implement authentication themselves. Here are a couple of golden rules if you go down this route.
Rule 1: Only a customer should know their password
Keeping secret data secure is vitally important for any company to protect their users and their reputation. We want to mitigate against the risk that even if a bad actor got access to a database, they would never get a plain text password. The safest strategy to prevent this is to not store the plain text password at all.
One solution is to hash passwords to carry out a ‘one way’ transformation which turns turns a plain text password into an unrecognisable string. We can use one way encryption to verify the password whilst making it very difficult for a bad actor to transform it to its original plain text value.
We can safely hash passwords by using well maintained and recognised open source libraries, such as bcrypt library. Here’s an example code snippet using the bcrypt library to hash a password before storing it, to keep user credentials safe.
Rule 2: Always validate user input before using it
One of the most common places for an attack on an application is the Login page. Injection or hijacking attacks can aim to make our code do something we did not tell it to do, by sending an instruction where we would expect some user-entered data or credentials.
Never trust the user input to safeguard applications, but rather always validate and transform data before using it.
Again a widely used open source validation library like Joi can help you easily create schemas and transform the data into safe objects.
Authorisation
After a user has been authenticated, we can move onto checking what resource they are authorised to access. It's important to control who-can-access-what to protect data, reputation and revenue.
We can use roles to indicate whether or not a user should have access to a resource. For instance, only an administrator should be able to access the admin page, or only a particular client has access to a specific endpoint.
JSON Web Tokens(JWT)
Json Web Tokens (JWT) help to implement authorisation.
JWT is an open standard that defines a way to securely transmit information between parties as a JSON object.
We can trust this information because it is digitally signed and verified by server-side code.
It helps authorisation systems scale, to ensure only those authorised have access to particular resources, and protect private personal data.
Sign
A JWT is digitally signed with a secret or a public/private key pair that is only known to an application. This means an application can ensure that the JWT was signed from a trusted source (via said secret or corresponding public key) and prevents it from being secretly tampered with.
Here’s an example of using the jsonwebtoken library to sign a JWT where it is then added to a cookie.
Role data is included as part of the JWT, for instance in this example if the username is ‘admin’ then this user gets an ‘admin’ role. Clearly this management of roles is a hack for a toy project, in a real secure system there will be more sophisticated ways of managing admins.
Verify
Once we have a JWT, we can verify that the token is valid on our servers, and only trust it if the JWT hasn’t been tampered with or expired.
The jsonwebtoken library allows us to digitally verify a user or client and effectively manage whether they should have access to a particular resource.
Summary
Aside from learning more about authentication and authorisation, the aim of this blog is to show that as engineers we can learn a few good security practices and have access to many open source libraries to help us build more secure applications.
Writing secure code is absolutely critical for our users - you don’t need to be a security expert to get started.
If you’d like to learn more about some of the topics touched on here, I've listed below some articles that I found useful.
Top comments (6)
I think the info here is a good start for engineers looking for some introductory knowledge. It would still be good to point out that
cookies
are actually separate from the JWT access token exchange. Cookies are neither stored in JWT, nor must JWTs be stored in cookies. It would be good to make sure these are separated topics.Often JWTs are returned with user claims (not cookies) that further identify the user. While it isn't exactly adhering to the OAuth2.0 specs, you can return the JWTs in cookies back to the user. Although more frequently JWTs are returned to the user via url query or hash parameters.
While bcrypt works, I caution about the straw-man laid out here. There's way more that needs to be done that just implementing one function to handle passwords, this actually why most apps on the internet that don't use social logins are so insecure. If you industry regulation (spoiler none of them do) require username and password to login, make sure to do in-depth analysis of all the attack vectors and choosing an appropriate hashing and access token generation strategy. (For instance the one in the article seems to suggest symmetric keys, please use at least RS256 asymmetric keys).
Also I totally agree with the lack of full authorization support in the solution. The optimal strategy for that is a separated handling of roles and permissions. While it is all too common to stuff roles in the JWTs, this is actually a common antipattern. JWTs are meant for identity information, roles are access control. These two resources change at different frequencies and have different cache control requirements as well as security measures implemented.
Additionally granular access is not possible due to header size/cookie size limits when it comes to handling user permissions.
(One side note about using Joi, if you are providing a REST api for your users you want to focus on validating the schema of the payloads using the openapi specification as your primary validation source. While the schema source location is irrelevant, in the past Joi has not done a good job of providing interoperability between the openapi standard and the custom Joi api.)
For jwt, you mentioned, it's either a symmetric key or a asymmetric key pair of which private key is only known by application which signs the key,
I watched this talk and have always implement this:
kid
which is key idThis means if you're scaling horizontally into 10 instances of your identity service, there'll be 10 key pairs, but because secrets aren't stored in a file based storage, but only in primary memory (RAM), the possibility of leak is almost non-existent)
If anyone wants me to write a full article on how I'd implement this, let me know, I do have an opensource project, but also can write a dev.to article.
I've been through 10s of authentication & authorisation articles. This on sums it up very well. I still have an issue which is rather unclear to me.
I'm working on a React app. Regarding CSRF & XSS, most articles tell not to store credentials into localStorage, but "we'll do this for the sake of this article"... So I saw proposals about using cookies (httpOnly, but how does it work for my API then ?), or redux.
I think I'll got to redux, or some Provider like auth provider, but I'm still a bit confused here on how auth provider works. Any pointer/reco about that?
I am also a bit confused regarding the best place/way to store jwt, especially for Cordova/Capacitor projects without any Javascript frameworks. I am currently putting a project on hold because of this. Any ideas would be much appreciated 🙈🙈
Thanks. Really clear and simple explanations with great drawings.
Thanks for this article Charlotte! Definitely clarified the difference between authentication and authorization for me. It also helped that you had nice dinosaur illustrations to support it :)