mirror of https://github.com/wekan/wekan
The Open Source kanban (built with Meteor). Keep variable/table/field names camelCase. For translations, only add Pull Request changes to wekan/i18n/en.i18n.json , other translations are done at https://transifex.com/wekan/wekan only.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
4.5 KiB
155 lines
4.5 KiB
// Login and register REST endpoints.
|
|
// Replaces communitypackages:rest-accounts-password.
|
|
// Logic copied from the package source to maintain identical API behavior.
|
|
|
|
const { Meteor } = require('meteor/meteor');
|
|
const { Accounts } = require('meteor/accounts-base');
|
|
const { WebApp } = require('meteor/webapp');
|
|
const { check, Match } = require('meteor/check');
|
|
const { sendJsonResult } = require('/server/apiMiddleware');
|
|
|
|
const NonEmptyString = Match.Where(function (x) {
|
|
check(x, String);
|
|
return x.length > 0;
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// POST /users/login
|
|
// ---------------------------------------------------------------------------
|
|
WebApp.handlers.options('/users/login', function (req, res) {
|
|
sendJsonResult(res);
|
|
});
|
|
|
|
WebApp.handlers.post('/users/login', async function (req, res) {
|
|
try {
|
|
const options = req.body;
|
|
|
|
let user;
|
|
if (options.email) {
|
|
check(options, {
|
|
email: String,
|
|
password: String,
|
|
code: Match.Optional(NonEmptyString),
|
|
});
|
|
user = await Meteor.users.findOneAsync({ 'emails.address': options.email });
|
|
} else {
|
|
check(options, {
|
|
username: String,
|
|
password: String,
|
|
code: Match.Optional(NonEmptyString),
|
|
});
|
|
user = await Meteor.users.findOneAsync({ username: options.username });
|
|
}
|
|
|
|
if (!user) {
|
|
throw new Meteor.Error(
|
|
'not-found',
|
|
'User with that username or email address not found.',
|
|
);
|
|
}
|
|
|
|
const result = await Accounts._checkPasswordAsync(user, options.password);
|
|
check(result, {
|
|
userId: String,
|
|
error: Match.Optional(Meteor.Error),
|
|
});
|
|
|
|
if (result.error) {
|
|
throw result.error;
|
|
}
|
|
|
|
// 2FA support
|
|
if (Accounts._check2faEnabled && Accounts._check2faEnabled(user)) {
|
|
if (!options.code) {
|
|
Accounts._handleError('2FA code must be informed', true, 'no-2fa-code');
|
|
}
|
|
if (
|
|
!Accounts._isTokenValid(
|
|
user.services.twoFactorAuthentication.secret,
|
|
options.code,
|
|
)
|
|
) {
|
|
Accounts._handleError('Invalid 2FA code', true, 'invalid-2fa-code');
|
|
}
|
|
}
|
|
|
|
const stampedLoginToken = Accounts._generateStampedLoginToken();
|
|
check(stampedLoginToken, { token: String, when: Date });
|
|
|
|
await Accounts._insertLoginToken(result.userId, stampedLoginToken);
|
|
|
|
const tokenExpiration = Accounts._tokenExpiration(stampedLoginToken.when);
|
|
check(tokenExpiration, Date);
|
|
|
|
sendJsonResult(res, {
|
|
data: {
|
|
id: result.userId,
|
|
token: stampedLoginToken.token,
|
|
tokenExpires: tokenExpiration,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
res.statusCode = error.statusCode || 401;
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.end(
|
|
JSON.stringify({
|
|
error: error.error || error.message || 'Login failed',
|
|
reason: error.reason || error.message,
|
|
}),
|
|
);
|
|
}
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// POST /users/register
|
|
// ---------------------------------------------------------------------------
|
|
WebApp.handlers.options('/users/register', function (req, res) {
|
|
sendJsonResult(res);
|
|
});
|
|
|
|
WebApp.handlers.post('/users/register', async function (req, res) {
|
|
try {
|
|
if (Accounts._options.forbidClientAccountCreation) {
|
|
sendJsonResult(res, { code: 403 });
|
|
return;
|
|
}
|
|
|
|
const options = req.body;
|
|
check(options, {
|
|
username: Match.Optional(String),
|
|
email: Match.Optional(String),
|
|
password: String,
|
|
});
|
|
|
|
const userOptions = { password: options.password };
|
|
if (options.username) userOptions.username = options.username;
|
|
if (options.email) userOptions.email = options.email;
|
|
|
|
const userId = await Accounts.createUserAsync(userOptions);
|
|
|
|
const stampedLoginToken = Accounts._generateStampedLoginToken();
|
|
check(stampedLoginToken, { token: String, when: Date });
|
|
|
|
await Accounts._insertLoginToken(userId, stampedLoginToken);
|
|
|
|
const tokenExpiration = Accounts._tokenExpiration(stampedLoginToken.when);
|
|
check(tokenExpiration, Date);
|
|
|
|
sendJsonResult(res, {
|
|
data: {
|
|
token: stampedLoginToken.token,
|
|
tokenExpires: tokenExpiration,
|
|
id: userId,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
res.statusCode = error.statusCode || 400;
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.end(
|
|
JSON.stringify({
|
|
error: error.error || error.message || 'Registration failed',
|
|
reason: error.reason || error.message,
|
|
}),
|
|
);
|
|
}
|
|
});
|
|
|