diff --git a/packages/hoppscotch-backend/Dockerfile b/packages/hoppscotch-backend/Dockerfile index ab0c88f42..ed9736551 100644 --- a/packages/hoppscotch-backend/Dockerfile +++ b/packages/hoppscotch-backend/Dockerfile @@ -6,10 +6,10 @@ WORKDIR /usr/src/app RUN npm i -g pnpm # Prisma bits -COPY prisma ./ +COPY prisma ./prisma/ RUN pnpx prisma generate -# # NPM package install +# # PNPM package install COPY . . RUN pnpm i diff --git a/packages/hoppscotch-backend/src/auth/auth.service.ts b/packages/hoppscotch-backend/src/auth/auth.service.ts index f9c31d913..288a541d3 100644 --- a/packages/hoppscotch-backend/src/auth/auth.service.ts +++ b/packages/hoppscotch-backend/src/auth/auth.service.ts @@ -40,7 +40,13 @@ export class AuthService { private readonly mailerService: MailerService, ) {} - // generate Id and token for email magiclink + // + /** + * Generate Id and token for email Magic-Link auth + * + * @param {AuthUser} user User Object + * @returns {Promise} Created PasswordlessVerification token + */ private async generatePasswordlessTokens(user: AuthUser) { const salt = await bcrypt.genSalt(10); const expiresOn = DateTime.now().plus({ hours: 3 }).toISO().toString(); @@ -56,6 +62,12 @@ export class AuthService { return idToken; } + /** + * Find and check passwordlessVerification exists or not + * + * @param {verifyMagicDto} data Object containing deviceIdentifier and token + * @returns {Promise>} Option of PasswordlessVerification token + */ private async validatePasswordlessTokens(data: verifyMagicDto) { try { const tokens = @@ -73,6 +85,13 @@ export class AuthService { } } + /** + * Update User with new generated hashed refresh token + * + * @param {string} tokenHash Hash of newly generated refresh token + * @param {string} userUid User uid + * @returns {Promise | E.Left<"user/not_found">>} Either of User with updated refreshToken + */ private async UpdateUserRefreshToken(tokenHash: string, userUid: string) { try { const user = await this.prismaService.user.update({ @@ -90,6 +109,12 @@ export class AuthService { } } + /** + * Generate new refresh token for user + * + * @param {string} userUid User Id + * @returns {Promise | E.Right>} Generated refreshToken + */ private async generateRefreshToken(userUid: string) { const refreshTokenPayload: RefreshTokenPayload = { iss: process.env.APP_DOMAIN, @@ -116,6 +141,12 @@ export class AuthService { return E.right(refreshToken); } + /** + * Generate access and refresh token pair + * + * @param {string} userUid User ID + * @returns {Promise | E.Right>} Either of generated AuthTokens + */ async generateAuthTokens(userUid: string) { const accessTokenPayload: AccessTokenPayload = { iss: process.env.APP_DOMAIN, @@ -138,6 +169,12 @@ export class AuthService { }); } + /** + * Deleted used PasswordlessVerification tokens + * + * @param {PasswordlessVerification} passwordlessTokens + * @returns {Promise | E.Left<"auth/passwordless_token_data_not_found">>} Either of deleted PasswordlessVerification token + */ private async deletePasswordlessVerificationToken( passwordlessTokens: PasswordlessVerification, ) { @@ -157,6 +194,13 @@ export class AuthService { } } + /** + * Verify if Provider account exists for User + * + * @param {User} user User Object + * @param profile Provider Account type (Magic,Google,Github,Microsoft) + * @returns {Promise>} Either of existing user provider Account + */ async checkIfProviderAccountExists(user: User, profile) { const provider = await this.prismaService.account.findUnique({ where: { @@ -172,9 +216,13 @@ export class AuthService { return O.some(provider); } - async signIn( - email: string, - ): Promise | E.Right> { + /** + * Send Magic-Link to provider User email + * + * @param {string} email User's email + * @returns {Promise | E.Right>} Either containing DeviceIdentifierToken + */ + async signIn(email: string) { if (!validateEmail(email)) return E.left({ message: INVALID_EMAIL, @@ -205,6 +253,12 @@ export class AuthService { }); } + /** + * Verify and authenticate user from received data for Magic-Link + * + * @param {verifyMagicDto} data + * @returns {Promise | E.Left>} Either of generated AuthTokens + */ async verifyPasswordlessTokens( data: verifyMagicDto, ): Promise | E.Left> { @@ -273,10 +327,14 @@ export class AuthService { return E.right(tokens.right); } - async refreshAuthTokens( - refresh_token: string, - user: AuthUser, - ): Promise | E.Right> { + /** + * Refresh refresh and auth tokens + * + * @param {string} refresh_token Hashed refresh token received from client + * @param {AuthUser} user User Object + * @returns {Promise | E.Right>} Either of generated AuthTokens + */ + async refreshAuthTokens(refresh_token: string, user: AuthUser) { if (!user) return E.left({ message: USER_NOT_FOUND, diff --git a/packages/hoppscotch-backend/src/auth/strategies/github.strategy.ts b/packages/hoppscotch-backend/src/auth/strategies/github.strategy.ts index 08cc3d640..e01bc59a0 100644 --- a/packages/hoppscotch-backend/src/auth/strategies/github.strategy.ts +++ b/packages/hoppscotch-backend/src/auth/strategies/github.strategy.ts @@ -34,6 +34,9 @@ export class GithubStrategy extends PassportStrategy(Strategy) { return createdUser; } + /** + * * displayName and photoURL maybe null if user logged-in via magic-link before SSO + */ if (!user.value.displayName || !user.value.photoURL) { const updatedUser = await this.usersService.updateUserDetails( user.value, diff --git a/packages/hoppscotch-backend/src/auth/strategies/google.strategy.ts b/packages/hoppscotch-backend/src/auth/strategies/google.strategy.ts index 93e2d4ec4..4420dd919 100644 --- a/packages/hoppscotch-backend/src/auth/strategies/google.strategy.ts +++ b/packages/hoppscotch-backend/src/auth/strategies/google.strategy.ts @@ -34,6 +34,9 @@ export class GoogleStrategy extends PassportStrategy(Strategy) { return createdUser; } + /** + * * displayName and photoURL maybe null if user logged-in via magic-link before SSO + */ if (!user.value.displayName || !user.value.photoURL) { const updatedUser = await this.usersService.updateUserDetails( user.value, diff --git a/packages/hoppscotch-backend/src/auth/strategies/microsoft.strategy.ts b/packages/hoppscotch-backend/src/auth/strategies/microsoft.strategy.ts index 085643ec6..cdffce8e6 100644 --- a/packages/hoppscotch-backend/src/auth/strategies/microsoft.strategy.ts +++ b/packages/hoppscotch-backend/src/auth/strategies/microsoft.strategy.ts @@ -34,6 +34,9 @@ export class MicrosoftStrategy extends PassportStrategy(Strategy) { return createdUser; } + /** + * * displayName and photoURL maybe null if user logged-in via magic-link before SSO + */ if (!user.value.displayName || !user.value.photoURL) { const updatedUser = await this.usersService.updateUserDetails( user.value, diff --git a/packages/hoppscotch-backend/src/mailer/mailer.service.ts b/packages/hoppscotch-backend/src/mailer/mailer.service.ts index 50f5d5aea..444515014 100644 --- a/packages/hoppscotch-backend/src/mailer/mailer.service.ts +++ b/packages/hoppscotch-backend/src/mailer/mailer.service.ts @@ -38,6 +38,12 @@ export class MailerService implements OnModuleInit { ); } + /** + * + * @param {string} to Receiver's email id + * @param {UserMagicLinkMailDescription} mailDesc Details of email to be sent for Magic-Link auth + * @returns {Promise} Response if email was send successfully or not + */ async sendAuthEmail(to: string, mailDesc: UserMagicLinkMailDescription) { try { const res = await this.client.sendEmailWithTemplate({ diff --git a/packages/hoppscotch-backend/src/user/user.service.ts b/packages/hoppscotch-backend/src/user/user.service.ts index 8c57ce85c..ec011423e 100644 --- a/packages/hoppscotch-backend/src/user/user.service.ts +++ b/packages/hoppscotch-backend/src/user/user.service.ts @@ -9,6 +9,12 @@ import { USER_NOT_FOUND } from 'src/errors'; export class UserService { constructor(private prisma: PrismaService) {} + /** + * Find User with given email id + * + * @param {string} email User's email + * @returns {Promise>} Option of found User + */ async findUserByEmail(email: string) { try { const user = await this.prisma.user.findUniqueOrThrow({ @@ -22,6 +28,12 @@ export class UserService { } } + /** + * Find User with given ID + * + * @param {string} userUid User ID + * @returns {Promise>} Option of found User + */ async findUserById(userUid: string) { try { const user = await this.prisma.user.findUniqueOrThrow({ @@ -35,6 +47,12 @@ export class UserService { } } + /** + * Create a new User when logged in via a Magic Link + * + * @param {string} email User's Email + * @returns {Promise} Created User + */ async createUserMagic(email: string) { const createdUser = await this.prisma.user.create({ data: { @@ -51,6 +69,14 @@ export class UserService { return createdUser; } + /** + * Create a new User when logged in via a SSO provider + * + * @param {string} accessToken User's access token generated by providers + * @param {string} refreshToken User's refresh token generated by providers + * @param {any} profile Data received from SSO provider on the users account + * @returns {Promise} Created User + */ async createUserSSO(accessToken: string, refreshToken: string, profile) { const createdUser = await this.prisma.user.create({ data: { @@ -71,6 +97,15 @@ export class UserService { return createdUser; } + /** + * Create a new Account for a given User + * + * @param {AuthUser} user User object + * @param {string} accessToken User's access token generated by providers + * @param {string} refreshToken User's refresh token generated by providers + * @param {any} profile Data received from SSO provider on the users account + * @returns {Promise} Created Account + */ async createProviderAccount( user: AuthUser, accessToken: string, @@ -94,6 +129,13 @@ export class UserService { return createdProvider; } + /** + * Update User displayName and photoURL + * + * @param {AuthUser} user User object + * @param {any} profile Data received from SSO provider on the users account + * @returns {Promise | E.Left<"user/not_found">>} Updated user object + */ async updateUserDetails(user: AuthUser, profile) { try { const updatedUser = await this.prisma.user.update({