diff --git a/apps/backend/src/api/routes/auth.controller.ts b/apps/backend/src/api/routes/auth.controller.ts
index 1938ff82..d2c91791 100644
--- a/apps/backend/src/api/routes/auth.controller.ts
+++ b/apps/backend/src/api/routes/auth.controller.ts
@@ -4,6 +4,8 @@ import { Response, Request } from 'express';
import { CreateOrgUserDto } from '@gitroom/nestjs-libraries/dtos/auth/create.org.user.dto';
import { LoginUserDto } from '@gitroom/nestjs-libraries/dtos/auth/login.user.dto';
import { AuthService } from '@gitroom/backend/services/auth/auth.service';
+import { ForgotReturnPasswordDto } from '@gitroom/nestjs-libraries/dtos/auth/forgot-return.password.dto';
+import { ForgotPasswordDto } from '@gitroom/nestjs-libraries/dtos/auth/forgot.password.dto';
@Controller('/auth')
export class AuthController {
@@ -33,7 +35,7 @@ export class AuthController {
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
});
- if (typeof addedOrg !== 'boolean') {
+ if (typeof addedOrg !== 'boolean' && addedOrg?.organizationId) {
response.cookie('showorg', addedOrg.organizationId, {
domain: '.' + new URL(process.env.FRONTEND_URL!).hostname,
secure: true,
@@ -62,6 +64,7 @@ export class AuthController {
const getOrgFromCookie = this._authService.getOrgFromCookie(
req?.cookies?.org
);
+
const { jwt, addedOrg } = await this._authService.routeAuth(
body.provider,
body,
@@ -76,7 +79,7 @@ export class AuthController {
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
});
- if (typeof addedOrg !== 'boolean') {
+ if (typeof addedOrg !== 'boolean' && addedOrg?.organizationId) {
response.cookie('showorg', addedOrg.organizationId, {
domain: '.' + new URL(process.env.FRONTEND_URL!).hostname,
secure: true,
@@ -94,4 +97,26 @@ export class AuthController {
response.status(400).send(e.message);
}
}
+
+ @Post('/forgot')
+ async forgot(@Body() body: ForgotPasswordDto) {
+ try {
+ await this._authService.forgot(body.email);
+ return {
+ forgot: true,
+ };
+ } catch (e) {
+ return {
+ forgot: false,
+ };
+ }
+ }
+
+ @Post('/forgot-return')
+ async forgotReturn(@Body() body: ForgotReturnPasswordDto) {
+ const reset = await this._authService.forgotReturn(body);
+ return {
+ reset: !!reset,
+ };
+ }
}
diff --git a/apps/backend/src/services/auth/auth.service.ts b/apps/backend/src/services/auth/auth.service.ts
index d63fd04b..e3824740 100644
--- a/apps/backend/src/services/auth/auth.service.ts
+++ b/apps/backend/src/services/auth/auth.service.ts
@@ -7,12 +7,16 @@ import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/o
import { AuthService as AuthChecker } from '@gitroom/helpers/auth/auth.service';
import { ProvidersFactory } from '@gitroom/backend/services/auth/providers/providers.factory';
import dayjs from 'dayjs';
+import { NewsletterService } from '@gitroom/nestjs-libraries/services/newsletter.service';
+import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service';
+import {ForgotReturnPasswordDto} from "@gitroom/nestjs-libraries/dtos/auth/forgot-return.password.dto";
@Injectable()
export class AuthService {
constructor(
- private _user: UsersService,
- private _organization: OrganizationService,
+ private _userService: UsersService,
+ private _organizationService: OrganizationService,
+ private _notificationService: NotificationService
) {}
async routeAuth(
provider: Provider,
@@ -20,16 +24,17 @@ export class AuthService {
addToOrg?: boolean | { orgId: string; role: 'USER' | 'ADMIN'; id: string }
) {
if (provider === Provider.LOCAL) {
- const user = await this._user.getUserByEmail(body.email);
+ const user = await this._userService.getUserByEmail(body.email);
if (body instanceof CreateOrgUserDto) {
if (user) {
throw new Error('User already exists');
}
- const create = await this._organization.createOrgAndUser(body);
+ const create = await this._organizationService.createOrgAndUser(body);
+ NewsletterService.register(body.email);
const addedOrg =
addToOrg && typeof addToOrg !== 'boolean'
- ? await this._organization.addUserToOrg(
+ ? await this._organizationService.addUserToOrg(
create.users[0].user.id,
addToOrg.id,
addToOrg.orgId,
@@ -53,7 +58,7 @@ export class AuthService {
const addedOrg =
addToOrg && typeof addToOrg !== 'boolean'
- ? await this._organization.addUserToOrg(
+ ? await this._organizationService.addUserToOrg(
user.id,
addToOrg.id,
addToOrg.orgId,
@@ -95,12 +100,12 @@ export class AuthService {
throw new Error('Invalid provider token');
}
- const user = await this._user.getUserByProvider(providerUser.id, provider);
+ const user = await this._userService.getUserByProvider(providerUser.id, provider);
if (user) {
return user;
}
- const create = await this._organization.createOrgAndUser({
+ const create = await this._organizationService.createOrgAndUser({
company: '',
email: providerUser.email,
password: '',
@@ -108,9 +113,38 @@ export class AuthService {
providerId: providerUser.id,
});
+ NewsletterService.register(providerUser.email);
+
return create.users[0].user;
}
+ async forgot(email: string) {
+ const user = await this._userService.getUserByEmail(email);
+ if (!user || user.providerName !== Provider.LOCAL) {
+ return false;
+ }
+
+ const resetValues = AuthChecker.signJWT({
+ id: user.id,
+ expires: dayjs().add(20, 'minutes').format('YYYY-MM-DD HH:mm:ss'),
+ });
+
+ await this._notificationService.sendEmail(
+ user.email,
+ 'Reset your password',
+ `You have requested to reset your passsord.
Click here to reset your password
The link will expire in 20 minutes`
+ );
+ }
+
+ forgotReturn(body: ForgotReturnPasswordDto) {
+ const user = AuthChecker.verifyJWT(body.token) as {id: string, expires: string};
+ if (dayjs(user.expires).isBefore(dayjs())) {
+ return false;
+ }
+
+ return this._userService.updatePassword(user.id, body.password);
+ }
+
private async jwt(user: User) {
return AuthChecker.signJWT(user);
}
diff --git a/apps/frontend/public/auth/bg-login.png b/apps/frontend/public/auth/bg-login.png
index d79ca79b..e8d2cb1a 100644
Binary files a/apps/frontend/public/auth/bg-login.png and b/apps/frontend/public/auth/bg-login.png differ
diff --git a/apps/frontend/src/app/auth/forgot/[token]/page.tsx b/apps/frontend/src/app/auth/forgot/[token]/page.tsx
new file mode 100644
index 00000000..4f399ebe
--- /dev/null
+++ b/apps/frontend/src/app/auth/forgot/[token]/page.tsx
@@ -0,0 +1,5 @@
+import { ForgotReturn } from '@gitroom/frontend/components/auth/forgot-return';
+
+export default async function Auth(params: { params: { token: string } }) {
+ return
{
+ e.stopPropagation();
+ }}
+ className="mt-[16px] w-full text-wrap font-['Inter'] font-[400] text-[16px] text-[#D3D3D3] select-text"
+ dangerouslySetInnerHTML={{ __html: description }}
+ />
+