propro-utils 1.3.46 → 1.3.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/middlewares/access_token.js +80 -32
- package/middlewares/account_info.js +53 -45
- package/package.json +1 -1
- package/src/server/index.js +0 -7
|
@@ -1,39 +1,87 @@
|
|
|
1
|
-
require(
|
|
2
|
-
const axios =
|
|
3
|
-
const {getOrSetCache} = require(
|
|
1
|
+
require('dotenv').config();
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const { getOrSetCache } = require('../utils/redis');
|
|
4
|
+
const { checkIfUserExists } = require('./account_info');
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Middleware for authenticating and authorizing API requests.
|
|
8
|
+
* It validates an access token and checks for required permissions using both a Redis cache
|
|
9
|
+
* and propro authentication service.
|
|
10
|
+
*
|
|
11
|
+
* @param {object} redisClient - The Redis client used for caching permission data.
|
|
12
|
+
* @param {object} userSchema - The user schema/model object.
|
|
13
|
+
* @param {string[]} [requiredPermissions=[]] - An array of permissions required to access the endpoint.
|
|
14
|
+
* This function first attempts to retrieve the account's permissions from the cache.
|
|
15
|
+
* If the cache is empty or expired, it fetches permissions from propro authentication service
|
|
16
|
+
* and updates the cache. If the access token is invalid or does not grant the required permissions,
|
|
17
|
+
* the request is rejected.
|
|
18
|
+
*
|
|
19
|
+
* @returns {function} A middleware function that takes Express.js request (req), response (res),
|
|
20
|
+
* and next function parameters. The middleware validates the access token and permissions.
|
|
21
|
+
* If validation is successful, it adds the account ID and req.user to the request object (req.account) and
|
|
22
|
+
* calls `next()` to pass control to the next middleware. If validation fails, it responds with
|
|
23
|
+
* an error message and a 403 status code.
|
|
24
|
+
*
|
|
25
|
+
* Usage of the middleware requires an environment variable `AUTH_URL` to be set, pointing to the
|
|
26
|
+
* authentication service's URL. The function leverages async/await for asynchronous operations
|
|
27
|
+
* and tries to handle errors gracefully, reporting them through the next middleware in the chain.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* // Applying authValidation middleware
|
|
31
|
+
* const redisClient = require('./redisClient');
|
|
32
|
+
* app.use('/api/protected-route', authValidation(redisClient, ['admin', 'user']), (req, res) => {
|
|
33
|
+
* res.json({ message: 'You have access to protected data' });
|
|
34
|
+
* });
|
|
35
|
+
*/
|
|
36
|
+
const authValidation = (redisClient, userSchema, requiredPermissions = []) => {
|
|
37
|
+
return async (req, res, next) => {
|
|
38
|
+
try {
|
|
39
|
+
const accessToken =
|
|
40
|
+
req.cookies['x-access-token'] ||
|
|
41
|
+
req.headers.authorization?.split(' ')[1];
|
|
9
42
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
43
|
+
if (!accessToken) {
|
|
44
|
+
return res.status(403).json({ error: 'Access token is required' });
|
|
45
|
+
}
|
|
13
46
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
47
|
+
const fetchPermission = async () => {
|
|
48
|
+
const response = await axios.post(
|
|
49
|
+
`${process.env.AUTH_URL}/api/v1/auth/validateToken`,
|
|
50
|
+
{
|
|
51
|
+
accessToken: accessToken,
|
|
52
|
+
requiredPermissions: requiredPermissions,
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
return response.data;
|
|
56
|
+
};
|
|
57
|
+
const cacheKey = `account:permissions:${accessToken}`;
|
|
58
|
+
const { accountId, validPermissions } = await getOrSetCache(
|
|
59
|
+
redisClient,
|
|
60
|
+
cacheKey,
|
|
61
|
+
fetchPermission,
|
|
62
|
+
1800
|
|
63
|
+
);
|
|
23
64
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
65
|
+
if (!validPermissions) {
|
|
66
|
+
return res.status(403).json({ error: 'Invalid permissions' });
|
|
67
|
+
}
|
|
27
68
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
69
|
+
req.account = accountId;
|
|
70
|
+
|
|
71
|
+
const user = await checkIfUserExists(userSchema, accountId);
|
|
72
|
+
if (!user) {
|
|
73
|
+
return res.status(403).json({ error: 'User not found' });
|
|
74
|
+
}
|
|
75
|
+
console.log('user', user);
|
|
76
|
+
req.user = user.id;
|
|
77
|
+
next();
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error.response && error.response.status) {
|
|
80
|
+
next(new Error(error.response.data.message));
|
|
81
|
+
}
|
|
82
|
+
next(new Error('Error validating token'));
|
|
83
|
+
}
|
|
84
|
+
};
|
|
37
85
|
};
|
|
38
86
|
|
|
39
|
-
module.exports = authValidation;
|
|
87
|
+
module.exports = authValidation;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
require(
|
|
2
|
-
const axios =
|
|
3
|
-
const {getOrSetCache} = require(
|
|
1
|
+
require('dotenv').config();
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const { getOrSetCache } = require('../utils/redis');
|
|
4
4
|
const { v4: uuidv4 } = require('uuid');
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -14,62 +14,70 @@ const { v4: uuidv4 } = require('uuid');
|
|
|
14
14
|
* @throws {Error} - If there is an error retrieving the account profile data or validating the token
|
|
15
15
|
*/
|
|
16
16
|
const getAccountProfile = async (redisClient, userSchema, accountId) => {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
try {
|
|
18
|
+
const accessToken =
|
|
19
|
+
req.cookies['x-access-token'] || req.headers.authorization?.split(' ')[1];
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const fetchPermission = async () => {
|
|
25
|
-
const response = await axios.get(`${process.env.AUTH_URL}/api/v1/account/profile`, {
|
|
26
|
-
headers: {
|
|
27
|
-
Authorization: `Bearer ${accessToken}`,
|
|
28
|
-
},
|
|
29
|
-
params: {
|
|
30
|
-
accountId
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
return response.data;
|
|
34
|
-
}
|
|
35
|
-
const cacheKey = `account:info:${accountId}`;
|
|
36
|
-
const { profileData } = await getOrSetCache(redisClient, cacheKey, fetchPermission, 1800);
|
|
21
|
+
if (!accessToken) {
|
|
22
|
+
throw new Error('Access token is required');
|
|
23
|
+
}
|
|
37
24
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
25
|
+
const fetchPermission = async () => {
|
|
26
|
+
const response = await axios.get(
|
|
27
|
+
`${process.env.AUTH_URL}/api/v1/account/profile`,
|
|
28
|
+
{
|
|
29
|
+
headers: {
|
|
30
|
+
Authorization: `Bearer ${accessToken}`,
|
|
31
|
+
},
|
|
32
|
+
params: {
|
|
33
|
+
accountId,
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
return response.data;
|
|
38
|
+
};
|
|
39
|
+
const cacheKey = `account:info:${accountId}`;
|
|
40
|
+
const { profileData } = await getOrSetCache(
|
|
41
|
+
redisClient,
|
|
42
|
+
cacheKey,
|
|
43
|
+
fetchPermission,
|
|
44
|
+
1800
|
|
45
|
+
);
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
if (!profileData) {
|
|
48
|
+
throw new Error('Invalid permissions');
|
|
49
|
+
}
|
|
43
50
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
return profileData;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (error.response && error.response.status) {
|
|
54
|
+
throw new Error(error.response.data.message);
|
|
55
|
+
}
|
|
56
|
+
throw new Error('Error validating token');
|
|
57
|
+
}
|
|
50
58
|
};
|
|
51
59
|
|
|
52
|
-
|
|
53
60
|
/**
|
|
54
61
|
* Checks if a user exists based on the given account ID.
|
|
55
62
|
* If the user does not exist, creates a new user with the given account ID.
|
|
56
63
|
*
|
|
57
64
|
* @param {Schema} userSchema - The user schema to perform the operations on.
|
|
58
65
|
* @param {string} accountId - The account ID of the user to check/create.
|
|
59
|
-
* @returns {Promise<
|
|
66
|
+
* @returns {Promise<user>} - A promise that resolves once the check/create operation is done.
|
|
60
67
|
*/
|
|
61
68
|
const checkIfUserExists = async (userSchema, accountId) => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
const user = await userSchema.findOne({ accountId });
|
|
70
|
+
if (!user) {
|
|
71
|
+
await userSchema.create({
|
|
72
|
+
accountId,
|
|
73
|
+
id: uuidv4(),
|
|
74
|
+
verified: false,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return user;
|
|
70
78
|
};
|
|
71
79
|
|
|
72
80
|
module.exports = {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
81
|
+
getAccountProfile,
|
|
82
|
+
checkIfUserExists,
|
|
83
|
+
};
|
package/package.json
CHANGED
package/src/server/index.js
CHANGED
|
@@ -107,7 +107,6 @@ function proproAuthMiddleware(options = {}, userSchema) {
|
|
|
107
107
|
return res.status(400).send('No code received');
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
console.log('code', code);
|
|
111
110
|
const { tokens, account, redirectUrl } = await exchangeToken(
|
|
112
111
|
authUrl,
|
|
113
112
|
code,
|
|
@@ -116,14 +115,8 @@ function proproAuthMiddleware(options = {}, userSchema) {
|
|
|
116
115
|
redirectUri
|
|
117
116
|
);
|
|
118
117
|
|
|
119
|
-
console.log('account', account);
|
|
120
|
-
console.log('tokens', tokens);
|
|
121
|
-
console.log('redirectUrl', redirectUrl);
|
|
122
|
-
|
|
123
118
|
await checkIfUserExists(userSchema, account.accountId);
|
|
124
119
|
|
|
125
|
-
console.log('User exists');
|
|
126
|
-
|
|
127
120
|
const currentDateTime = new Date();
|
|
128
121
|
|
|
129
122
|
const refreshMaxAge =
|