Skip to content

Commit b5eb1b9

Browse files
committed
chore: type controllers/users
1 parent ea2c552 commit b5eb1b9

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);
@@ -525,25 +536,27 @@ export async function verifyEmail(req: express.Request, res: express.Response) {
525536
*
526537
* B) If no 2FA, we send back a "session" token
527538
*/
528-
export const exchangeLoginToken = async (req, res, next) => {
539+
export const exchangeLoginToken = async (req: express.Request, res: express.Response, next: express.NextFunction) => {
529540
const rateLimit = new RateLimit(
530541
`user_exchange_login_token_ip_${req.ip}`,
531542
config.limits.userExchangeLoginTokenPerHourPerIp,
532543
ONE_HOUR_IN_SECONDS,
533544
true,
534545
);
535546
if (!(await rateLimit.registerCall())) {
536-
return res.status(403).send({
547+
res.status(403).send({
537548
error: { message: 'Rate limit exceeded' },
538549
});
550+
return;
539551
}
540552

541553
// This is already checked in checkJwtScope but lets' make it clear
542554
if (req.jwtPayload?.scope !== 'login') {
543555
const errorMessage = `Cannot use this token on this route (scope: ${
544556
req.jwtPayload?.scope || 'session'
545557
}, expected: login)`;
546-
return next(new BadRequest(errorMessage));
558+
next(new BadRequest(errorMessage));
559+
return;
547560
}
548561

549562
// If a guest signs in, it's safe to directly confirm its account
@@ -587,33 +600,37 @@ export const exchangeLoginToken = async (req, res, next) => {
587600
/**
588601
* Exchange a session JWT against a fresh one with extended expiration
589602
*/
590-
export const refreshToken = async (req, res, next) => {
603+
export const refreshToken = async (req: express.Request, res: express.Response, next: express.NextFunction) => {
591604
const rateLimit = new RateLimit(
592605
`user_refresh_token_ip_${req.ip}`,
593606
config.limits.userRefreshTokenPerHourPerIp,
594607
ONE_HOUR_IN_SECONDS,
595608
true,
596609
);
597610
if (!(await rateLimit.registerCall())) {
598-
return res.status(403).send({
611+
res.status(403).send({
599612
error: { message: 'Rate limit exceeded' },
600613
});
614+
return;
601615
}
602616

603617
if (req.personalToken) {
604618
const errorMessage = `Cannot use this token on this route (personal token)`;
605-
return next(new BadRequest(errorMessage));
619+
next(new BadRequest(errorMessage));
620+
return;
606621
}
607622

608623
if (req.jwtPayload?.scope && req.jwtPayload?.scope !== 'session') {
609624
const errorMessage = `Cannot use this token on this route (scope: ${req.jwtPayload?.scope}, expected: session)`;
610-
return next(new BadRequest(errorMessage));
625+
next(new BadRequest(errorMessage));
626+
return;
611627
}
612628

613629
// TODO: not necessary once all oAuth tokens have the scope "oauth"
614630
if (req.jwtPayload?.access_token) {
615631
const errorMessage = `Cannot use this token on this route (oAuth access_token)`;
616-
return next(new BadRequest(errorMessage));
632+
next(new BadRequest(errorMessage));
633+
return;
617634
}
618635

619636
// Context: this is token generation when extending a session
@@ -630,12 +647,17 @@ export const refreshToken = async (req, res, next) => {
630647
/**
631648
* Verify the 2FA code or recovery code the user has entered when logging in and send back another JWT.
632649
*/
633-
export const twoFactorAuthAndUpdateToken = async (req, res, next) => {
650+
export const twoFactorAuthAndUpdateToken = async (
651+
req: express.Request,
652+
res: express.Response,
653+
next: express.NextFunction,
654+
) => {
634655
if (req.jwtPayload?.scope !== 'twofactorauth') {
635656
const errorMessage = `Cannot use this token on this route (scope: ${
636657
req.jwtPayload?.scope || 'session'
637658
}, expected: twofactorauth)`;
638-
return next(new BadRequest(errorMessage));
659+
next(new BadRequest(errorMessage));
660+
return;
639661
}
640662

641663
const { twoFactorAuthenticatorCode, twoFactorAuthenticationRecoveryCode, twoFactorAuthenticationType } = req.body;
@@ -650,7 +672,8 @@ export const twoFactorAuthAndUpdateToken = async (req, res, next) => {
650672
};
651673

652674
if (await rateLimit.hasReachedLimit()) {
653-
return next(new TooManyRequests('Too many attempts. Please try again in an hour'));
675+
next(new TooManyRequests('Too many attempts. Please try again in an hour'));
676+
return;
654677
}
655678

656679
const user = await User.findByPk(userId);
@@ -665,7 +688,8 @@ export const twoFactorAuthAndUpdateToken = async (req, res, next) => {
665688
twoFactorAuthenticationType ?? (twoFactorAuthenticatorCode ? TwoFactorMethod.TOTP : TwoFactorMethod.RECOVERY_CODE);
666689

667690
if (!code) {
668-
return fail(new BadRequest('This endpoint requires you to provide a 2FA code or a recovery code'));
691+
fail(new BadRequest('This endpoint requires you to provide a 2FA code or a recovery code'));
692+
return;
669693
}
670694

671695
try {
@@ -678,7 +702,8 @@ export const twoFactorAuthAndUpdateToken = async (req, res, next) => {
678702
req,
679703
);
680704
} catch {
681-
return fail(new Unauthorized('Two-factor authentication code failed. Please try again'));
705+
fail(new Unauthorized('Two-factor authentication code failed. Please try again'));
706+
return;
682707
}
683708

684709
// 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)