chore: :necktie: Add logs for magic link authentication #579
Tim Izzo tim@octree.ch
Thu, 20 Mar 2025 15:01:25 +0100
6 files changed,
105 insertions(+),
2 deletions(-)
A
backend/src/api/log/content-types/log/schema.json
@@ -0,0 +1,27 @@
+{ + "kind": "collectionType", + "collectionName": "logs", + "info": { + "singularName": "log", + "pluralName": "logs", + "displayName": "Log", + "description": "" + }, + "options": { + "draftAndPublish": false + }, + "pluginOptions": {}, + "attributes": { + "type": { + "type": "enumeration", + "enum": [ + "AUTH_TOKEN_GENERATION", + "AUTH_TOKEN_VERIFICATION" + ], + "required": true + }, + "payload": { + "type": "json" + } + } +}
A
backend/src/api/log/controllers/log.ts
@@ -0,0 +1,7 @@
+/** + * log controller + */ + +import { factories } from '@strapi/strapi' + +export default factories.createCoreController('api::log.log');
A
backend/src/api/log/routes/log.ts
@@ -0,0 +1,7 @@
+/** + * log router + */ + +import { factories } from '@strapi/strapi'; + +export default factories.createCoreRouter('api::log.log');
A
backend/src/api/log/services/log.ts
@@ -0,0 +1,7 @@
+/** + * log service + */ + +import { factories } from '@strapi/strapi'; + +export default factories.createCoreService('api::log.log');
M
backend/src/extensions/users-permissions/services/magic-link.ts
→
backend/src/extensions/users-permissions/services/magic-link.ts
@@ -2,6 +2,11 @@ import jwt from "jsonwebtoken";
const MAGICLINK_SECRET = process.env.MAGICLINK_SECRET; +interface MagicTokenPayload { + email: string; + lang: string; +} + export const generateMagicToken = async (email: string, lang: string) => { const existingUser = await strapi.db .query("plugin::users-permissions.user")@@ -17,8 +22,31 @@ throw new Error("GoogleAccount");
} if (!MAGICLINK_SECRET) throw new Error("No MAGICLINK_SECRET provided"); + strapi.entityService.create("api::log.log", { + data: { type: "AUTH_TOKEN_GENERATION", payload: { email, lang } }, + }); + return jwt.sign({ email, lang }, MAGICLINK_SECRET, { expiresIn: "20m" }); }; -export const verifyMagicToken = (token: string) => - jwt.verify(token, MAGICLINK_SECRET); +export const verifyMagicToken = (token: string) => { + try { + const decoded = jwt.verify(token, MAGICLINK_SECRET) as MagicTokenPayload; + strapi.entityService.create("api::log.log", { + data: { + type: "AUTH_TOKEN_VERIFICATION", + payload: { email: decoded.email, lang: decoded.lang, valid: true }, + }, + }); + return decoded; + } catch (error) { + const decoded = jwt.decode(token) as MagicTokenPayload; + strapi.entityService.create("api::log.log", { + data: { + type: "AUTH_TOKEN_VERIFICATION", + payload: { email: decoded.email, lang: decoded.lang, valid: false }, + }, + }); + throw new Error("Invalid token"); + } +};
M
backend/types/generated/contentTypes.d.ts
→
backend/types/generated/contentTypes.d.ts
@@ -439,6 +439,32 @@ uuid: Attribute.String & Attribute.Unique;
}; } +export interface ApiLogLog extends Schema.CollectionType { + collectionName: 'logs'; + info: { + description: ''; + displayName: 'Log'; + pluralName: 'logs'; + singularName: 'log'; + }; + options: { + draftAndPublish: false; + }; + attributes: { + createdAt: Attribute.DateTime; + createdBy: Attribute.Relation<'api::log.log', 'oneToOne', 'admin::user'> & + Attribute.Private; + payload: Attribute.JSON; + type: Attribute.Enumeration< + ['AUTH_TOKEN_GENERATION', 'AUTH_TOKEN_VERIFICATION'] + > & + Attribute.Required; + updatedAt: Attribute.DateTime; + updatedBy: Attribute.Relation<'api::log.log', 'oneToOne', 'admin::user'> & + Attribute.Private; + }; +} + export interface ApiModuleModule extends Schema.SingleType { collectionName: 'modules'; info: {@@ -1312,6 +1338,7 @@ 'admin::transfer-token': AdminTransferToken;
'admin::transfer-token-permission': AdminTransferTokenPermission; 'admin::user': AdminUser; 'api::event.event': ApiEventEvent; + 'api::log.log': ApiLogLog; 'api::module.module': ApiModuleModule; 'api::notification.notification': ApiNotificationNotification; 'api::page.page': ApiPagePage;