Strapi user permission with react-email

asdfas
Daniel Lazar
May 14, 2024 09:36
asdfas

The CMS strapi allow us to simply use user-permission plugin which has build in email templates for user email confirmation and reset password. The template is done by plain html injecting data.

<!-- users-permissions confirmation email template --> <p>Thank you for registering!</p> <p>You have to confirm your email address. Please click on the link below.</p> <p><%= URL %>?confirmation=<%= CODE %></p> <p>Thanks.</p>

As you can see it is only html and adding css manually is a pain and you need to write all classes. Fortunatelly for all react developers there is react-email which has build in tailwind and renders all data by react. It is perfect match to generate nice looking emails using react with tailwind. We can write just one simple file and render it. That's it.

So let's install strapi plugin

yarn add strapi-react-email

or

npm install strapi-react-email

Great now we can tweak template. On your navigation menu there should be React email

Screenshot from 2024-05-10 06-20-22.png

As you can see there are two templates already prepared. Those are two like in Settings > Emal templates but you can add more templates and of course localization is enabled. Those two templates are fully preconfigured and could be changed later. For this moment let's continue by extending users-permissions plugin.

Extension

Now extension of users-permission plugin needs to be done. Create following folder structure and create strapi-server.ts. Basically we need to override two parts:

  1. plugin.controllers.auth.forgotPassword
  2. plugin.services.user.sendConfirmationEmail
// <STRAPI_ROOT>/src/extensions/users-permissions/strapi-server.ts> import {yup, validateYupSchema, getAbsoluteServerUrl, getAbsoluteAdminUrl, sanitize} from '@strapi/utils'; import crypto from 'crypto'; import urlJoin from "url-join"; const forgotPasswordSchema = yup .object({ email: yup.string().email().required(), }) .noUnknown(); const validateForgotPasswordBody = validateYupSchema(forgotPasswordSchema); const sanitizeUser = (user, ctx) => { const { auth } = ctx.state; const userSchema = strapi.getModel('plugin::users-permissions.user'); return sanitize.contentAPI.output(user, userSchema, { auth }); }; module.exports = (plugin) => { plugin.controllers.auth.forgotPassword = async (ctx) => { const { email } = await validateForgotPasswordBody(ctx.request.body); const pluginStore = await strapi.store({ type: 'plugin', name: 'users-permissions' }); const advancedSettings = await pluginStore.get({ key: 'advanced' }) as {email_reset_password: string}; // Find the user by email. const user = await strapi .query('plugin::users-permissions.user') .findOne({ where: { email: email.toLowerCase() } }); if (!user || user.blocked) { return ctx.send({ ok: true }); } // Generate random token. const userInfo = await sanitizeUser(user, ctx); const resetPasswordToken = crypto.randomBytes(64).toString('hex'); // NOTE: Update the user before sending the email so an Admin can generate the link if the email fails await strapi.plugin('users-permissions').service('user').edit(user.id, { resetPasswordToken }); await strapi.plugin('strapi-react-email').service('reactEmail').sendEmail({ slug: 'reset-password', to: user.email, locale: 'en', emailProps: { user: userInfo, token: resetPasswordToken, url: advancedSettings.email_reset_password, server_url: getAbsoluteServerUrl(strapi.config), admin_url: getAbsoluteAdminUrl(strapi.config) }, }); ctx.send({ ok: true }); } const oldUserServices = plugin.services.user({ strapi }); plugin.services.user = ({strapi}) => { return { ...oldUserServices, sendConfirmationEmail: async (user) => { const userSchema = strapi.getModel('plugin::users-permissions.user'); // Sanitize the template's user information const sanitizedUserInfo = await sanitize.sanitizers.defaultSanitizeOutput(userSchema, user); const confirmationToken = crypto.randomBytes(20).toString('hex'); await strapi.plugin('users-permissions').service('user').edit(user.id, { confirmationToken }); const apiPrefix = strapi.config.get('api.rest.prefix'); await strapi.plugin('strapi-react-email').service('reactEmail').sendEmail({ slug: 'email-address-confirmation', to: user.email, locale: 'en', emailProps:{ user: sanitizedUserInfo, token: confirmationToken, url: urlJoin(getAbsoluteServerUrl(strapi.config), apiPrefix, '/auth/email-confirmation'), server_url: getAbsoluteServerUrl(strapi.config), admin_url: getAbsoluteAdminUrl(strapi.config) } }); } } } return plugin; }

Now just play with your template, setup SMTP client and test it out. You can follow this to setup nodemailer