diff --git a/src/_staart/interfaces/enum.ts b/src/_staart/interfaces/enum.ts index b7907fce3..28e442d0a 100644 --- a/src/_staart/interfaces/enum.ts +++ b/src/_staart/interfaces/enum.ts @@ -34,6 +34,7 @@ export enum Templates { UNAPPROVED_LOCATION = "unapproved-location", CREDITS_NEW_USER = "credits-new-user", CREDITS_INVITED_BY = "credits-invited-by", + LOGIN_LINK = "login-link", } export enum Tokens { diff --git a/src/_staart/rest/auth.ts b/src/_staart/rest/auth.ts index cf67e529a..d4fe35195 100644 --- a/src/_staart/rest/auth.ts +++ b/src/_staart/rest/auth.ts @@ -54,10 +54,10 @@ export const validateRefreshToken = async (token: string, locals: Locals) => { }; export const invalidateRefreshToken = async (token: string, locals: Locals) => { - const data = await verifyToken<{ id: string }>(token, Tokens.REFRESH); + const data = await verifyToken<{ id: number }>(token, Tokens.REFRESH); if (!data.id) throw new Error(USER_NOT_FOUND); await prisma.sessions.deleteMany({ - where: { token, userId: parseInt(data.id) }, + where: { token, userId: data.id }, }); return; }; @@ -85,7 +85,10 @@ export const login = async ( if (hasUserWithUnverifiedEmail) throw new Error("401/unverified-email"); throw new Error(USER_NOT_FOUND); } - if (!user.password) throw new Error(MISSING_PASSWORD); + if (!user.password) { + await mail({ template: Templates.LOGIN_LINK, data: user, to: email }); + return { success: true, message: "login-link-sent" }; + } const isPasswordCorrect = await compare(password, user.password); if (isPasswordCorrect) return getLoginResponse(user, EventType.AUTH_LOGIN, "local", locals); @@ -210,10 +213,10 @@ export const sendNewPassword = async (userId: number, email: string) => { export const verifyEmail = async (token: string, locals: Locals) => { const emailId = ( - await verifyToken<{ id: string }>(token, Tokens.EMAIL_VERIFY) + await verifyToken<{ id: number }>(token, Tokens.EMAIL_VERIFY) ).id; const email = await prisma.emails.findOne({ - where: { id: parseInt(emailId) }, + where: { id: emailId }, }); if (!email) throw new Error(RESOURCE_NOT_FOUND); trackEvent( @@ -225,21 +228,39 @@ export const verifyEmail = async (token: string, locals: Locals) => { locals ); return prisma.emails.update({ - where: { id: parseInt(emailId) }, + where: { id: emailId }, data: { isVerified: true }, }); }; +export const loginLink = async (token: string, locals: Locals) => { + const userId = (await verifyToken<{ id: number }>(token, Tokens.EMAIL_VERIFY)) + .id; + const user = await prisma.users.findOne({ + where: { id: userId }, + }); + if (!user) throw new Error(RESOURCE_NOT_FOUND); + trackEvent( + { + userId, + type: EventType.AUTH_LOGIN, + data: { id: userId }, + }, + locals + ); + return postLoginTokens(user, locals); +}; + export const updatePassword = async ( token: string, password: string, locals: Locals ) => { const userId = ( - await verifyToken<{ id: string }>(token, Tokens.PASSWORD_RESET) + await verifyToken<{ id: number }>(token, Tokens.PASSWORD_RESET) ).id; await prisma.users.update({ - where: { id: parseInt(userId) }, + where: { id: userId }, data: { password: await hash(password, 8) }, }); await deleteItemFromCache(`cache_getUserById_${userId}`); diff --git a/src/_staart/rest/group.ts b/src/_staart/rest/group.ts index 478f7a3ca..155b6f255 100644 --- a/src/_staart/rest/group.ts +++ b/src/_staart/rest/group.ts @@ -646,7 +646,6 @@ export const inviteMemberToGroup = async ( const newAccount = await register( { name: newMemberName, - prefersEmail: {}, }, locals, newMemberEmail, diff --git a/src/controllers/auth/index.ts b/src/controllers/auth/index.ts index 43b6f379c..249f7b608 100644 --- a/src/controllers/auth/index.ts +++ b/src/controllers/auth/index.ts @@ -25,6 +25,7 @@ import { updatePassword, validateRefreshToken, verifyEmail, + loginLink, } from "../../_staart/rest/auth"; import { addInvitationCredits } from "../../_staart/rest/user"; import { twtToId } from "../../_staart/helpers/utils"; @@ -210,4 +211,12 @@ export class AuthController { await verifyEmail(token, res.locals); return respond(RESOURCE_SUCCESS); } + + @Post("login-link") + async postLoginLink(req: Request, res: Response) { + const token = req.body.token || req.params.token; + joiValidate({ token: Joi.string().required() }, { token }); + await loginLink(token, res.locals); + return respond(RESOURCE_SUCCESS); + } } diff --git a/src/templates/login-link.md b/src/templates/login-link.md new file mode 100644 index 000000000..11c9613d7 --- /dev/null +++ b/src/templates/login-link.md @@ -0,0 +1,7 @@ +# Your login link + +Dear {{name}}, + +To log in to your account, click on the following link: + +Login to your account