Skip to content

Commit 2a68e22

Browse files
committed
graphql: Added graphql support for the user
1 parent 7afbc53 commit 2a68e22

File tree

7 files changed

+163
-22
lines changed

7 files changed

+163
-22
lines changed

api/src/graphql/resolvers.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
1+
const {
2+
NameType,
3+
PasswordType,
4+
} = require('./resolvers/custom-types/custom-types');
5+
const {
6+
query: userQueries,
7+
mutation: userMutations,
8+
} = require('./resolvers/user/index');
9+
110
const resolvers = {
211
Query: {
312
hello: () => 'Hello world!',
13+
14+
...userQueries,
415
},
16+
Mutation: {
17+
...userMutations,
18+
},
19+
20+
NameType,
21+
PasswordType,
522
};
623

724
module.exports = { resolvers };
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const { RegularExpression } = require('graphql-scalars');
2+
3+
const NameType = new RegularExpression(
4+
'NameType',
5+
/^[a-zA-ZÀ-ÿ ]{3,80}$/,
6+
'Name must contain only letters and spaces.',
7+
);
8+
9+
const PasswordType = new RegularExpression(
10+
'PasswordType',
11+
/^(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=:|,.<>\\/?])[a-zA-Z0-9!@#$%^&*()_+\-=:|,.<>\\/?]{8,35}$/,
12+
'Password must contain at least 1 capital, 1 number and 1 special character.',
13+
);
14+
15+
module.exports = {
16+
NameType,
17+
PasswordType,
18+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const {
2+
getAll: getAllUsers,
3+
login,
4+
getByEmail,
5+
create: createUser,
6+
} = require('./user.resolvers');
7+
8+
const query = {
9+
users: getAllUsers,
10+
user: getByEmail,
11+
};
12+
13+
const mutation = {
14+
signUp: createUser,
15+
login,
16+
};
17+
18+
module.exports = { query, mutation };
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const {
2+
getAllUsers,
3+
login: loginUser,
4+
getUserByEmail,
5+
createUser,
6+
} = require('../../../modules/user/user.service');
7+
const {
8+
validateGraphQLSession,
9+
} = require('../../../middlewares/authentication');
10+
11+
const getAll = async (_, __, context) => {
12+
await validateGraphQLSession(context.req);
13+
14+
return getAllUsers();
15+
};
16+
17+
const getByEmail = async (_, { email }, context) => {
18+
await validateGraphQLSession(context.req);
19+
20+
const user = await getUserByEmail(email);
21+
return user;
22+
};
23+
24+
const create = async (_, { name, email, password }) => {
25+
return createUser({ name, email, password });
26+
};
27+
28+
const login = async (_, { email, password }) => {
29+
return loginUser({ email, password });
30+
};
31+
32+
module.exports = {
33+
getAll,
34+
getByEmail,
35+
login,
36+
create,
37+
};

api/src/graphql/schema.graphql

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
type Query {
2+
hello: String!
3+
4+
# User queries
5+
users: [User!]!
6+
user(email: EmailAddress!): User!
7+
}
8+
9+
type Mutation {
10+
# User mutations
11+
signUp(name: NameType!, email: EmailAddress!, password: PasswordType!): User!
12+
login(email: EmailAddress!, password: String!): LoginResponse!
13+
}
14+
15+
type User {
16+
id: UUID!
17+
name: String!
18+
email: EmailAddress!
19+
role: String!
20+
createdAt: Date!
21+
}
22+
23+
type LoginResponse {
24+
user: User!
25+
accessToken: String!
26+
}
27+
28+
scalar NameType
29+
scalar PasswordType

api/src/middlewares/authentication.js

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,45 @@ const limiter = (limit, windowMs, message) =>
1515
legacyHeaders: false,
1616
});
1717

18-
const validateSession = async (req, res, next) => {
18+
const handlerValidateSession = async (req) => {
19+
let decodedAccessToken = null;
20+
let accessToken = null;
21+
1922
try {
20-
const accessToken = req.cookies?.accessToken;
21-
if (!accessToken) throw Boom.unauthorized('Access token was not provided');
23+
if (req.cookies.accessToken) {
24+
accessToken = req.cookies.accessToken;
25+
} else if (req.headers.authorization) {
26+
const authHeader = req.headers.authorization;
27+
if (authHeader.startsWith('Bearer ')) {
28+
[, accessToken] = authHeader.split(' ');
29+
} else {
30+
throw Boom.unauthorized('Invalid authorization header format');
31+
}
32+
}
2233

23-
let decodedAccessToken = null;
34+
decodedAccessToken = jwt.verify(accessToken, config.jwtAccessSecret);
35+
} catch (error) {
36+
throw Boom.badRequest(`Error verifying the accessToken: ${error.message}`);
37+
}
2438

25-
try {
26-
decodedAccessToken = jwt.verify(accessToken, config.jwtAccessSecret);
27-
} catch (error) {
28-
throw Boom.badRequest(
29-
`Error verifying the accessToken: ${error.message}`,
30-
);
31-
}
39+
if (!decodedAccessToken?.sub) {
40+
throw Boom.unauthorized('Access token has expired');
41+
}
3242

33-
if (!decodedAccessToken?.sub)
34-
throw Boom.unauthorized('Access token has expired');
43+
const user = await userRepository.findOneToValidateSession(
44+
decodedAccessToken.sub,
45+
accessToken,
46+
);
3547

36-
const user = await userRepository.findOneToValidateSession(
37-
decodedAccessToken.sub,
38-
accessToken,
39-
);
40-
if (!user?.id) throw Boom.unauthorized('Invalid access token');
48+
if (!user?.id) throw Boom.unauthorized('Invalid access token');
49+
50+
return { user, decodedAccessToken, accessToken };
51+
};
52+
53+
const validateSession = async (req, res, next) => {
54+
try {
55+
const { decodedAccessToken, accessToken } =
56+
await handlerValidateSession(req);
4157

4258
req.user = decodedAccessToken;
4359
req.tokens = { accessToken };
@@ -47,4 +63,13 @@ const validateSession = async (req, res, next) => {
4763
}
4864
};
4965

50-
module.exports = { validateSession, limiter };
66+
const validateGraphQLSession = async (req) => {
67+
try {
68+
const { user } = await handlerValidateSession(req);
69+
return user;
70+
} catch (error) {
71+
throw Boom.unauthorized('GraphQL session validation failed');
72+
}
73+
};
74+
75+
module.exports = { validateSession, limiter, validateGraphQLSession };

0 commit comments

Comments
 (0)