Authorization
Session based authorization
Earlier developers were using session
based authorization.
After the user logs in, a session Id is sent to client in form of a cookie and when there is a user request, the server checks for that cookie and gives access and response.
Drawbacks
- The problem is sessions are stored in the server, and when the app scalability increases, user requests also increase which eventually put loads on the server.
- It doesn't support multiple servers. For example, suppose user logs in to the 'Amazon' website then the session id is passed to the client, but if the same user wants to open 'Amazon Prime', the user has to log in once again to create a session id.
JWT based authorization
In this process, when the user logs in, the user information is signed with a secret (which is stored in the server) and a JSON web token is generated and it is sent to the client. Generally, it is stored in browser localStorage, whenever a user sends a request to the server in which the authorization token is present in the headers
of the request , the server just verifies that JWT token with the secret it has by unsigning that JWT token and gives users access.
Why JWT?
- All the user state is stored on the client-side, so there is no load on the server.
- It supports multiple servers by just passing the secret to multiple servers for verification.
Implementation using AccessToken and RefreshToken
-
RefreshToken
is used to generate newAccessToken
when it expires.AccessToken
contains the user state or information andRefreshToken
. - When user logs in, server creates
AccessToken
andRefreshToken
stores the userId or something which when unsigns by someone, the person would not understand the context, and it returns response to the client. UsuallyAccessToken
expires after a short time andRefreshToken
after a long time.
router.post('/signin', async (req, res) => {
try {
const user = await req.body;
const { email, password } = user;
const userEmail = email;
await User.findOne({ email: userEmail })
.exec((err, user) => {
if (err || user === null) {
return res.status(400).json({
message: "user does not exists!",
});
} else if (!user.authenticate(password)) {
return res.status(401).json({
message: "please enter the correct password!",
});
}
const accessToken = jwt.sign(
{ user },
process.env.ACCESS_TOKEN_SECRET,
{
expiresIn: "15m",
}
);
const refreshToken = jwt.sign(
{ userId: user._id },
process.env.REFRESH_TOKEN_SECRET,
{
expiresIn: "7d",
}
);
res.json({ user, accessToken, refreshToken });
});
} catch (error) {
res.status(400).json({
message: error.message,
});
}
})
- Then we can make a middleware for the verification of the token and put it in any route for authorization.
exports.authorizeToken = async (req, res, next) => {
if (
!req.headers["authorization"] &&
typeof req.headers["authorization"] !== "string"
) {
return res.status(401).json({
message: "No tokens found",
});
}
try {
const accessToken = req.headers["authorization"].split(" ")[1];
const { user } = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET);
req.user = user;
return next();
} catch (error) {
res.status(401).json({
message: "token cannot be verified! please check it again.",
});
}
};
- Before
accessToken
gets expire, we generate anotheraccessToken
in server from therefreshToken
we got from the client's request. Then we send both the tokens to the client.
router.post('/token/access', (req, res) => {
if (
!req.headers["refresh-token"] &&
typeof req.headers["refresh-token"] !== "string"
) {
return res.status(401).json({
message: "No refresh tokens found",
});
}
try {
const oldRefreshToken = req.headers["refresh-token"].split(" ")[1];
const { userId } = jwt.verify(
oldRefreshToken,
process.env.REFRESH_TOKEN_SECRET
);
const refreshToken = jwt.sign(
{ userId: userId },
process.env.REFRESH_TOKEN_SECRET,
{
expiresIn: "7d",
}
);
const accessToken = jwt.sign(
{ user },
process.env.ACCESS_TOKEN_SECRET,
{
expiresIn: "15m",
}
);
res.json({ accessToken, refreshToken });
} catch (error) {
res.status(401).json({
message: "refresh token cannot be verified! please check it again.",
});
}
})
- The
refreshToken
is generally stored in thelocalStorage
of the browser and theaccessToken
is stored in the headers of the API requests.
Voila! your app is now secured with the authentication
and authorization
.
Hope you find this useful.
references
cover image : https://jwt.io/
youtube: https://www.youtube.com/watch?v=7Q17ubqLfaM&t=332s
Top comments (0)