diff --git a/controllers/auth.ts b/controllers/auth.ts index 91b7c9e..0aea4fd 100644 --- a/controllers/auth.ts +++ b/controllers/auth.ts @@ -4,9 +4,44 @@ import { NextFunction, Request, Response } from 'express'; import logger from '../utils/logger'; import * as authService from '../services/authService'; import { apiResponse } from '../@types/apiReponse'; +import { Users } from '@prisma/client'; + +const COOKIE_CALENDAR_ID = 'calendar_id'; +const COOKIE_USERNAME = 'username'; + +/** + * + * Sets cookies for + * @param res {Object} + * @param user {Users & { calendarId: number }} + */ +const setCookies = ( + res: Response, + user: Users & { calendarId: number } +): void => { + const COOKIE_OPTIONS: any = { + domain: config.get('userAccessToken.cookieDomain'), + expires: new Date(Date.now() + config.get('userAccessToken.ttl') * 1000), + httpOnly: true, + secure: true, + sameSite: 'lax', + }; + + const token = authService.generateAuthToken({ userId: user.id }); + + res.cookie(config.get('userAccessToken.cookieName'), token, COOKIE_OPTIONS); + res.cookie(COOKIE_CALENDAR_ID, user.calendarId, { + ...COOKIE_OPTIONS, + httpOnly: false, + }); + res.cookie(COOKIE_USERNAME, user.username ?? '', { + ...COOKIE_OPTIONS, + httpOnly: false, + }); +}; /** - * Makes authentication call to google statergy + * Makes authentication call to google strategy * * @param req {Object} - Express request object * @param res {Object} - Express response object @@ -54,20 +89,10 @@ const googleAuthCallback = ( return res.boom(Boom.unauthorized('User cannot be authenticated')); } - const userData = await authService.loginOrSignupWithGoogle(user._json); - - const token = authService.generateAuthToken({ userId: userData?.id }); + const data = await authService.loginOrSignupWithGoogle(user._json); // respond with a cookie - res.cookie(config.get('userAccessToken.cookieName'), token, { - domain: config.get('userAccessToken.cookieDomain'), - expires: new Date( - Date.now() + config.get('userAccessToken.ttl') * 1000 - ), - httpOnly: true, - secure: true, - sameSite: 'lax', - }); + setCookies(res, data); return res.redirect(rCalUiUrl.href); })(req, res, next); @@ -92,19 +117,10 @@ const microsoftAuthCallback = ( logger.error(err); return res.boom(Boom.unauthorized('User cannot be authenticated')); } - const userData = await authService.loginOrSignupWithMicrosoft(user._json); - const token = authService.generateAuthToken({ userId: userData?.id }); + const data = await authService.loginOrSignupWithMicrosoft(user._json); // respond with a cookie - res.cookie(config.get('userAccessToken.cookieName'), token, { - domain: config.get('userAccessToken.cookieDomain'), - expires: new Date( - Date.now() + config.get('userAccessToken.ttl') * 1000 - ), - httpOnly: true, - secure: true, - sameSite: 'lax', - }); + setCookies(res, data); return res.redirect(rCalUiUrl.href); })(req, res, next); diff --git a/services/authService.ts b/services/authService.ts index c758559..1c6a74d 100644 --- a/services/authService.ts +++ b/services/authService.ts @@ -2,7 +2,7 @@ import jwt from 'jsonwebtoken'; import { GoogleOAuthJson, MicrosoftOAuthJson } from '../@types/providers'; import prisma from '../prisma/prisma'; import { jwtPayload } from '../@types/services'; -import { Users } from '@prisma/client'; +import { Calendar, Users } from '@prisma/client'; /** * Generates the JWT @@ -39,47 +39,107 @@ const decodeAuthToken = (token: string): any => { return jwt.decode(token); }; +/** + * + * Returns user details with calendarId + * @param email string + * @returns Promise => { + const user = await prisma.users.findUniqueOrThrow({ + where: { + email, + }, + include: { + Calendar: { + where: { + isDeleted: false, + isPrimary: true, + }, + select: { + id: true, + }, + }, + }, + }); + return { ...user, calendarId: user.Calendar[0].id }; +}; + +/** + * + * Creates new user in DB + * @param user GoogleOAuthJson | MicrosoftOAuthJson + * @returns Promise<{ user: Users; calendar: Calendar } + */ +const createNewUser = async ( + user: GoogleOAuthJson | MicrosoftOAuthJson +): Promise<{ user: Users; calendar: Calendar }> => { + let createdUser: Users | undefined; + + if ('email' in user) { + logger.info( + `User with email ${user.email} does not exist. Creating new account from Google` + ); + createdUser = await prisma.users.create({ + data: { + email: user.email, + firstname: user.given_name, + lastname: user.family_name, + emailVerified: true, + googleProfileId: user?.sub, + }, + }); + } else if ('mail' in user) { + logger.info( + `User with email ${user.mail} does not exist. Creating new account from Microsoft` + ); + createdUser = await prisma.users.create({ + data: { + email: user.mail ?? user.userPrincipalName, + firstname: user.givenName, + lastname: user.surname, + emailVerified: true, + microsoftProfileId: user.id, + }, + }); + } + + if (!createdUser) { + throw new Error('Failed to create user'); + } + + logger.info(`Creating users default calender.`); + + const createdCalendar = await prisma.calendar.create({ + data: { + name: createdUser.email, + ownerId: createdUser.id, + isPrimary: true, + }, + }); + + return { user: createdUser, calendar: createdCalendar }; +}; + /** * Login or signUp with Google * @param googleProfile{Object} : Google profile response from Google OAuth2.0 */ const loginOrSignupWithGoogle = async ( googleProfile: GoogleOAuthJson -): Promise => { +): Promise => { try { - const user = await prisma.users.findUnique({ - where: { - email: googleProfile?.email, - }, - }); + const user = await getUserData(googleProfile.email); if (user) { return user; } else { - logger.info( - `User with email ${googleProfile?.email} does not exist. Creating new account.` - ); - const createdUser = await prisma.users.create({ - data: { - email: googleProfile?.email, - firstname: googleProfile?.given_name, - lastname: googleProfile?.family_name, - emailVerified: true, - googleProfileId: googleProfile?.sub, - }, - }); - - logger.info(`Creating users default calender.`); + const { user: createdUser, calendar: createdCalendar } = + await createNewUser(googleProfile); - await prisma.calendar.create({ - data: { - name: createdUser.email, - ownerId: createdUser.id, - isPrimary: true, - }, - }); - - return createdUser; + return { ...createdUser, calendarId: createdCalendar.id }; } } catch (err: any) { logger.error('loginOrSignupWithGoogle:: Error in authenticating user', { @@ -96,41 +156,19 @@ const loginOrSignupWithGoogle = async ( */ const loginOrSignupWithMicrosoft = async ( microsoftProfile: MicrosoftOAuthJson -): Promise => { +): Promise => { try { - const user = await prisma.users.findUnique({ - where: { - email: microsoftProfile.mail ?? microsoftProfile?.userPrincipalName, - }, - }); + const user = await getUserData( + microsoftProfile.mail ?? microsoftProfile?.userPrincipalName + ); if (user) { return user; } else { - logger.info( - `User with email ${microsoftProfile?.userPrincipalName} does not exist. Creating new account.` - ); - const createdUser = await prisma.users.create({ - data: { - email: microsoftProfile.mail ?? microsoftProfile?.userPrincipalName, - firstname: microsoftProfile?.givenName, - lastname: microsoftProfile?.surname, - emailVerified: true, - microsoftProfileId: microsoftProfile?.id, - }, - }); - - logger.info(`Creating users default calender.`); - - await prisma.calendar.create({ - data: { - name: createdUser.email, - ownerId: createdUser.id, - isPrimary: true, - }, - }); + const { user: createdUser, calendar: createdCalendar } = + await createNewUser(microsoftProfile); - return createdUser; + return { ...createdUser, calendarId: createdCalendar.id }; } } catch (err: any) { logger.error('loginOrSignupWithGoogle:: Error in authenticating user', {