strapi-plugin-passwordless
strapi-plugin-passwordless copied to clipboard
Extending / customizing a plugin
Did anyone try to extend strapi-plugin-passwordless
?
I tried methods suggested in official docs and I'm not able to customize createToken
service function.
I've tried the extensions folder technique and override it through server-strapi.js. Any advice on what should I try next?
Hi, thank you for the issue.
To be honest I didn't try to rewrite services, could you please give me the link to the official docs that are you trying to follow?
@Chmarusso Also, I can try to implement the logic in the public version of the plugin. I had planned to add token patterns - numbers, symbols, etc.
@kucherenko
That's a tutorial that I tried to follow: https://docs.strapi.io/dev-docs/plugins-extension#extending-a-plugin-s-interface
I had planned to add token patterns - numbers, symbols, etc.
That is exactly the reason why I'm trying to customize your extension 😃
I would like to use customAlphabet
from nanoid and just generate tokens that are only containing 0-9
In the meantime, I managed to override plugin in the following way:
- I've copied your package directory to my root dir of strapi
- changed nanoid call to use customAlphabet
- installed package from local directory
npm install ./path/strapi-plugin-passwordless
Not ideal, but works.
Regarding how your plugin may support custom alphabets in future. Perhaps something like this:
async createToken(email, context) {
const settings = await this.settings();
const {token_length = 20, alphabet = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"} = settings;
await strapi.query('plugin::passwordless.token').update({where: {email}, data: {is_active: false}});
const body = customAlphabet(alphabet, token_length);
const tokenInfo = {
email,
body: body(),
context: JSON.stringify(context)
};
return strapi.query('plugin::passwordless.token').create({data: tokenInfo});
},
@kucherenko
Hi, is there any progress there.
I plan to generate a code that only contains numbers.
Are there already settings for this or do you have to overwrite the code yourself?
Hi! I could do this, it may help:
- Create a folder named
passwordless
in src/extensions - Create a strapi-server.ts file with the following:
import passwordlessService from "./services/passwordless";
export default (plugin) => {
plugin.services.passwordless = passwordlessService;
return plugin;
};
Use this function to replace a controller or service as you wish. In my case replaces the passwordless service with a custom service.
Hi! I could do this, it may help:
- Create a folder named
passwordless
in src/extensions- Create a strapi-server.ts file with the following:
import passwordlessService from "./services/passwordless"; export default (plugin) => { plugin.services.passwordless = passwordlessService; return plugin; };
Use this function to replace a controller or service as you wish. In my case replaces the passwordless service with a custom service.
Okay thanks i will try it
Thanks @marcosnc08
Here is a complete working implementation.
// src/extenstions/passwordless/strapi-server.ts
import { Strapi } from '@strapi/strapi'
import passwordlessService from 'strapi-plugin-passwordless/server/services/passwordless'
const extendedPasswordlessService = ({ strapi }: { strapi: Strapi }) => {
const { sendLoginLink: _extracted, ...otherMethods } = passwordlessService({ strapi })
return {
...otherMethods,
async sendLoginLink(token) {
const settings = await this.settings()
const user = await this.fetchUser({ email: token.email })
let context = undefined
try {
context = JSON.parse(JSON.parse(token.context))
} catch (_) {
console.error('passwordless send-link failed to parse context: context should be in JSON format.')
}
const params = {
URL: settings.confirmationUrl,
CODE: token.body,
USER: user,
CONTEXT: context,
}
console.log('sendLoginLink params:', params)
const text = await this.template(settings.message_text, params)
const html = await this.template(settings.message_html, params)
const subject = await this.template(settings.object, params)
const sendData = {
to: token.email,
from: settings.from_email && settings.from_name ? `${settings.from_name} <${settings.from_email}>` : undefined,
replyTo: settings.response_email,
subject,
text,
html,
}
// Send an email to the user.
return await strapi.plugin('email').service('email').send(sendData)
},
}
}
export default (plugin) => {
plugin.services.passwordless = extendedPasswordlessService
return plugin
}
And here is the log I get:
sendLoginLink params: {
URL: 'http://localhost:3000/fr/mon-compte',
CODE: 'k3D2mMGKZqkqv4gCkUmq',
USER: {
id: 18,
username: 'Kim',
email: '[email protected]',
provider: null,
confirmed: false,
blocked: false,
createdAt: '2024-02-01T11:20:58.478Z',
updatedAt: '2024-02-01T11:20:58.478Z',
role: {
id: 6,
name: 'Authenticated',
description: 'Default role given to authenticated user.',
type: 'authenticated',
createdAt: '2023-03-02T18:53:41.863Z',
updatedAt: '2023-03-02T18:53:41.863Z'
}
},
CONTEXT: { to: 'http://localhost:3000/fr/ici' }
}
And here is how it can be used in the admin "Subject", "Message (text)" and "Message (html)" fields:
Context.to: <%= CONTEXT.to %>
USER.username: <%= USER.username %>
:warning: With this implementation, the context param of the request should be formatted in json.
Ex: {"to":"http://localhost:3000/fr/ici"}
.
I saw in the code that the templates use lodash template, where we can find more details about syntax.
thank @cvolant for the code exemple, I was able to change some parts and use an email template from Brevo directly, instead of the standard template of passwordless plugin
@cvolant I just realized there's a double parse on your code
context = JSON.parse(JSON.parse(token.context))