Lessons Learned - API Security, JWTs and OAuth 2.0
Recently, I’ve been working on REST APIs and API Security. In the process, I’ve learnt a lot and I’ll like to list it down here.
Authentication vs Authorization
Firstly, let’s talk about the two frequently used terms “Authorisation” and “Authentication” of REST APIs. Refer this SO thread to hear it from experts, but in short “Authorization” defines what we can do (access rights, etc.) and Authentication defines who we are. To quote an answer from the thread above,
Authentication = login + password (who you are), Authorization = permissions (what you are allowed to do)
Having a clarity of these two terms would help understand the different workfows in API Security. In order to understand the importance of Token-Based Authentication, we must understand the flaws in traditional Cookies and Session based Authentication.
Cookie-Based Authentication VS Token-Based Authentication
Cookie-based authentication is stateful. This means that an authentication record or session must be kept both server and client-side. The server needs to keep track of active sessions in a database, while on the front-end a cookie is created that holds a session identifier, thus the name cookie based authentication. Let’s look at the flow of traditional cookie-based authentication:
- User enters their login credentials.
- Server verifies the credentials are correct and creates a session which is then stored in a database.
- A cookie with the session ID is placed in the users browser.
- On subsequent requests, the session ID is verified against the database and if valid the request processed. Once a user logs out of the app, the session is destroyed both client-side and server-side.
Token-based authentication has gained prevalence over the last few years. While there are different ways to implement tokens, JWTs have become the de-facto standard.
Token-based authentication is stateless. The server does not keep a record of which users are logged in or which JWTs have been issued. Instead, every request to the server is accompanied by a token which the server uses to verify the authenticity of the request. The token is generally sent as an addition Authorization header in the form of Bearer {JWT}, but can additionally be sent in the body of a POST request or even as a query parameter. Let’s see how this flow works:
- User enters their login credentials.
- Server verifies the credentials are correct and returns a signed token. This token is stored client-side, most commonly in local storage - but can be stored in session storage or a cookie as well.
- Subsequent requests to the server include this token as an additional Authorization header or through one of the other methods mentioned above.
- The server decodes the JWT and if the token is valid processes the request. Once a user logs out, the token is destroyed client-side, no interaction with the server is necessary.
Advantages of Token-based authentication (ex. JWTs)
The key to token-based authentication is that it’s stateless, meaning there is no state being stored on the server regarding the session/login.
What are JWTs?
A JSON Web Token (JWT) is a single string that plays the role of a “token”. JWTs are an encoded representation of a JSON object. Encoded JSON objects are used as access tokens for authentication from the server.
Refer this JWT Introduction from Auth0
to read it in depth.
Basically, JWTs have 3 parts:
- Header : The header contains the key id or jwt type and the hashing algorithm being used in the JSON Web Token.
- Payload : The payload contains the main part of the JWT, called the JWT claims. These claims are broken up into into registered claims, public claims and private claims. Private claims are additional tid-bits of data that are used by producer and consumer and may provide information needed by the application.
- Signature : The Signature, which is the third part of the JSON Web Token is composed of a base64 encoded header and payload. We take these two parts and make a hash using some type of Message Authentication Code (MAC), ex. HMAC RSA SHA-256 with the private key or secret to encrypt and seal the integrity of the message in order to ensure it has not been tampered with.
The key points about a JWT are:
- The token can contain whatever custom data (called claims) we want to put in it.
- The token is cryptographically signed by the server when it is created so that if the token is changed in any way, it is considered invalid.
- The token is encoded using a standard known as base64url so that it can be easily serialized across the internet or even be included in a URL’s querystring. JWTs claims can be decoded easily and anyone who has access of the token can use official JWT Debugger to see the header and payload.
So, if anyone can decode JWT, how are they secure?
I had the same doubt when I started reading about JWTs. This Stack Overflow thread helped clear all the questions I had in my mind. Short answer for this is, if a token is signed, but not encrypted, everyone can read the contents of the token, but when you don’t know the private key, you can’t change it. If you try to change the payload without knowing the secret key, signature won’t be verified and thus token would be rejected by the server.
Where to store Tokens?
There is no need to store JWT tokens on the server side. When the token is generated for the first time, it is sent to client and client can store it inside local storage. When expired, client must delete the token and should send the request to server to issue a new JWT token. This brings me to another question,
So, how to manage Token-Invalidation on Server side?
Read this article. JWTs are way to handle authentication statelessly. So, there is no need to store the state of token on server. There is a claim in JWT called exp
which defines the timestamp at which your token expires. As soon as token expires, it should supposedly throw an exception. JJWT handles this well and throws a JWTTokenExpired
Exception whenever token expires.
So, in order to handle the stateless nature of JWTs, client must simply delete the token whenever user wants to log out. In this way, client would need to get a new token from server.
However, if you still think someone has stolen the token before user logged out and is sending requests to server, you can refer
Integrating OAuth 2.0 and JWTs
Firstly, we need to understand that OAuth 2.0 and JWTs are not exclusive. We can use Oauth2 with JWT tokens. OAuth 2.0 defines an authorization protocol and specifies how tokens are transferred, JWT defines a token format. OAuth 2.0 states different authorisation flows. They are :
- Authorization code grant
- Implicit grant
- Resource owner credentials grant
- Client credentials grant
- Refresh token grant
Read OAauth 2.0 in detail in order to understand the framework in detail. But the point that I’ll like to emphasize here is that OAuth does not specify a fixed format of Tokens. So, we can simply format the OAuth access token according to the rules defined for JWT. Since the OAuth spec leaves the token format open, this is perfectly fine. A JWT token is self-contained and contains an expiration date. An access token in OAuth also has this property, but it should be possible to revoke the access token. The mechanisms for token revocation are neither specified for JWT nor with OAuth, you will need to specify and implement this.
Following articles would help clarify the above :
Additional Resources :
- JWT Spec
- 100% Stateless with JWT (JSON Web Token) by Hubert Sablonnière
- Token Authentication for Java Applications
- OAuth 2.0 and OpenID
- JJWT Library (Java)
- Cookies Vs Tokens
- Best practice for REST token-based authentication with JAX-RS and Jersey
- Generating Secret Key for JWTs
- Secret Rotation for JWTs
- Role Based Authorisation in JWTs