UNPKG

authenzify

Version:

server to manage authentication authorization of users and more

409 lines (355 loc) 11.3 kB
import httpStatus from 'http-status' import { HttpError } from 'core-services-sdk' import { cleanUser } from '../util/helpers.js' import { SIGN_UP_SUCCEEDED } from '../api/responses.js' import { SIGN_IN_ERRORS, USER_ACTIONS } from '../errors/error-codes.js' import { withErrorHandlingReply } from '../errors/with-error-reply-handling.js' export const initUsersRoutes = ({ configService, usersManagement, tryToExtractUserAuthenticated, }) => { const replyToken = ({ token, reply }, payload = {}) => { if (!configService.setCookieOnSignIn) { reply.send({ ...payload, token }) return } reply .clearCookie(configService.authorizationCookieKey) .setCookie(configService.authorizationCookieKey, token, { path: '/', signed: false, sameSite: true, }) .send({ ...payload, token }) } const validateAuthentication = async (request, reply) => { try { const userInfo = await tryToExtractUserAuthenticated(request) request.requestContext.set('userInfo', userInfo) } catch (error) { reply .status(httpStatus.UNAUTHORIZED) .send({ message: httpStatus[httpStatus.UNAUTHORIZED] }) return reply } } return async (webServer) => { webServer.addHook('onRequest', async (request, reply) => { const correlationId = request.id request.log = request.log.child({ correlationId, userAgent: request.headers['user-agent'] || 'unknown', }) request.correlationId = correlationId reply.header('x-correlation-id', correlationId) }) await webServer.post('/sign-up', function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { body } = request const userDetails = body this.log.info(`Sign up user email: '${userDetails.email}'`) const user = await usersManagement.signUp(userDetails) this.log.info( `Succeeded to create user: { email: ${user.email}, id: ${user.id} }`, ) reply .status(SIGN_UP_SUCCEEDED.httpStatusCode) .send({ ...SIGN_UP_SUCCEEDED.httpResponse }) }) }) await webServer.post('/sign-in', function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { body } = request const userDetails = body this.log.info(`Sign in user email: '${userDetails.email}'`) const token = await usersManagement.signIn(userDetails) replyToken({ token, reply }) }) }) await webServer.get( '/:id', { preHandler: [validateAuthentication], }, function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const params = request.params const { id } = params this.log.info(`Get user by id: '${id}'`) const user = await usersManagement.getUser({ id }) if (!user) { throw new HttpError(SIGN_IN_ERRORS.USER_NOT_EXIST) } reply.send(cleanUser(user)) }) }, ) await webServer.get( '/me', { preHandler: [validateAuthentication] }, function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { id } = request.requestContext.get('userInfo') this.log.info(`Get user 'me' by id: '${id}'`) const user = await usersManagement.getUser({ id }) if (!user) { throw new HttpError(SIGN_IN_ERRORS.USER_NOT_EXIST) } reply.send(cleanUser(user)) }) }, ) await webServer.get('/verify/:id/activation', function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const params = request.params const { id } = params this.log.info(`Verify user by activation id: '${id}'`) const verified = await usersManagement.verifyActivation(id) reply.send({ verified }) }) }) await webServer.post( '/:id/permissions', { preHandler: [validateAuthentication] }, function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { id } = request.params const userInfo = request.requestContext.get('userInfo') const { body: companyDetails } = request const { isAdminUser, token } = await usersManagement.requestPermissionForUser({ companyDetails, userInfo, id, }) if (isAdminUser) { replyToken({ token, reply }, { isAdminUser, emailSent: false }) return } reply.send({ emailSent: true, isAdminUser }) }) }, ) await webServer.get( '/verify/:id/permissions/:role', { preHandler: [validateAuthentication] }, function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { id, role } = request.params const userInfo = request.requestContext.get('userInfo') const verified = await usersManagement.verifyUserPermissionRequest({ verificationId: id, role, userInfo, }) reply.send({ verified }) }) }, ) await webServer.post( '/change-password', { preHandler: [validateAuthentication] }, function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const userInfo = request.requestContext.get('userInfo') const { body: { password, newPassword }, } = request const passwordChanged = await usersManagement.changePassword({ password, newPassword, userInfo, }) reply.send({ passwordChanged }) }) }, ) await webServer.post('/forgot-password', function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { body: { email }, log, } = request try { const passwordChanged = await usersManagement.forgotPassword({ email, }) reply.send({ passwordChanged }) } catch (error) { log.error(error) reply.send({ passwordChanged: true }) } }) }) await webServer.post( '/verify/:verificationId/apply-forgot-password', function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { params, body: { newPassword }, log, } = request const { verificationId } = params try { const passwordChanged = await usersManagement.applyForgotPassword({ newPassword, verificationId, }) reply.send({ passwordChanged }) } catch (error) { log.error(error) reply.send({ passwordChanged: true }) } }) }, ) await webServer.post('/sign-in-google', function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { body } = request this.log.info(`Sign in user email: '${'userDetails.email'}'`) const token = await usersManagement.signInGoogle(body) replyToken({ token, reply }) }) }) await webServer.post('/sign-in/code', function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { signInVia, identifier, deviceIdentity } = request.body const verification = await usersManagement.getSignInCode({ signInVia, identifier, deviceIdentity, }) reply.send(verification) }) }) await webServer.post('/sign-in/verify', function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { identifier, code, signedInVia, verificationId } = request.body const token = await usersManagement.signInOrUpWithCode({ code, identifier, signedInVia, verificationId, }) this.log.info(`User verified and signed in: ${identifier}`) replyToken({ token, reply }) }) }) await webServer.post( '/assign-phone-or-email/code', { preHandler: [validateAuthentication] }, function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { id } = request.requestContext.get('userInfo') this.log.info(`Get user 'me' by id: '${id}'`) const user = await usersManagement.getUser({ id }) if (!user) { throw new HttpError(SIGN_IN_ERRORS.USER_NOT_EXIST) } const { identifier, assignType } = request.body const verification = await usersManagement.getAssignPhoneOrEmailCode({ user, assignInfo: { identifier, assignType }, }) reply.send(verification) }) }, ) await webServer.post( '/assign-phone-or-email/verify', { preHandler: [validateAuthentication] }, function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const { id } = request.requestContext.get('userInfo') this.log.info(`Get user 'me' by id: '${id}'`) const user = await usersManagement.getUser({ id }) if (!user) { throw new HttpError(SIGN_IN_ERRORS.USER_NOT_EXIST) } const { code, identifier, assignType, verificationId } = request.body const verification = await usersManagement.assignIdentifierViaCode({ user, code, identifier, assignType, verificationId, }) reply.send(verification) }) }, ) await webServer.put( '/:userId/profile', { preHandler: [validateAuthentication] }, function (request, reply) { withErrorHandlingReply({ reply, log: this.log, })(async () => { const user = request.requestContext.get('userInfo') const { id } = user this.log.info(`Update user's profile '${id}'`) const { body: profile, params } = request const { userId } = params if (userId !== id) { throw new HttpError( USER_ACTIONS.ONLY_USER_ACCOUNT_CAN_CHANGE_IS_PROFILE, ) } const userUpdated = await usersManagement.updateUserProfile({ user, profile, }) reply.send(cleanUser(userUpdated)) }) }, ) } }