UNPKG

acebase-server

Version:

AceBase realtime database server (webserver endpoint to allow remote connections)

128 lines 5.97 kB
import { getPublicAccountDetails } from '../schema/user.js'; import { emailExistsError, emailOrUsernameExistsError, invalidDisplayNameError, invalidEmailError, invalidPasswordError, invalidPictureError, invalidSettingsError, invalidUsernameError, isValidDisplayName, isValidEmail, isValidNewEmailAddress, isValidNewUsername, isValidPassword, isValidPicture, isValidSettings, isValidUsername, usernameExistsError } from '../shared/validate.js'; import { createPasswordHash } from '../shared/password.js'; import { ID } from 'acebase-core'; import { createPublicAccessToken, createSignedPublicToken } from '../shared/tokens.js'; import { sendUnauthorizedError, sendUnexpectedError } from '../shared/error.js'; import { isAdmin } from '../shared/admin.js'; export class SignupError extends Error { constructor(code, message) { super(message); this.code = code; } } export const addRoute = (env) => { env.router.post(`/auth/${env.db.name}/signup`, async (req, res) => { const LOG_ACTION = 'auth.signup'; const LOG_DETAILS = { ip: req.ip, uid: req.user?.uid ?? null }; if (!env.config.auth.allowUserSignup && !isAdmin(req.user)) { env.log.error(LOG_ACTION, 'user_signup_disabled', LOG_DETAILS); return sendUnauthorizedError(res, 'admin_only', 'Only admins are allowed to create users'); } // Create user if it doesn't exist yet. // TODO: Rate-limit nr of signups per IP to prevent abuse const details = req.body; if (typeof details.display_name === 'string' && typeof details.displayName !== 'string') { // Allow display_name to be sent also (which is used in update endpoint) details.displayName = details.display_name; } // Check if sent details are ok let err; if (!details.username && !details.email) { err = { code: 'missing_details', message: 'No username or email provided' }; } else if (details.email && !isValidEmail(details.email)) { err = invalidEmailError; } else if (details.email && !await isValidNewEmailAddress(env.authRef, details.email)) { err = emailExistsError; } else if (details.username && !isValidUsername(details.username)) { err = invalidUsernameError; } else if (details.username && !await isValidNewUsername(env.authRef, details.username)) { err = usernameExistsError; } else if (!isValidDisplayName(details.displayName)) { err = invalidDisplayNameError; } else if (!isValidPassword(details.password)) { err = invalidPasswordError; } else if (!isValidSettings(details.settings)) { err = invalidSettingsError; } else if (details.picture && !isValidPicture(details.picture)) { err = invalidPictureError; } if (err === emailExistsError || err === usernameExistsError) { env.log.error(LOG_ACTION, 'conflict', { ...LOG_DETAILS, username: details.username, email: details.email }); res.statusCode = 409; // conflict return res.send(emailOrUsernameExistsError); } else if (err) { // Log failure env.log.error(LOG_ACTION, err.code ?? 'unexpected', LOG_DETAILS); res.statusCode = 422; // Unprocessable Entity return res.send(err); } try { // Ok, create user const pwd = createPasswordHash(details.password); const user = { uid: null, username: details.username ?? null, email: details.email ?? null, email_verified: false, display_name: details.displayName, password: pwd.hash, password_salt: pwd.salt, created: new Date(), created_ip: req.ip, access_token: ID.generate(), access_token_created: new Date(), last_signin: new Date(), last_signin_ip: req.ip, picture: details.picture ?? null, settings: details.settings ?? {}, }; const userRef = await env.authRef.push(user); user.uid = userRef.key; LOG_DETAILS.uid = user.uid; // Log success env.log.event(LOG_ACTION, LOG_DETAILS); // Cache the user env.authCache.set(user.uid, user); // Request welcome e-mail to be sent const request = { type: 'user_signup', user: { uid: user.uid, username: user.username, email: user.email, displayName: user.display_name, settings: user.settings, }, date: user.created, ip: user.created_ip, provider: 'acebase', activationCode: createSignedPublicToken({ uid: user.uid }, env.tokenSalt), emailVerified: false, }; env.config.email?.send(request).catch(err => { env.log.error(LOG_ACTION + '.email', 'unexpected', { ...LOG_DETAILS, request }, err); }); // Return the positive news res.send({ access_token: isAdmin(req.user) ? '' : createPublicAccessToken(user.uid, req.ip, user.access_token, env.tokenSalt), user: getPublicAccountDetails(user), }); } catch (err) { env.log.error(LOG_ACTION, 'unexpected', { ...LOG_DETAILS, message: err instanceof Error ? err.message : err.toString(), username: details.username, email: details.email }); sendUnexpectedError(res, err); } }); }; export default addRoute; //# sourceMappingURL=auth-signup.js.map