Skip to content

Commit 95a55b6

Browse files
committed
chore: type controllers/users
1 parent 13b5826 commit 95a55b6

File tree

2 files changed

+55
-29
lines changed

2 files changed

+55
-29
lines changed

server/controllers/users.ts

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ const { User } = models;
3636
/**
3737
* Check existence of a user based on email
3838
*/
39-
export const exists = async (req, res) => {
40-
const email = req.query.email.toLowerCase();
39+
export const exists = async (req: express.Request, res: express.Response) => {
40+
const email = (req.query.email as string).toLowerCase();
4141
if (!isValidEmail(email)) {
42-
return res.send({ exists: false });
42+
res.send({ exists: false });
43+
return;
4344
} else {
4445
const rateLimit = new RateLimit(
4546
`user_email_search_ip_${req.ip}`,
@@ -55,7 +56,8 @@ export const exists = async (req, res) => {
5556
attributes: ['id'],
5657
where: { email },
5758
});
58-
return res.send({ exists: Boolean(user) });
59+
res.send({ exists: Boolean(user) });
60+
return;
5961
}
6062
};
6163

@@ -66,7 +68,7 @@ export const exists = async (req, res) => {
6668
* create a new account. In the future once signin.js is fully deprecated (replaced by signinV2.js)
6769
* this function should be refactored to remove createProfile.
6870
*/
69-
export const signin = async (req, res, next) => {
71+
export const signin = async (req: express.Request, res: express.Response, next: express.NextFunction) => {
7072
const { redirect, websiteUrl, sendLink, resetPassword, createProfile = true } = req.body;
7173
try {
7274
const rateLimit = new RateLimit(
@@ -76,40 +78,45 @@ export const signin = async (req, res, next) => {
7678
true,
7779
);
7880
if (!(await rateLimit.registerCall())) {
79-
return res.status(403).send({
81+
res.status(403).send({
8082
error: { message: 'Rate limit exceeded' },
8183
});
84+
return;
8285
}
8386
let user = await models.User.findOne({ where: { email: req.body.user.email.toLowerCase() } });
8487
if (!user && !createProfile) {
85-
return res.status(400).send({
88+
res.status(400).send({
8689
errorCode: 'EMAIL_DOES_NOT_EXIST',
8790
message: 'Email does not exist',
8891
});
92+
return;
8993
} else if (!user && createProfile) {
9094
user = await models.User.createUserWithCollective(req.body.user);
9195
} else if (!user.CollectiveId || user.data?.requiresVerification === true) {
92-
return res.status(403).send({
96+
res.status(403).send({
9397
errorCode: 'EMAIL_AWAITING_VERIFICATION',
9498
message: 'Email awaiting verification',
9599
});
100+
return;
96101
}
97102

98103
// If password set and not passed, challenge user with password
99104
if (user.passwordHash && !sendLink && !resetPassword) {
100105
if (!req.body.user.password) {
101-
return res.status(403).send({
106+
res.status(403).send({
102107
errorCode: 'PASSWORD_REQUIRED',
103108
message: 'Password requested to complete sign in.',
104109
});
110+
return;
105111
}
106112
const validPassword = await bcrypt.compare(req.body.user.password, user.passwordHash);
107113
if (!validPassword) {
108114
// Would be great to be consistent in the way we send errors
109115
// This is what works best with Frontend today
110-
return res.status(401).send({
116+
res.status(401).send({
111117
error: { errorCode: 'PASSWORD_INVALID', message: 'Invalid password' },
112118
});
119+
return;
113120
}
114121

115122
const twoFactorAuthenticationEnabled = parseToBoolean(config.twoFactorAuthentication.enabled);
@@ -131,12 +138,14 @@ export const signin = async (req, res, next) => {
131138
},
132139
auth.TOKEN_EXPIRATION_2FA,
133140
);
134-
return res.send({ token });
141+
res.send({ token });
142+
return;
135143
} else {
136144
// Context: this is token generation when using a password and no 2FA
137145
const token = await user.generateSessionToken({ createActivity: true, updateLastLoginAt: true, req });
138146
auth.setAuthCookie(res, token);
139-
return res.send({ token });
147+
res.send({ token });
148+
return;
140149
}
141150
}
142151

@@ -154,9 +163,10 @@ export const signin = async (req, res, next) => {
154163
);
155164
} catch (e) {
156165
reportErrorToSentry(e, { user });
157-
return res.status(500).send({
166+
res.status(500).send({
158167
error: { message: 'Error sending reset password email' },
159168
});
169+
return;
160170
}
161171
} else {
162172
const collective = await user.getCollective();
@@ -175,17 +185,18 @@ export const signin = async (req, res, next) => {
175185
);
176186
} catch (e) {
177187
reportErrorToSentry(e, { user });
178-
return res.status(500).send({
188+
res.status(500).send({
179189
error: { message: 'Error sending login email' },
180190
});
191+
return;
181192
}
182193

183194
// For e2e testing, we enable testuser+(admin|member)@opencollective.com to automatically receive the login link
184195
if (config.env !== 'production' && user.email.match(/.*test.*@opencollective.com$/)) {
185-
return res.send({ success: true, redirect: loginLink });
196+
res.send({ success: true, redirect: loginLink });
197+
return;
186198
}
187199
}
188-
189200
res.send({ success: true });
190201
} catch (e) {
191202
next(e);
@@ -529,25 +540,27 @@ export async function verifyEmail(req: express.Request, res: express.Response) {
529540
*
530541
* B) If no 2FA, we send back a "session" token
531542
*/
532-
export const exchangeLoginToken = async (req, res, next) => {
543+
export const exchangeLoginToken = async (req: express.Request, res: express.Response, next: express.NextFunction) => {
533544
const rateLimit = new RateLimit(
534545
`user_exchange_login_token_ip_${req.ip}`,
535546
config.limits.userExchangeLoginTokenPerHourPerIp,
536547
ONE_HOUR_IN_SECONDS,
537548
true,
538549
);
539550
if (!(await rateLimit.registerCall())) {
540-
return res.status(403).send({
551+
res.status(403).send({
541552
error: { message: 'Rate limit exceeded' },
542553
});
554+
return;
543555
}
544556

545557
// This is already checked in checkJwtScope but lets' make it clear
546558
if (req.jwtPayload?.scope !== 'login') {
547559
const errorMessage = `Cannot use this token on this route (scope: ${
548560
req.jwtPayload?.scope || 'session'
549561
}, expected: login)`;
550-
return next(new BadRequest(errorMessage));
562+
next(new BadRequest(errorMessage));
563+
return;
551564
}
552565

553566
// If a guest signs in, it's safe to directly confirm its account
@@ -591,33 +604,37 @@ export const exchangeLoginToken = async (req, res, next) => {
591604
/**
592605
* Exchange a session JWT against a fresh one with extended expiration
593606
*/
594-
export const refreshToken = async (req, res, next) => {
607+
export const refreshToken = async (req: express.Request, res: express.Response, next: express.NextFunction) => {
595608
const rateLimit = new RateLimit(
596609
`user_refresh_token_ip_${req.ip}`,
597610
config.limits.userRefreshTokenPerHourPerIp,
598611
ONE_HOUR_IN_SECONDS,
599612
true,
600613
);
601614
if (!(await rateLimit.registerCall())) {
602-
return res.status(403).send({
615+
res.status(403).send({
603616
error: { message: 'Rate limit exceeded' },
604617
});
618+
return;
605619
}
606620

607621
if (req.personalToken) {
608622
const errorMessage = `Cannot use this token on this route (personal token)`;
609-
return next(new BadRequest(errorMessage));
623+
next(new BadRequest(errorMessage));
624+
return;
610625
}
611626

612627
if (req.jwtPayload?.scope && req.jwtPayload?.scope !== 'session') {
613628
const errorMessage = `Cannot use this token on this route (scope: ${req.jwtPayload?.scope}, expected: session)`;
614-
return next(new BadRequest(errorMessage));
629+
next(new BadRequest(errorMessage));
630+
return;
615631
}
616632

617633
// TODO: not necessary once all oAuth tokens have the scope "oauth"
618634
if (req.jwtPayload?.access_token) {
619635
const errorMessage = `Cannot use this token on this route (oAuth access_token)`;
620-
return next(new BadRequest(errorMessage));
636+
next(new BadRequest(errorMessage));
637+
return;
621638
}
622639

623640
// Context: this is token generation when extending a session
@@ -634,12 +651,17 @@ export const refreshToken = async (req, res, next) => {
634651
/**
635652
* Verify the 2FA code or recovery code the user has entered when logging in and send back another JWT.
636653
*/
637-
export const twoFactorAuthAndUpdateToken = async (req, res, next) => {
654+
export const twoFactorAuthAndUpdateToken = async (
655+
req: express.Request,
656+
res: express.Response,
657+
next: express.NextFunction,
658+
) => {
638659
if (req.jwtPayload?.scope !== 'twofactorauth') {
639660
const errorMessage = `Cannot use this token on this route (scope: ${
640661
req.jwtPayload?.scope || 'session'
641662
}, expected: twofactorauth)`;
642-
return next(new BadRequest(errorMessage));
663+
next(new BadRequest(errorMessage));
664+
return;
643665
}
644666

645667
const { twoFactorAuthenticatorCode, twoFactorAuthenticationRecoveryCode, twoFactorAuthenticationType } = req.body;
@@ -654,7 +676,8 @@ export const twoFactorAuthAndUpdateToken = async (req, res, next) => {
654676
};
655677

656678
if (await rateLimit.hasReachedLimit()) {
657-
return next(new TooManyRequests('Too many attempts. Please try again in an hour'));
679+
next(new TooManyRequests('Too many attempts. Please try again in an hour'));
680+
return;
658681
}
659682

660683
const user = await User.findByPk(userId);
@@ -669,7 +692,8 @@ export const twoFactorAuthAndUpdateToken = async (req, res, next) => {
669692
twoFactorAuthenticationType ?? (twoFactorAuthenticatorCode ? TwoFactorMethod.TOTP : TwoFactorMethod.RECOVERY_CODE);
670693

671694
if (!code) {
672-
return fail(new BadRequest('This endpoint requires you to provide a 2FA code or a recovery code'));
695+
fail(new BadRequest('This endpoint requires you to provide a 2FA code or a recovery code'));
696+
return;
673697
}
674698

675699
try {
@@ -682,7 +706,8 @@ export const twoFactorAuthAndUpdateToken = async (req, res, next) => {
682706
req,
683707
);
684708
} catch {
685-
return fail(new Unauthorized('Two-factor authentication code failed. Please try again'));
709+
fail(new Unauthorized('Two-factor authentication code failed. Please try again'));
710+
return;
686711
}
687712

688713
// Context: this is token generation after signin and valid 2FA authentication

server/types/express.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ declare global {
1818
iat: number;
1919
exp: number;
2020
sub?: string;
21+
access_token?: string;
2122
};
2223
clientApp?: {
2324
id: number;

0 commit comments

Comments
 (0)