Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
} as UserDocument;
// optional data
if (profile.photos && profile.photos.length > 0) {
newUser.thumb = profile.photos[0].value;
}
newUser.providers[provider].profile = profile._json; // save original data to separate field
newUser.emails = uniq(emails);
logger.info(ctx.state, '[AuthenticationApi.createOAuthUser] Creating new user.');
newUser = await UserUtil.createUser(ctx, newUser, false);
/* istanbul ignore if */
if (process.env.SQREEN_ENABLED) {
require('sqreen').signup_track({ email: newUser.email });
}
logger.info(ctx.state, '[AuthenticationApi.createOAuthUser] New user <%s> created.', newUser.email);
await LogUserUtil.success(ctx, newUser, 'registration', {
provider,
email: newUser.email,
});
// new oauth user, clear provider cache since there might gonna be new user matches.
await apiCache.invalidateProviderCache(ctx.state, provider);
this.noAwait(async () => {
await mailer.welcomeOAuth(ctx.state, newUser);
});
return newUser;
}
// TODO make sure newUser.email is sane (comes from user directly)
const existingUser = await state.models.User.findOne({
$or: [
{ emails: sanitize(postedUser.email) },
{ validated_emails: sanitize(postedUser.email) },
],
}).exec();
if (existingUser) {
throw new ApiError('User with email <%s> already exists.', postedUser.email).warn().status(409);
}
const confirmUserEmail = config.vpdb.email.confirmUserEmail && !skipEmailConfirmation;
const createdUser = await UserUtil.createUser(ctx, postedUser, confirmUserEmail);
if (process.env.SQREEN_ENABLED) {
require('sqreen').signup_track({ email: createdUser.email });
}
await LogUserUtil.success(ctx, createdUser, 'registration', {
provider: 'local',
email: createdUser.email,
username: createdUser.username,
});
this.noAwait(async () => {
// user validated and created. time to send the activation email.
if (config.vpdb.email.confirmUserEmail) {
await mailer.registrationConfirmation(ctx.state, createdUser);
}
});
// return result
delete ctx.state.appToken;
delete ctx.state.tokenType;
delete ctx.state.tokenScopes;
// get token sent by user
const token = retrieveToken(ctx);
// try to authenticate with token
const user = /[0-9a-f]{32,}/i.test(token.value) ?
await authenticateWithAppToken(ctx, token) : // app token?
await authenticateWithJwt(ctx, token); // otherwise, assume it's a JWT.
// log to sqreen
/* istanbul ignore if */
if (process.env.SQREEN_ENABLED) {
require('sqreen').identify(ctx.req, { email: user.email });
}
// update state
ctx.state.user = user;
ctx.response.set('X-User-Id', user.id);
} catch (err) {
if (err.isApiError) {
// update state with error if it's API-related
ctx.state.authError = err;
} else {
// otherwise, re-throw (this is unexpected)
/* istanbul ignore next */
throw err;
private async assertUserIsActive(ctx: Context, user: UserDocument): Promise {
if (!user.is_active) {
/* istanbul ignore if */
if (process.env.SQREEN_ENABLED) {
require('sqreen').auth_track(false, { email: user.email });
}
if (user.email_status && user.email_status.code === 'pending_registration') {
await LogUserUtil.failure(ctx, user, 'authenticate', { provider: 'local' }, null, 'Inactive account due to pending email confirmation.');
throw new ApiError('User <%s> tried to login with unconfirmed email address.', user.email)
.display('Your account is inactive until you confirm your email address <%s>. If you did not get an email from <%s>, please contact an administrator.', user.email, config.vpdb.email.sender.email)
.code('email_unconfirmed')
.warn()
.status(403);
} else {
await LogUserUtil.failure(ctx, user, 'authenticate', { provider: 'local' }, null, 'Inactive account.');
throw new ApiError('User <%s> is disabled, refusing access', user.email)
.display('Inactive account. Please contact an administrator')
.code('account_disabled')
.warn()
.status(403);
}
throw new ApiError('Cannot use token of type "%s" for authentication (must be of type "personal").', token.type).status(401);
}
// fail if not login token
if (!scope.isIdentical(token.scopes, ['login'])) {
/* istanbul ignore if */
if (process.env.SQREEN_ENABLED) {
require('sqreen').auth_track(false, { email: (token._created_by as UserDocument).email });
}
throw new ApiError('Token to exchange for JWT must exclusively be "login" ([ "' + token.scopes.join('", "') + '" ] given).').status(401);
}
// fail if token expired
if (token.expires_at.getTime() < Date.now()) {
/* istanbul ignore if */
if (process.env.SQREEN_ENABLED) {
require('sqreen').auth_track(false, { email: (token._created_by as UserDocument).email });
}
throw new ApiError('Token has expired.').status(401);
}
// fail if token inactive
if (!token.is_active) {
/* istanbul ignore if */
if (process.env.SQREEN_ENABLED) {
require('sqreen').auth_track(false, { email: (token._created_by as UserDocument).email });
}
throw new ApiError('Token is inactive.').status(401);
}
await token.update({ last_used_at: new Date() });
return token._created_by as UserDocument;
}
protected async authenticateUser(ctx: Context, authenticatedUser: UserDocument, how: 'password' | 'token' | 'oauth') {
await this.assertUserIsActive(ctx, authenticatedUser);
// generate token and return.
const now = new Date();
const expires = new Date(now.getTime() + config.vpdb.apiTokenLifetime);
const token = AuthenticationUtil.generateApiToken(authenticatedUser, now, how !== 'password' && how !== 'oauth');
await LogUserUtil.success(ctx, authenticatedUser, 'authenticate', { provider: 'local', how });
logger.info(ctx.state, '[AuthenticationApi.authenticate] User <%s> successfully authenticated using %s.', authenticatedUser.email, how);
/* istanbul ignore if */
if (process.env.SQREEN_ENABLED) {
require('sqreen').auth_track(true, { email: authenticatedUser.email });
}
const span = this.apmStartSpan(`UserUtil.getACLs()`);
const acls = await UserUtil.getACLs(authenticatedUser);
this.apmEndSpan(span);
const response = {
token,
expires,
user: assign(state.serializers.User.detailed(ctx, authenticatedUser), acls),
};
/* istanbul ignore if */
if (process.env.SQREEN_ENABLED) {
require('sqreen').auth_track(true, { email: authenticatedUser.email });
}
this.success(ctx, response, 200);
}
private async authenticateLocally(ctx: Context): Promise {
// try to authenticate with user/pass
if (!ctx.request.body.username || !ctx.request.body.password) {
return null;
}
const localUser = await state.models.User.findOne({ username: sanitize(ctx.request.body.username) }).exec();
if (localUser && localUser.authenticate(ctx.request.body.password)) {
// all good, return.
return localUser;
}
// log if there was a user
if (localUser) {
await LogUserUtil.failure(ctx, localUser, 'authenticate', { provider: 'local' }, null, 'Invalid password.');
/* istanbul ignore if */
if (process.env.SQREEN_ENABLED) {
require('sqreen').auth_track(false, { username: ctx.request.body.username });
}
} // don't bother logging unknown user names
throw new ApiError('Authentication denied for user "%s" (%s)', ctx.request.body.username, localUser ? 'password' : 'username')
.display('Wrong username or password')
.code('wrong_username_or_password')
.warn()
.status(401);
}
return Token.findOne({ token: req.body.token }).populate('_created_by').exec().then(token => {
// fail if not found
if (!token) {
antiBruteForce = true;
throw error('Invalid token.').status(401);
}
// fail if invalid type
if (token.type !== 'personal') {
if (config.vpdb.services.sqreen.enabled) {
require('sqreen').auth_track(false, { email: token._created_by.email });
}
throw error('Cannot use token of type "%s" for authentication (must be of type "personal").', token.type).status(401);
}
// fail if not login token
if (!scope.isIdentical(token.scopes, [ 'login' ])) {
if (config.vpdb.services.sqreen.enabled) {
require('sqreen').auth_track(false, { email: token._created_by.email });
}
throw error('Token to exchange for JWT must exclusively be "login" ([ "' + token.scopes.join('", "') + '" ] given).').status(401);
}
// fail if token expired
if (token.expires_at.getTime() < new Date().getTime()) {
if (config.vpdb.services.sqreen.enabled) {
require('sqreen').auth_track(false, { email: token._created_by.email });
private async updateOAuthUser(ctx: Context, user: UserDocument, provider: string, profile: OAuthProfile, emails: string[]): Promise {
/* istanbul ignore if */
if (process.env.SQREEN_ENABLED) {
require('sqreen').auth_track(true, { email: user.email });
}
if (!user.providers || !user.providers[provider]) {
user.providers = user.providers || {};
user.providers[provider] = {
id: String(profile.id),
name: UserUtil.getNameFromProfile(profile),
emails,
created_at: new Date(),
modified_at: new Date(),
profile: profile._json,
};
await LogUserUtil.success(ctx, user, 'authenticate', {
provider,
profile: profile._json,
});
logger.info(ctx.state, '[AuthenticationApi.updateOAuthUser] Adding profile from %s to user.', provider, emails[0]);
}).then(user => {
if (config.vpdb.services.sqreen.enabled) {
require('sqreen').signup_track({ email: user.email });
}
LogUser.success(req, user, 'registration', { provider: 'local', email: newUser.email, username: newUser.username });
// user validated and created. time to send the activation email.
if (config.vpdb.email.confirmUserEmail) {
mailer.registrationConfirmation(user);
}
// return result now and send email afterwards
if (testMode && req.body.returnEmailToken) {
api.success(res, _.extend(UserSerializer.detailed(user, req), { email_token: user.email_status.toObject().token }), 201);
} else {
api.success(res, UserSerializer.detailed(user, req), 201);
}