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 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. No passwords required.
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: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
6
  [![npm version](https://badge.fury.io/js/strapi-plugin-magic-link-v5.svg)](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-RMaBG9DA.js"
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-DJXc9sCn.js"
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-loURsawN.mjs";
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
- 800: "#1F2937"
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-DS6PLbr2.js");
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-loURsawN.mjs";
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-DS6PLbr2.js");
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
- 800: "#1F2937"
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-UVs49kU3.mjs"
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-BR9E5y4C.mjs"
85
+ "./index-BhkA52jz.mjs"
86
86
  ),
87
87
  permissions: pluginPermissions.readSettings
88
88
  },
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
- const index = require("../_chunks/index-DS6PLbr2.js");
2
+ const index = require("../_chunks/index-B42ZwsAG.js");
3
3
  require("@strapi/icons");
4
4
  module.exports = index.index;
@@ -1,4 +1,4 @@
1
- import { i } from "../_chunks/index-loURsawN.mjs";
1
+ import { i } from "../_chunks/index-vWNBTz70.mjs";
2
2
  import "@strapi/icons";
3
3
  export {
4
4
  i as default
@@ -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: "magic-link",
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
- * Store and validate an existing license key
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;
@@ -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: "magic-link",
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
- * Store and validate an existing license key
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
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.15.2",
2
+ "version": "4.16.1",
3
3
  "keywords": [],
4
4
  "type": "commonjs",
5
5
  "exports": {