strapi-plugin-magic-link-v5 4.15.2 → 4.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/_chunks/{index-DS6PLbr2.js → index-B42ZwsAG.js} +2 -2
- package/dist/_chunks/{index-UVs49kU3.mjs → index-BNmznBjA.mjs} +63 -2
- package/dist/_chunks/{index-DJXc9sCn.js → index-BZ_MHHzj.js} +1 -1
- package/dist/_chunks/{index-BR9E5y4C.mjs → index-BhkA52jz.mjs} +1 -1
- package/dist/_chunks/{index-RMaBG9DA.js → index-CXYCiJxF.js} +63 -2
- package/dist/_chunks/{index-loURsawN.mjs → index-vWNBTz70.mjs} +2 -2
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +25 -3
- package/dist/server/index.mjs +25 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Magic Link - Passwordless Authentication for Strapi v5
|
|
2
2
|
|
|
3
|
-
Secure passwordless authentication for Strapi using email-based magic links.
|
|
3
|
+
Secure passwordless authentication for Strapi v5 using email-based magic links. Simple, secure, and user-friendly - no passwords required.
|
|
4
4
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.npmjs.com/package/strapi-plugin-magic-link-v5)
|
|
@@ -44,7 +44,7 @@ This plugin is licensed under the **MIT License** - free for everyone to use!
|
|
|
44
44
|
## ✨ Features
|
|
45
45
|
|
|
46
46
|
### Core Authentication
|
|
47
|
-
- 🔐 **Passwordless Login** - Users log in via secure email links
|
|
47
|
+
- 🔐 **Passwordless Login** - Users log in via secure email magic links
|
|
48
48
|
- 🎫 **Magic Link Tokens** - Cryptographically secure, time-limited tokens
|
|
49
49
|
- 🔑 **JWT Session Management** - Monitor and manage active user sessions
|
|
50
50
|
- 👤 **Auto User Creation** - Optionally create users automatically on first login
|
|
@@ -60,7 +60,7 @@ const index = {
|
|
|
60
60
|
},
|
|
61
61
|
Component: () => Promise.resolve().then(() => require(
|
|
62
62
|
/* webpackChunkName: "magic-link-tokens" */
|
|
63
|
-
"./index-
|
|
63
|
+
"./index-CXYCiJxF.js"
|
|
64
64
|
)),
|
|
65
65
|
permissions: []
|
|
66
66
|
// Leeres Array = keine Permission-Prüfung nötig
|
|
@@ -83,7 +83,7 @@ const index = {
|
|
|
83
83
|
to: `${PLUGIN_ID}/config`,
|
|
84
84
|
Component: () => Promise.resolve().then(() => require(
|
|
85
85
|
/* webpackChunkName: "magic-link-settings" */
|
|
86
|
-
"./index-
|
|
86
|
+
"./index-BZ_MHHzj.js"
|
|
87
87
|
)),
|
|
88
88
|
permissions: pluginPermissions.readSettings
|
|
89
89
|
},
|
|
@@ -5,7 +5,7 @@ import styled, { keyframes, css } from "styled-components";
|
|
|
5
5
|
import { useIntl } from "react-intl";
|
|
6
6
|
import { Box, Flex, Typography, Button, TextInput, Textarea, Checkbox, IconButton, Loader, Searchbar, SingleSelect, SingleSelectOption, Thead, Tr, Th, Tbody, Td, Pagination, PreviousLink, PageLink, NextLink, Table, Badge, Main, VisuallyHidden } from "@strapi/design-system";
|
|
7
7
|
import { Cross, Sparkle, Check, Shield, Clock, Lock, Trash, ArrowClockwise, CaretDown, User, Monitor, Calendar, Plus, Earth, WarningCircle, Server, Cog, Key, Eye, Link } from "@strapi/icons";
|
|
8
|
-
import { g as getTrad } from "./index-
|
|
8
|
+
import { g as getTrad } from "./index-vWNBTz70.mjs";
|
|
9
9
|
import { useFetchClient, useNotification } from "@strapi/strapi/admin";
|
|
10
10
|
import { L as LicenseGuard, a as LanguageProvider } from "./LicenseGuard-CaYQ_9Dz.mjs";
|
|
11
11
|
const CreateTokenModal = ({ isOpen, onClose, onSubmit, formData, setFormData }) => {
|
|
@@ -2097,8 +2097,11 @@ const theme = {
|
|
|
2097
2097
|
},
|
|
2098
2098
|
neutral: {
|
|
2099
2099
|
0: "#FFFFFF",
|
|
2100
|
+
200: "#E5E7EB",
|
|
2100
2101
|
500: "#6B7280",
|
|
2101
|
-
|
|
2102
|
+
600: "#4B5563",
|
|
2103
|
+
800: "#1F2937",
|
|
2104
|
+
900: "#111827"
|
|
2102
2105
|
}
|
|
2103
2106
|
},
|
|
2104
2107
|
shadows: {
|
|
@@ -2147,6 +2150,26 @@ const slideIn = keyframes`
|
|
|
2147
2150
|
transform: translateX(0);
|
|
2148
2151
|
}
|
|
2149
2152
|
`;
|
|
2153
|
+
keyframes`
|
|
2154
|
+
from {
|
|
2155
|
+
opacity: 0;
|
|
2156
|
+
transform: translateX(30px);
|
|
2157
|
+
}
|
|
2158
|
+
to {
|
|
2159
|
+
opacity: 1;
|
|
2160
|
+
transform: translateX(0);
|
|
2161
|
+
}
|
|
2162
|
+
`;
|
|
2163
|
+
keyframes`
|
|
2164
|
+
from {
|
|
2165
|
+
opacity: 0;
|
|
2166
|
+
transform: translateX(-30px);
|
|
2167
|
+
}
|
|
2168
|
+
to {
|
|
2169
|
+
opacity: 1;
|
|
2170
|
+
transform: translateX(0);
|
|
2171
|
+
}
|
|
2172
|
+
`;
|
|
2150
2173
|
keyframes`
|
|
2151
2174
|
0%, 100% {
|
|
2152
2175
|
transform: scale(1);
|
|
@@ -2313,6 +2336,15 @@ const TabsWrapper = styled(Box)`
|
|
|
2313
2336
|
max-width: 900px;
|
|
2314
2337
|
margin-left: auto;
|
|
2315
2338
|
margin-right: auto;
|
|
2339
|
+
|
|
2340
|
+
@media screen and (max-width: 768px) {
|
|
2341
|
+
background: ${theme.colors.neutral[200]} !important;
|
|
2342
|
+
border-radius: 12px !important;
|
|
2343
|
+
padding: 6px !important;
|
|
2344
|
+
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1) !important;
|
|
2345
|
+
margin: 0 12px 20px 12px !important;
|
|
2346
|
+
max-width: none !important;
|
|
2347
|
+
}
|
|
2316
2348
|
`;
|
|
2317
2349
|
const TabButton = styled(Button)`
|
|
2318
2350
|
border-radius: ${theme.borderRadius.lg};
|
|
@@ -2341,6 +2373,35 @@ const TabButton = styled(Button)`
|
|
|
2341
2373
|
border-color: ${props.theme.colors.neutral400};
|
|
2342
2374
|
}
|
|
2343
2375
|
`}
|
|
2376
|
+
|
|
2377
|
+
@media screen and (max-width: 768px) {
|
|
2378
|
+
flex: 1 !important;
|
|
2379
|
+
border-radius: 10px !important;
|
|
2380
|
+
padding: 11px 8px !important;
|
|
2381
|
+
font-size: 13px !important;
|
|
2382
|
+
border: none !important;
|
|
2383
|
+
background: ${(props) => props.$active ? "white" : "transparent"} !important;
|
|
2384
|
+
color: ${(props) => props.$active ? theme.colors.neutral[900] : theme.colors.neutral[600]} !important;
|
|
2385
|
+
box-shadow: ${(props) => props.$active ? "0 2px 6px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04)" : "none"} !important;
|
|
2386
|
+
font-weight: ${(props) => props.$active ? "700" : "500"} !important;
|
|
2387
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
|
2388
|
+
transform: ${(props) => props.$active ? "scale(1)" : "scale(0.96)"} !important;
|
|
2389
|
+
|
|
2390
|
+
&:hover {
|
|
2391
|
+
background: ${(props) => props.$active ? "white" : "rgba(255,255,255,0.3)"} !important;
|
|
2392
|
+
transform: ${(props) => props.$active ? "scale(1)" : "scale(0.98)"} !important;
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
&:active {
|
|
2396
|
+
transform: scale(0.94) !important;
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
svg {
|
|
2400
|
+
width: 16px !important;
|
|
2401
|
+
height: 16px !important;
|
|
2402
|
+
margin-right: 4px !important;
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2344
2405
|
`;
|
|
2345
2406
|
const StatsGrid = styled(Box)`
|
|
2346
2407
|
margin-bottom: ${theme.spacing.xl};
|
|
@@ -7,7 +7,7 @@ const reactIntl = require("react-intl");
|
|
|
7
7
|
const designSystem = require("@strapi/design-system");
|
|
8
8
|
const icons = require("@strapi/icons");
|
|
9
9
|
const admin = require("@strapi/strapi/admin");
|
|
10
|
-
const index = require("./index-
|
|
10
|
+
const index = require("./index-B42ZwsAG.js");
|
|
11
11
|
const LicenseGuard = require("./LicenseGuard-CmMMPuWx.js");
|
|
12
12
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
13
13
|
const styled__default = /* @__PURE__ */ _interopDefault(styled);
|
|
@@ -5,7 +5,7 @@ import { useIntl } from "react-intl";
|
|
|
5
5
|
import { Box, Typography, Flex, Button, Accordion, SingleSelect, SingleSelectOption, Grid, Toggle, NumberInput, TextInput, Divider, Badge } from "@strapi/design-system";
|
|
6
6
|
import { Check, Cog, Shield, Mail, Code } from "@strapi/icons";
|
|
7
7
|
import { useNotification, useFetchClient } from "@strapi/strapi/admin";
|
|
8
|
-
import { g as getTrad } from "./index-
|
|
8
|
+
import { g as getTrad } from "./index-vWNBTz70.mjs";
|
|
9
9
|
import { u as usePluginLanguage, L as LicenseGuard, a as LanguageProvider } from "./LicenseGuard-CaYQ_9Dz.mjs";
|
|
10
10
|
const theme = {
|
|
11
11
|
colors: {
|
|
@@ -7,7 +7,7 @@ const styled = require("styled-components");
|
|
|
7
7
|
const reactIntl = require("react-intl");
|
|
8
8
|
const designSystem = require("@strapi/design-system");
|
|
9
9
|
const icons = require("@strapi/icons");
|
|
10
|
-
const index = require("./index-
|
|
10
|
+
const index = require("./index-B42ZwsAG.js");
|
|
11
11
|
const admin = require("@strapi/strapi/admin");
|
|
12
12
|
const LicenseGuard = require("./LicenseGuard-CmMMPuWx.js");
|
|
13
13
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
@@ -2101,8 +2101,11 @@ const theme = {
|
|
|
2101
2101
|
},
|
|
2102
2102
|
neutral: {
|
|
2103
2103
|
0: "#FFFFFF",
|
|
2104
|
+
200: "#E5E7EB",
|
|
2104
2105
|
500: "#6B7280",
|
|
2105
|
-
|
|
2106
|
+
600: "#4B5563",
|
|
2107
|
+
800: "#1F2937",
|
|
2108
|
+
900: "#111827"
|
|
2106
2109
|
}
|
|
2107
2110
|
},
|
|
2108
2111
|
shadows: {
|
|
@@ -2151,6 +2154,26 @@ const slideIn = styled.keyframes`
|
|
|
2151
2154
|
transform: translateX(0);
|
|
2152
2155
|
}
|
|
2153
2156
|
`;
|
|
2157
|
+
styled.keyframes`
|
|
2158
|
+
from {
|
|
2159
|
+
opacity: 0;
|
|
2160
|
+
transform: translateX(30px);
|
|
2161
|
+
}
|
|
2162
|
+
to {
|
|
2163
|
+
opacity: 1;
|
|
2164
|
+
transform: translateX(0);
|
|
2165
|
+
}
|
|
2166
|
+
`;
|
|
2167
|
+
styled.keyframes`
|
|
2168
|
+
from {
|
|
2169
|
+
opacity: 0;
|
|
2170
|
+
transform: translateX(-30px);
|
|
2171
|
+
}
|
|
2172
|
+
to {
|
|
2173
|
+
opacity: 1;
|
|
2174
|
+
transform: translateX(0);
|
|
2175
|
+
}
|
|
2176
|
+
`;
|
|
2154
2177
|
styled.keyframes`
|
|
2155
2178
|
0%, 100% {
|
|
2156
2179
|
transform: scale(1);
|
|
@@ -2317,6 +2340,15 @@ const TabsWrapper = styled__default.default(designSystem.Box)`
|
|
|
2317
2340
|
max-width: 900px;
|
|
2318
2341
|
margin-left: auto;
|
|
2319
2342
|
margin-right: auto;
|
|
2343
|
+
|
|
2344
|
+
@media screen and (max-width: 768px) {
|
|
2345
|
+
background: ${theme.colors.neutral[200]} !important;
|
|
2346
|
+
border-radius: 12px !important;
|
|
2347
|
+
padding: 6px !important;
|
|
2348
|
+
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1) !important;
|
|
2349
|
+
margin: 0 12px 20px 12px !important;
|
|
2350
|
+
max-width: none !important;
|
|
2351
|
+
}
|
|
2320
2352
|
`;
|
|
2321
2353
|
const TabButton = styled__default.default(designSystem.Button)`
|
|
2322
2354
|
border-radius: ${theme.borderRadius.lg};
|
|
@@ -2345,6 +2377,35 @@ const TabButton = styled__default.default(designSystem.Button)`
|
|
|
2345
2377
|
border-color: ${props.theme.colors.neutral400};
|
|
2346
2378
|
}
|
|
2347
2379
|
`}
|
|
2380
|
+
|
|
2381
|
+
@media screen and (max-width: 768px) {
|
|
2382
|
+
flex: 1 !important;
|
|
2383
|
+
border-radius: 10px !important;
|
|
2384
|
+
padding: 11px 8px !important;
|
|
2385
|
+
font-size: 13px !important;
|
|
2386
|
+
border: none !important;
|
|
2387
|
+
background: ${(props) => props.$active ? "white" : "transparent"} !important;
|
|
2388
|
+
color: ${(props) => props.$active ? theme.colors.neutral[900] : theme.colors.neutral[600]} !important;
|
|
2389
|
+
box-shadow: ${(props) => props.$active ? "0 2px 6px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04)" : "none"} !important;
|
|
2390
|
+
font-weight: ${(props) => props.$active ? "700" : "500"} !important;
|
|
2391
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
|
2392
|
+
transform: ${(props) => props.$active ? "scale(1)" : "scale(0.96)"} !important;
|
|
2393
|
+
|
|
2394
|
+
&:hover {
|
|
2395
|
+
background: ${(props) => props.$active ? "white" : "rgba(255,255,255,0.3)"} !important;
|
|
2396
|
+
transform: ${(props) => props.$active ? "scale(1)" : "scale(0.98)"} !important;
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
&:active {
|
|
2400
|
+
transform: scale(0.94) !important;
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
svg {
|
|
2404
|
+
width: 16px !important;
|
|
2405
|
+
height: 16px !important;
|
|
2406
|
+
margin-right: 4px !important;
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2348
2409
|
`;
|
|
2349
2410
|
const StatsGrid = styled__default.default(designSystem.Box)`
|
|
2350
2411
|
margin-bottom: ${theme.spacing.xl};
|
|
@@ -59,7 +59,7 @@ const index = {
|
|
|
59
59
|
},
|
|
60
60
|
Component: () => import(
|
|
61
61
|
/* webpackChunkName: "magic-link-tokens" */
|
|
62
|
-
"./index-
|
|
62
|
+
"./index-BNmznBjA.mjs"
|
|
63
63
|
),
|
|
64
64
|
permissions: []
|
|
65
65
|
// Leeres Array = keine Permission-Prüfung nötig
|
|
@@ -82,7 +82,7 @@ const index = {
|
|
|
82
82
|
to: `${PLUGIN_ID}/config`,
|
|
83
83
|
Component: () => import(
|
|
84
84
|
/* webpackChunkName: "magic-link-settings" */
|
|
85
|
-
"./index-
|
|
85
|
+
"./index-BhkA52jz.mjs"
|
|
86
86
|
),
|
|
87
87
|
permissions: pluginPermissions.readSettings
|
|
88
88
|
},
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -239,6 +239,27 @@ Thanks.`,
|
|
|
239
239
|
} catch (error2) {
|
|
240
240
|
strapi2.log.error("❌ Error initializing License Guard:", error2);
|
|
241
241
|
}
|
|
242
|
+
try {
|
|
243
|
+
setTimeout(async () => {
|
|
244
|
+
const usersToUpdate = await strapi2.db.query("plugin::users-permissions.user").findMany({
|
|
245
|
+
where: { provider: "magic-link" },
|
|
246
|
+
select: ["id", "email"]
|
|
247
|
+
});
|
|
248
|
+
if (usersToUpdate && usersToUpdate.length > 0) {
|
|
249
|
+
strapi2.log.info('🔄 [Magic-Link Migration] Found %d user(s) with old provider "magic-link"', usersToUpdate.length);
|
|
250
|
+
await strapi2.db.query("plugin::users-permissions.user").updateMany({
|
|
251
|
+
where: { provider: "magic-link" },
|
|
252
|
+
data: { provider: "local" }
|
|
253
|
+
});
|
|
254
|
+
strapi2.log.info('✅ [Magic-Link Migration] Updated %d user(s) to provider "local"', usersToUpdate.length);
|
|
255
|
+
strapi2.log.info(" Users can now login with both Magic-Link AND Email/Password! 🎉");
|
|
256
|
+
} else {
|
|
257
|
+
strapi2.log.info('✅ [Magic-Link Migration] All users already using provider "local" - no migration needed');
|
|
258
|
+
}
|
|
259
|
+
}, 5e3);
|
|
260
|
+
} catch (error2) {
|
|
261
|
+
strapi2.log.error("❌ [Magic-Link Migration] Error updating providers:", error2);
|
|
262
|
+
}
|
|
242
263
|
};
|
|
243
264
|
var destroy$1 = ({ strapi: strapi2 }) => {
|
|
244
265
|
try {
|
|
@@ -23119,7 +23140,8 @@ var tokens$1 = {
|
|
|
23119
23140
|
username,
|
|
23120
23141
|
email: email2,
|
|
23121
23142
|
password,
|
|
23122
|
-
provider: "
|
|
23143
|
+
provider: "local",
|
|
23144
|
+
// Use 'local' so users can login with email/password AND magic-link
|
|
23123
23145
|
confirmed: true,
|
|
23124
23146
|
// Auto-confirm the user
|
|
23125
23147
|
blocked: false,
|
|
@@ -23858,8 +23880,8 @@ var license$1 = {
|
|
|
23858
23880
|
}
|
|
23859
23881
|
},
|
|
23860
23882
|
/**
|
|
23861
|
-
|
|
23862
|
-
|
|
23883
|
+
* Store and validate an existing license key
|
|
23884
|
+
*/
|
|
23863
23885
|
async storeKey(ctx) {
|
|
23864
23886
|
try {
|
|
23865
23887
|
const { licenseKey, email: email2 } = ctx.request.body;
|
package/dist/server/index.mjs
CHANGED
|
@@ -207,6 +207,27 @@ Thanks.`,
|
|
|
207
207
|
} catch (error2) {
|
|
208
208
|
strapi2.log.error("❌ Error initializing License Guard:", error2);
|
|
209
209
|
}
|
|
210
|
+
try {
|
|
211
|
+
setTimeout(async () => {
|
|
212
|
+
const usersToUpdate = await strapi2.db.query("plugin::users-permissions.user").findMany({
|
|
213
|
+
where: { provider: "magic-link" },
|
|
214
|
+
select: ["id", "email"]
|
|
215
|
+
});
|
|
216
|
+
if (usersToUpdate && usersToUpdate.length > 0) {
|
|
217
|
+
strapi2.log.info('🔄 [Magic-Link Migration] Found %d user(s) with old provider "magic-link"', usersToUpdate.length);
|
|
218
|
+
await strapi2.db.query("plugin::users-permissions.user").updateMany({
|
|
219
|
+
where: { provider: "magic-link" },
|
|
220
|
+
data: { provider: "local" }
|
|
221
|
+
});
|
|
222
|
+
strapi2.log.info('✅ [Magic-Link Migration] Updated %d user(s) to provider "local"', usersToUpdate.length);
|
|
223
|
+
strapi2.log.info(" Users can now login with both Magic-Link AND Email/Password! 🎉");
|
|
224
|
+
} else {
|
|
225
|
+
strapi2.log.info('✅ [Magic-Link Migration] All users already using provider "local" - no migration needed');
|
|
226
|
+
}
|
|
227
|
+
}, 5e3);
|
|
228
|
+
} catch (error2) {
|
|
229
|
+
strapi2.log.error("❌ [Magic-Link Migration] Error updating providers:", error2);
|
|
230
|
+
}
|
|
210
231
|
};
|
|
211
232
|
var destroy$1 = ({ strapi: strapi2 }) => {
|
|
212
233
|
try {
|
|
@@ -23087,7 +23108,8 @@ var tokens$1 = {
|
|
|
23087
23108
|
username,
|
|
23088
23109
|
email: email2,
|
|
23089
23110
|
password,
|
|
23090
|
-
provider: "
|
|
23111
|
+
provider: "local",
|
|
23112
|
+
// Use 'local' so users can login with email/password AND magic-link
|
|
23091
23113
|
confirmed: true,
|
|
23092
23114
|
// Auto-confirm the user
|
|
23093
23115
|
blocked: false,
|
|
@@ -23826,8 +23848,8 @@ var license$1 = {
|
|
|
23826
23848
|
}
|
|
23827
23849
|
},
|
|
23828
23850
|
/**
|
|
23829
|
-
|
|
23830
|
-
|
|
23851
|
+
* Store and validate an existing license key
|
|
23852
|
+
*/
|
|
23831
23853
|
async storeKey(ctx) {
|
|
23832
23854
|
try {
|
|
23833
23855
|
const { licenseKey, email: email2 } = ctx.request.body;
|
package/package.json
CHANGED