Deep Dive to OAuth2.0 and JWT (Part 3)
All you need to know about token-based authentication.
Join the DZone community and get the full member experience.
Join For Free
In previous article we have introduced OAuth2.0. In this article let us have a look at JWT.
JSON Web Token (JWT), usually pronounced as “jot,” is an standard () that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. They contain information in terms of claims and are specially used in in space constrained environments such as HTTP. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
The two features of JWT are:
- Compact: Because of its relatively small size, a JWT can be sent through a URL, through a POST parameter, or inside an HTTP header quickly.
- Self-contained: A JWT contains all the required information about an entity to avoid querying a database more than once. The recipient of a JWT also does not need to call a server to validate the token.
These tokens can be signed, encrypted, or both. Signed tokens are used to verify the integrity of tokens while the encrypted token is used to hide the claims.
Note: As the name suggests, JWT are JSON representation, which means it has key value pairs. While there is no limitation on the Key and value as long as they are valid JSON and is in agreement with involved parties. But most of the standard claims follow three letter key formats.
You may also like; Spring Boot Security + JSON Web Token (JWT) ''Hello World'' Example.
Terminology
JWT are represented by the series of three strings separated by a dot (.), in terms of format it typically looks like the following:
AAAAA.BBBBB.CCCCC
The first part is the Header, second is the Payloadand the third is the Signature. Let us have a look on each of these in detail.
Header
Though there is no limitation on what you can have in header, as long as there is mutual agreement between the parties involved. But usually the header consists of two parts.
- typ: represents what is the type of the token and this will be JWT
- alg: it denotes the algorithm used for signing this token, such as HMAC, RSA, SHA
The second part in any JWT represents the payload. This is the part which consists of claims.
Claims are information about the entity and any additional data. In a JWT, the claims are denoted by the Key. These claims are context dependent and should be parsed and understood accordingly, but as per specifications there are some standard rule the apply to a claim.
- The claim name must be unique within a JWT claim set.
- JWT parsers MUST either reject JWTs with duplicate Claim Names or use a JSON parser that returns only the lexically last duplicate member name.
- Applications using JWTs should define which specific claims they use and when they are required or optional.
- All the names are should be short because a core goal of JWTs is to be compact.
And example of the payload could be.
{
"sub": "1234567890",
"name": "Alice",
"admin": true
}
These claims can be subdivided in following three categories.
Registered Claims
There are the claims which are registered in IANA "JSON Web Token Claims" registry. These claims are not mandatory to use or to be implement in all cases, rather they are registered to provide a starting point in for a set of useful, interoperable claims.
If you want, you can skip the description section and jump to Public claims, but I would recommend reading them for better understanding.
Some of these claims that you should be aware of:
- iss (issuer): The "iss" (issuer) claim identifies the principal that issued the JWT. The processing of this claim is generally application specific. The "iss" value is a case-sensitive string containing a String or URI value. Use of this claim is OPTIONAL.
- sub (subject): This claim represents the subject of JWT (the user). The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique. The processing of this claim is generally application specific. The "sub" value is a case-sensitive string containing a String or URI value. Use of this claim is OPTIONAL.
- aud (audience): This claim represents the intended recipient of the JWT. If the party processing the claim does not identify itself with a value in the "aud" claim when this claim is present, then the JWT MUST be rejected. In the general case, the "aud" value is an array of case- sensitive strings, each containing a String or URI value. Use of this claim is OPTIONAL.
- exp (expiration): The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. The processing of the "exp" claim requires that the current date/time MUST be before the expiration date/time listed in the "exp" claim. Usually the value is kept short preferably in seconds. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.
- nbf (not before) : The "nbf" (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. The processing of the "nbf" claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the "nbf" claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.
- iat (issued at): The "iat" (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.
- jti (JWT ID): The "jti" (JWT ID) claim provides a unique identifier for the JWT. The identifier value MUST be assigned in a manner that ensures that there is a negligible probability that the same value will be accidentally assigned to a different data object; if the application uses multiple issuers, collisions MUST be prevented among values produced by different issuers as well. The "jti" claim can be used to prevent the JWT from being replayed. The "jti" value is a case-sensitive string. Use of this claim is OPTIONAL.
Public Claims
These claim names can be defined at will by those using JWTs. However, in order to prevent collisions, any new Claim Name should either be registered in the IANA "JSON Web Token Claims" registry or be a Public Name: a value that contains a Collision-Resistant Name.
In each case, the definer of the name or value needs to take reasonable precautions to make sure they are in control of the part of the namespace they use to define the Claim Name.
Private Claims
This could be thought of as analogous to creating private custom claims to share information specific to your application. These could be any names that are not Registered Claims Names or Public Claims Names. Unlike Public Claim Names, Private Claim Names are subject to collision and should be used with caution.
Signature
The signature is generated by taking the Base64-encoded header and payload. It is then combined with a secret. Finally, this is signed with the algorithm specified in the header.
The signature is used to validate the validate that the sender of the JWT is who it says it is and to ensure that the message wasn't changed along the way. For example, if you are creating a signature for a token using the HMAC SHA256 algorithm, you would do the following:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
Putting it all Together
For example, the JWT signature
eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzYXRpc2giLCJhdWQiOiJteWFwcCIsIkNVU1QiOiIxIiwiZXhwIjoxNTY2MjE0NTg1LCJpc3MiOiJhdXRoLWFwcCJ9.WknG6jiM_vAaflLnKyjlXh5BrM4MUJR9dFrVx-XE3zRVWiyXeIVzI-OomFh0vVHRwrK3-Tttg0HyKBTnCA3mSg
Is encoded using HS512 algorithm and contains below information
Header:
{
"alg": "HS512",
"typ": "JWT"
}
Payload:
{
"sub": "satish",
"aud": "myapp",
"CUST": "1",
"exp": 1566214585,
"iss": "my-auth-app"
}
JWT Use Cases
Authentication
When a user successfully logs in using their credentials, an ID Token is returned. According to the OpenID Connect (OIDC) specs, an ID Token is always a JWT.
Authorization
Once a user is successfully logged in, an application may request to access routes, services, or resources on behalf of that user. To do so, it uses an Access Token, which may be in the form of a JWT. Each subsequent request includes the access token. Single Sign-on (SSO) widely uses JWT because of the small overhead of the format, and its ability to easily be used across different domains.
Information Exchange
JWTs are a good way of securely transmitting information between parties because they can be signed, which means you can be sure that the senders are who they say they are. Additionally, the structure of a JWT allows you to verify that the content hasn't been tampered with.
Why Use JWT?
Decoupling
The biggest advantage of JWTs (in comparison to user session management using an in-memory random token) is that they enable the delegation of the authentication logic to a third-party server that might be:
- A centralized in-house custom developed authentication server.
- More typically, a commercial product like a LDAP capable of issuing JWTs.
- Rr even a completely external third-party authentication provider.
The authentication logic/server could be totally separated from the application server. There is no requirement of sharing password digests between application.
Stateless
The application server can be completely stateless, as JWT’s are self-contained and there is no need to keep tokens in-memory between requests. The authentication server can issue the token, send it back and then immediately discard it!
Compact
JSON is less verbose than XML, so when it is encoded, a JWT is smaller than a SAML token. This makes JWT a good choice to be passed in HTML and HTTP environments.
More Secure
JWT can use a public/private key pair in the form of an X.509 certificate for signing. A JWT can also be symmetrically signed by a shared secret using the HMAC algorithm. And while SAML tokens can use public/private key pairs like JWT, signing XML with XML Digital Signature without introducing obscure security holes is very difficult when compared to the simplicity of signing JSON.
More Common
JSON parsers are common in most programming languages because they map directly to objects. Conversely, XML doesn't have a natural document-to-object mapping. This makes it easier to work with JWT than SAML assertions.
Easier to Process
JWT is designed for internet scale. This means that it is easier to process on user's devices, especially mobile.
JWT: Points to consider
Apart from pros and cons already detailed above, the JWT standard has its own issues:
- If a user account needs to be blocked or deactivated, the application will have to wait for the token to expire for the lockout to be fully effective.
- If a user needs to change their password (for instance in case of account hijacking) and if an authentication has been performed beforehand, then a token generated with the previous password will still be valid until expiry.
- No “refresh” token is specified by the standard implementation. On expiry, the user will therefore have to re-authenticate.
- It is not possible to destroy a token without breaching the “stateless” aspect of JWT tokens, even if the token is deleted from the browser, it is still valid until expiry, so no real logout is possible.
To deal with these challenges, some JWT libraries add a layer above the standard specification and allow refresh token mechanisms as well as some features like forcing a user to re-authenticate, if need be.
JWT: Best practices
Before we get to implementing JWT, let’s cover some best practices to ensure token based authentication is properly implemented in your application.
- Keep it secret. Keep it safe. The signing key should be treated like any other credentials and revealed only to services that absolutely need it.
- Do not add sensitive data to the payload. Tokens are signed to protect against manipulation and are easily decoded. Add the bare minimum number of claims to the payload for best performance and security.
- Give tokens an expiration. Technically, once a token is signed – it is valid forever – unless the signing key is changed, or expiration explicitly set. This could pose potential issues so have a strategy for expiring and/or revoking tokens.
- Embrace HTTPS. Do not send tokens over non-HTTPS connections as those requests can be intercepted and tokens compromised.
- Consider all your authorization use cases. Adding a secondary token verification system that ensure tokens were generated from your server, for example, may not be common practice, but may be necessary to meet your requirements.
Phew..!!! Boy that was lengthy, but hopefully it leaves you with fair understanding of JWT. In next article we shall be looking at how to implement RBAC (Role Based Access Control) using JWT. Please share your valuable comments and questions.
Thank you for reading!
Further Reading
Opinions expressed by DZone contributors are their own.
Trending
-
Seven Steps To Deploy Kedro Pipelines on Amazon EMR
-
Tech Hiring: Trends, Predictions, and Strategies for Success
-
SRE vs. DevOps
-
Deploying Smart Contract on Ethereum Blockchain
Comments