JSON Web Token is a standard in RFC7519 that uses JSON to pass data to determine whether a user is logged in.

Before JWT, sessions were used for user authentication.

The following code is written in javascript.

  • Original link: Shan Yue’s blog

session

The traditional way to check whether to log in is to use session + Token.

The token is used as the user status certificate on the client. The browser usually stores the token in localStorage or cookies.

Session refers to the redis or SQL-like database used on the server to store the key-value pairs of user_id and token. The basic working principle is as follows.

Use sessions on the server side to store key-value pairs

const sessions = {
  "ABCED1": 10086."CDEFA0": 10010
}
Copy the code

Each time the client requests permission data with a token, the server obtains the user_id based on the token and sessions to complete the authentication process

function getUserIdByToken (token) {
  return sessions[token]
}
Copy the code

If stored in cookies is often heard session + cookie login scheme. In fact, storage in cookies, localStorage, even IndexedDB or WebSQL have advantages and disadvantages, the core idea is the same.

Cookies and the pros and cons of tokens are discussed in token Authetication vs. Cookies.

If cookies are not used, localStorage + Authorization can be adopted for authentication, which is more stateless

// The HTTP Header, which needs to carry the Authorization Header each time the permission interface is requested
const headers = {
  Authorization: `Bearer ${localStorage.get('token')}`
}
Copy the code

A front-end repository, localForage, is recommended, using IndexedDB, WebSQL, and IndexedDB for key-value pair storage.

Stateless login

A session needs to keep user and token information in the database, so it is called stateful.

Think about how you can log in without being a user in the database.

The first method: the front-end directly passes the user_id to the server

The disadvantages are also particularly obvious, easy to be tampered with by users into any user_ID, permission Settings are meaningless. But you’re on the right track. Keep going.

Improved: Symmetric encryption for user_id

The server encrypts the user_id symmetrically and returns it as a token to the client as the user status certificate. Slightly stronger than above, but because of symmetric encryption, it is important to choose the right algorithm and key

Improvements: User_id does not need to be encrypted, only need to be signed, to ensure that the tamper

This is the idea of JWT: the user_id, encryption algorithm and signature form the token and store it together with the client. Whenever the client requests the interface, it carries the token, and the server resolves the encryption algorithm and user_id according to the token to determine whether the signature is consistent.

Json Web Token

  • jwt.io

The JWT consists of three parts: Header, Payload, and Signature. Spliced together.

Header

Header consists of asymmetric encryption algorithms and types, as follows

const header = {
  // Encryption algorithm
  alg: 'HS256'.type: 'jwt'
}
Copy the code

Payload

Payload consists of Registered claims and data that needs to be communicated. These data fields are also called claims.

An important part of Registered Claims is that the “exp” Claim stands for expiration time, which is set when the user logs in.

const payload = {
  // indicates the JWT creation time
  iat: 1532135735.// Indicates the JWT expiration time
  exp: 1532136735.// User ID for communication
  user_id: 10086
}
Copy the code

Signature

Signature is calculated by Header, Payload, and secretOrPrivateKey. SecretOrPrivateKey is stored on the server as sensitive data. Consider Using Vault Secret or K8S Secret

For secretOrPrivateKey, it is a string if the encryption algorithm is HMAC, or PrivateKey if RSA or ECDSA is used.

// HMACSHA256 algorithm for signature, secret can not be leaked
const sign = HMACSHA256(base64.encode(header) + '. ' + base64.encode(payload), secret)

// JWT is made up of three parts
const jwt = base64.encode(header) + '. ' + base64.encode(payload) + '. ' + sign
Copy the code

According to the JWT rule, the client can parse out the payload. Therefore, do not carry sensitive data, such as user passwords, in the payload

The calibration process

In the generation rule, the first two parts of JWT are base64 encoding of header and payload.

After receiving the token from the client, the server parses the first two parts to obtain the header and payload, uses the algorithm in the header and the secretOrPrivateKey to sign the signature, and checks whether the signature is consistent with the signature carried in the JWT.

How to determine the expiration of token?

application

As can be seen from the above, JWT does not encrypt the data, but signs the data to ensure that it is not tampered with. In addition to login, it can also be used for email verification, graphic verification code, and SMS verification code.

Graphic verification code

If the password is entered incorrectly for too many times during login, a graphic verification code will appear.

The principle of graphic verification code is to give a graph to the client, and save the string paired with the image on the server. In the past, it was mostly realized through session.

The string paired with the verification code can be used as secret for stateless verification.

const jwt = require('jsonwebtoken')

// Assume that the verification code is a character verification code, and the character is ACDE
const token = jwt.sign({}, secrect + 'ACDE', { expiresIn: 60 * 10 })

const codeImage = getImageFromString('ACDE')

// The response to the front-end
const res = {
  // The token of the image from which the verification code sent by the front end can be verified
  token,
  // Captcha image
  codeImage,
}
Copy the code

The SMS verification code is the same as the graphical verification code

Mail check

Now the website will verify the mailbox after the successful registration. The specific method is to send a link to the mailbox, and the user clicks the link to verify the success.

// Bind the mailbox and user ID together
const code = jwt.sign({ email, userId }, secret, { expiresIn: 60 * 30 })

// Verify the verification code at this link
const link = `https://example.com/code=${code}`
Copy the code

Stateless vs. stateful

StateLess component and stateful Component are also compared in other technical directions, such as React’s stateLess component and stateful Component. Side effects in functional programming can be understood as states. HTTP is also a stateLess protocol. You need headers and cookies to carry state.

In user authentication, stateless refers to whether it depends on external data stores, such as mysql and Redis.

case

Consider the following login questions: How to use session and JWT implementations to get a clearer picture of JWT usage scenarios

How do I invalidate the token when a user logs out

Because JWT is stateless and does not save user device information, it cannot simply use it to complete the above problems. We can use the database to save some states to complete the problems.

  • session: Clear the token corresponding to user_id
  • jwt: Redis is used to maintain a blacklist. When a user logs out, the token is added to the blacklist. The expiration time is the same as that of the JWT.

How to allow users to log in only on one device, such as wechat

  • session: The SQL database is used to add the token field and index to the user database table, reset the token field each time you log in, and search the user_id according to the token when the permission interface is required for each request
  • jwt: If an SQL class database is used, add the token field to the user database table (without adding an index), reset the token field each time you log in, and obtain the user_id according to JWT for each request that requires the permission interface. Check whether the tokens are consistent based on the user_id. You can also use the counter method, the following question.

Sessions are a little easier for this requirement, since JWT also relies on databases.

How to allow users to log in only from the last five devices, such as many players

  • session: Create a token database table using an SQL database. The table contains the id, token, and user_id fields. The relationship between user and token is 1: M. Add a row for each login. Obtain the user_id based on the token, and then obtain the number of device logins of the user based on the user_id. If the number of device logins exceeds 5, delete the line with the minimum ID.
  • jwtThe default value is 0. The default value of the count field increases by 1 for each login. The Payload of the JWT created for each login carries the current_count value as the user’s count value. On each request to the permission interface, obtain count and current_count based on JWT, and obtain count based on user_id from the user table to determine whether the difference between current_count and JWT is less than 5

JWT is slightly simpler for this requirement, and using sessions requires maintaining an additional token table.

How do you allow users to log in only from the last five devices, and have a user kick all but one of their existing devices, such as many players

  • session: Based on the previous question, delete all token records other than the device.
  • jwt: Based on the previous question, add count + 5 and reassign the device to the new count.

How do I display the list of devices that the user has logged in to? How do I kick out a specific user

  • session: Add device to the token table
  • jwt: The device list information needs to be kept by the server, which is the same as session. JWT is meaningless

conclusion

From the above questions, stateless JWT is a good choice if you don’t need to control the number of logged devices and device information. Session is a good choice when you need to add additional state support to JWT once device information is involved, adding complexity to authentication.

JWT is not a panacea. Whether to use JWT depends on business requirements.


I am Shanyue, a programmer who likes running and climbing mountains. I will regularly share full stack articles in my personal official account. If you are interested in full stack interviews, front-end engineering, GraphQL, Devops, personal server operations and microservices, please follow me