Magic-Sessionmanager — Examples
Force logout after password change
Keep users safe after a credential rotation:
typescript
// src/extensions/users-permissions/controllers/user.ts
module.exports = (plugin) => {
const originalUpdate = plugin.controllers.user.update;
plugin.controllers.user.update = async (ctx) => {
const result = await originalUpdate(ctx);
if (ctx.request.body.password) {
await strapi
.plugin('magic-sessionmanager')
.service('sessions')
.terminateAllForUser(ctx.params.id, 'password_change');
}
return result;
};
return plugin;
};Slack alert on suspicious login
Configure in admin → Sessions → Settings → Alerts:
Webhook URL: https://hooks.slack.com/services/...
Trigger: threatScore > 70 OR isVpn
Format: Slack blocksOr hook programmatically:
typescript
// src/index.ts
export default {
register({ strapi }) {
strapi.plugin('magic-sessionmanager').hooks.on('threat.detected', async ({ session, score }) => {
if (score < 70) return;
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `🚨 Suspicious login (score: ${score})`,
blocks: [
{
type: 'section',
fields: [
{ type: 'mrkdwn', text: `*User:*\n${session.userEmail}` },
{ type: 'mrkdwn', text: `*IP:*\n${session.ip}` },
{ type: 'mrkdwn', text: `*Country:*\n${session.geolocation.country}` },
{ type: 'mrkdwn', text: `*VPN:*\n${session.threatFlags.isVpn ? 'Yes' : 'No'}` },
],
},
{
type: 'actions',
elements: [{
type: 'button',
text: { type: 'plain_text', text: 'Force logout' },
url: `${process.env.STRAPI_URL}/admin/plugins/magic-sessionmanager/sessions/${session.id}`,
}],
},
],
}),
});
});
},
};Custom threat scoring rule
Extend the built-in threat scoring:
typescript
// src/index.ts
strapi.plugin('magic-sessionmanager').hooks.on('threat.scoring', async ({ session, score }) => {
// Add 25 points if login is outside business hours (in user's timezone)
const hour = new Date().getUTCHours();
if (hour < 6 || hour > 22) return score + 25;
return score;
});Geo-fencing per role
Different countries allowed for different roles:
typescript
// src/index.ts
strapi.plugin('magic-sessionmanager').hooks.on('login.attempt', async ({ user, session }) => {
const allowedCountries = {
admin: ['DE', 'AT', 'CH'],
employee: ['DE', 'AT', 'CH', 'US', 'GB'],
customer: null, // any country
};
const allowed = allowedCountries[user.role?.name];
if (allowed && !allowed.includes(session.geolocation.country)) {
throw new Error(`Login from ${session.geolocation.country} not allowed for ${user.role.name}`);
}
});Monitoring dashboard — custom endpoint
Expose session stats to an external dashboard:
typescript
// src/api/monitoring/controllers/monitoring.ts
export default {
async overview(ctx) {
const stats = await strapi.plugin('magic-sessionmanager').service('sessions').getStats();
ctx.body = {
online: stats.online,
threats: stats.threatScoreDistribution['80-100'] || 0,
vpnLogins: stats.vpnLoginsLast24h,
byCountry: stats.loginsByCountry,
};
},
};Audit log export
Weekly CSV export of all security-relevant events:
typescript
// src/cron.ts
export default {
'0 0 * * 0': async ({ strapi }) => {
const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
const logs = await strapi.db.query('plugin::magic-sessionmanager.audit-log').findMany({
where: { createdAt: { $gte: since } },
});
const csv = logsToCsv(logs);
await uploadToS3(csv, `audit-${since.toISOString().slice(0, 10)}.csv`);
},
};React admin widget — live session count
tsx
import { useEffect, useState } from 'react';
export function OnlineUsersWidget() {
const [count, setCount] = useState(0);
useEffect(() => {
async function fetchStats() {
const res = await fetch('/magic-sessionmanager/stats', {
headers: { Authorization: `Bearer ${adminToken}` },
});
const { online } = await res.json();
setCount(online);
}
fetchStats();
const id = setInterval(fetchStats, 30_000);
return () => clearInterval(id);
}, []);
return (
<div className="widget">
<h3>{count}</h3>
<p>users online</p>
</div>
);
}Migration from another session plugin
typescript
// One-time migration script
async function migrate({ strapi }) {
const oldSessions = await strapi.db.query('old-plugin::session').findMany();
for (const old of oldSessions) {
await strapi.db.query('plugin::magic-sessionmanager.session').create({
data: {
userId: old.user,
ip: old.ip,
userAgent: old.userAgent,
token: old.token,
createdAt: old.createdAt,
lastSeen: old.lastSeen,
},
});
}
}Next: Troubleshooting →