securili-auth-vendor-package 3.0.0
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/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/securili-auth-widget.es.js +327 -0
- package/dist/securili-auth-widget.es.js.map +1 -0
- package/dist/securili-auth-widget.umd.js +2 -0
- package/dist/securili-auth-widget.umd.js.map +1 -0
- package/dist/types/index.d.ts +79 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Securili Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# securili-auth-vendor-package
|
|
2
|
+
|
|
3
|
+
> **v3.0** — Securili authentication widget for vendor sites. Drop-in React component for **login**, **signup**, and **2FA** powered by EzAuth.
|
|
4
|
+
|
|
5
|
+
A single, self-contained React component. No secrets are bundled — the vendor passes its own `vendorId` and (for 2FA) a `vendorToken` at render time, and all calls go to the `apiBaseUrl` you provide.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install securili-auth-vendor-package
|
|
11
|
+
# react + react-dom are peer dependencies
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
### Login / Signup (default mode)
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { SecuriliWidget } from 'securili-auth-vendor-package';
|
|
20
|
+
|
|
21
|
+
export default function LoginPage() {
|
|
22
|
+
return (
|
|
23
|
+
<SecuriliWidget
|
|
24
|
+
vendor="Acme Corp"
|
|
25
|
+
vendorId="b3acb7b9-02b9-421a-952e-a850c47c6d0a"
|
|
26
|
+
apiBaseUrl="https://api.securili.com"
|
|
27
|
+
onSuccess={() => console.log('authenticated')}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Two-Factor challenge only
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
<SecuriliWidget
|
|
37
|
+
mode="twofactor"
|
|
38
|
+
vendor="Acme Corp"
|
|
39
|
+
vendorId="b3acb7b9-..."
|
|
40
|
+
apiBaseUrl="https://api.securili.com"
|
|
41
|
+
userEmail="user@example.com"
|
|
42
|
+
vendorToken={vendorTokenFromYourBackend}
|
|
43
|
+
onSuccess={() => console.log('2fa passed')}
|
|
44
|
+
/>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Props
|
|
48
|
+
|
|
49
|
+
| Prop | Type | Required | Description |
|
|
50
|
+
|------|------|----------|-------------|
|
|
51
|
+
| `vendor` | `string` | ✅ | Vendor display name, sent to the API. |
|
|
52
|
+
| `vendorId` | `string` | ✅ | Sent as the `X-Vendor-Id` header. |
|
|
53
|
+
| `apiBaseUrl` | `string` | ✅ | Base URL of the Securili / EzAuth backend. |
|
|
54
|
+
| `onSuccess` | `() => void` | ✅ | Called on successful authentication, before redirect. |
|
|
55
|
+
| `mode` | `'Securili' \| 'twofactor'` | – | Defaults to `'Securili'`. |
|
|
56
|
+
| `userEmail` | `string` | – | Pre-fills email in `twofactor` mode. |
|
|
57
|
+
| `vendorToken` | `string` | – | Required to complete a `twofactor` challenge. |
|
|
58
|
+
|
|
59
|
+
## Security
|
|
60
|
+
|
|
61
|
+
- **No credentials are shipped** in this package — nothing reads `process.env`, and no `.env` files are bundled. The vendor supplies `vendorId` / `vendorToken` at runtime.
|
|
62
|
+
- **Open-redirect protection:** the `redirect_url` returned by the backend is validated and only followed if it is an `http(s)` URL (`javascript:` / `data:` URLs are rejected).
|
|
63
|
+
- **OTP/2FA inputs** are constrained to 6 numeric digits client-side.
|
|
64
|
+
- Requests are sent with `credentials: 'omit'` so ambient cookies aren't leaked cross-origin.
|
|
65
|
+
- Only the built `dist/` is published (`files` allowlist + `.npmignore`).
|
|
66
|
+
|
|
67
|
+
## API endpoints used
|
|
68
|
+
|
|
69
|
+
`POST` against `apiBaseUrl`:
|
|
70
|
+
|
|
71
|
+
- `/api/generate-otp-for-vendors/` — login OTP
|
|
72
|
+
- `/api/verify-login-otp/` — verify login OTP
|
|
73
|
+
- `/api/user-to-vendor-signup-request/` — signup OTP
|
|
74
|
+
- `/api/confirm-vendor-signup/` — confirm signup
|
|
75
|
+
- `/api/vendors/2fa/verify/` — verify 2FA code
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
MIT
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { jsxs as c, jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import { useState as u } from "react";
|
|
3
|
+
const e = {
|
|
4
|
+
colors: {
|
|
5
|
+
background: "#0D0D0D",
|
|
6
|
+
panel: "#1C1C1E",
|
|
7
|
+
primary: "#50c371",
|
|
8
|
+
primaryHover: "#178636",
|
|
9
|
+
text: {
|
|
10
|
+
primary: "#FFFFFF",
|
|
11
|
+
secondary: "rgba(255, 255, 255, 0.85)",
|
|
12
|
+
tertiary: "rgba(255, 255, 255, 0.65)"
|
|
13
|
+
},
|
|
14
|
+
success: "#24B44E",
|
|
15
|
+
error: "#FF4444",
|
|
16
|
+
warning: "#FFAA00",
|
|
17
|
+
info: "#007AFF",
|
|
18
|
+
border: "rgba(255, 255, 255, 0.1)",
|
|
19
|
+
shadow: "rgba(0, 0, 0, 0.3)"
|
|
20
|
+
},
|
|
21
|
+
spacing: {
|
|
22
|
+
xs: 4,
|
|
23
|
+
sm: 8,
|
|
24
|
+
md: 16,
|
|
25
|
+
lg: 24,
|
|
26
|
+
xl: 32,
|
|
27
|
+
xxl: 48
|
|
28
|
+
},
|
|
29
|
+
borderRadius: {
|
|
30
|
+
sm: 8,
|
|
31
|
+
md: 12,
|
|
32
|
+
lg: 16,
|
|
33
|
+
xl: 20
|
|
34
|
+
},
|
|
35
|
+
widget: {
|
|
36
|
+
maxWidth: "400px",
|
|
37
|
+
padding: "32px",
|
|
38
|
+
borderRadius: "16px",
|
|
39
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
40
|
+
titleFontSize: "24px",
|
|
41
|
+
subtitleFontSize: "14px",
|
|
42
|
+
inputBorderRadius: "8px",
|
|
43
|
+
boxShadow: "0 10px 25px rgba(0, 0, 0, 0.15), 0 4px 10px rgba(0, 0, 0, 0.1)"
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
function E(d) {
|
|
47
|
+
if (typeof d == "string")
|
|
48
|
+
try {
|
|
49
|
+
const y = new URL(d, window.location.origin);
|
|
50
|
+
(y.protocol === "http:" || y.protocol === "https:") && window.location.assign(y.toString());
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const Y = ({
|
|
55
|
+
vendor: d,
|
|
56
|
+
vendorId: y,
|
|
57
|
+
onSuccess: S,
|
|
58
|
+
apiBaseUrl: V,
|
|
59
|
+
mode: W = "Securili",
|
|
60
|
+
userEmail: w,
|
|
61
|
+
vendorToken: F
|
|
62
|
+
}) => {
|
|
63
|
+
const s = W === "twofactor", [p, j] = u(
|
|
64
|
+
() => s && w ? w : ""
|
|
65
|
+
), [C, D] = u(!1), [l, B] = u(""), [b, i] = u(""), [v, L] = u(!1), [r, g] = u(!1), M = V.replace(/\/+$/, ""), h = (o, n) => fetch(`${M}${o}`, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: {
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
"X-Vendor-Id": y
|
|
70
|
+
},
|
|
71
|
+
credentials: "omit",
|
|
72
|
+
body: JSON.stringify(n)
|
|
73
|
+
}), k = async (o) => {
|
|
74
|
+
if (s) return;
|
|
75
|
+
L(o === "signup"), i(""), g(!0);
|
|
76
|
+
const n = o === "signup" ? "/api/user-to-vendor-signup-request/" : "/api/generate-otp-for-vendors/";
|
|
77
|
+
try {
|
|
78
|
+
const a = await h(n, { email: p, vendor: d }), A = await a.json();
|
|
79
|
+
a.ok ? (D(!0), i(
|
|
80
|
+
o === "signup" ? "✅ Signup OTP sent! Check your Securili portal." : "✅ Login OTP sent. Please enter it below."
|
|
81
|
+
)) : i(`❌ ${A.error || A.message}`);
|
|
82
|
+
} catch {
|
|
83
|
+
i("❌ Network error.");
|
|
84
|
+
} finally {
|
|
85
|
+
g(!1);
|
|
86
|
+
}
|
|
87
|
+
}, R = async () => {
|
|
88
|
+
if (s) return;
|
|
89
|
+
i(""), g(!0);
|
|
90
|
+
const o = v ? "/api/confirm-vendor-signup/" : "/api/verify-login-otp/";
|
|
91
|
+
try {
|
|
92
|
+
const n = await h(o, { email: p, vendor: d, otp: l }), a = await n.json();
|
|
93
|
+
n.ok && a.redirect_url ? (i(
|
|
94
|
+
v ? "✅ Signup complete! Redirecting..." : "✅ Login successful! Redirecting..."
|
|
95
|
+
), S(), setTimeout(() => E(a.redirect_url), 500)) : i(`❌ ${a.error || a.message}`);
|
|
96
|
+
} catch {
|
|
97
|
+
i("❌ OTP verification failed.");
|
|
98
|
+
} finally {
|
|
99
|
+
g(!1);
|
|
100
|
+
}
|
|
101
|
+
}, T = async () => {
|
|
102
|
+
if (s) {
|
|
103
|
+
if (!F) {
|
|
104
|
+
i("❌ Vendor token missing");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
i(""), g(!0);
|
|
108
|
+
try {
|
|
109
|
+
const o = await h("/api/vendors/2fa/verify/", {
|
|
110
|
+
email: p,
|
|
111
|
+
vendor: d,
|
|
112
|
+
code: l,
|
|
113
|
+
vendor_token: F
|
|
114
|
+
}), n = await o.json();
|
|
115
|
+
o.ok && n.redirect_url ? (S(), E(n.redirect_url)) : i(`❌ ${n.error || n.message}`);
|
|
116
|
+
} catch {
|
|
117
|
+
i("❌ Verification failed.");
|
|
118
|
+
} finally {
|
|
119
|
+
g(!1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}, $ = (o) => o.replace(/\D/g, "").slice(0, 6), _ = {
|
|
123
|
+
backgroundColor: e.colors.panel,
|
|
124
|
+
border: `1px solid ${e.colors.border}`,
|
|
125
|
+
borderRadius: e.widget.borderRadius,
|
|
126
|
+
boxShadow: e.widget.boxShadow,
|
|
127
|
+
padding: e.widget.padding,
|
|
128
|
+
width: "100%",
|
|
129
|
+
maxWidth: e.widget.maxWidth,
|
|
130
|
+
margin: "0 auto",
|
|
131
|
+
fontFamily: e.widget.fontFamily
|
|
132
|
+
}, I = {
|
|
133
|
+
textAlign: "center",
|
|
134
|
+
marginBottom: e.spacing.lg
|
|
135
|
+
}, q = {
|
|
136
|
+
fontSize: e.widget.titleFontSize,
|
|
137
|
+
fontWeight: "bold",
|
|
138
|
+
marginBottom: e.spacing.sm,
|
|
139
|
+
color: e.colors.text.primary
|
|
140
|
+
}, J = {
|
|
141
|
+
color: e.colors.text.secondary,
|
|
142
|
+
fontSize: e.widget.subtitleFontSize
|
|
143
|
+
}, K = (o) => ({
|
|
144
|
+
padding: e.spacing.sm,
|
|
145
|
+
borderRadius: e.widget.inputBorderRadius,
|
|
146
|
+
marginBottom: e.spacing.md,
|
|
147
|
+
fontSize: "14px",
|
|
148
|
+
fontWeight: 500,
|
|
149
|
+
border: `1px solid ${o ? e.colors.success : e.colors.error}`,
|
|
150
|
+
backgroundColor: o ? `${e.colors.success}15` : `${e.colors.error}15`,
|
|
151
|
+
color: o ? e.colors.success : e.colors.error
|
|
152
|
+
}), z = {
|
|
153
|
+
display: "block",
|
|
154
|
+
fontSize: "14px",
|
|
155
|
+
fontWeight: 500,
|
|
156
|
+
marginBottom: e.spacing.sm,
|
|
157
|
+
color: e.colors.text.primary
|
|
158
|
+
}, O = {
|
|
159
|
+
width: "100%",
|
|
160
|
+
padding: e.spacing.sm,
|
|
161
|
+
border: `1px solid ${e.colors.border}`,
|
|
162
|
+
borderRadius: e.widget.inputBorderRadius,
|
|
163
|
+
backgroundColor: e.colors.background,
|
|
164
|
+
color: e.colors.text.primary,
|
|
165
|
+
fontSize: "16px",
|
|
166
|
+
transition: "all 0.2s ease",
|
|
167
|
+
outline: "none",
|
|
168
|
+
boxSizing: "border-box"
|
|
169
|
+
}, P = {
|
|
170
|
+
...O,
|
|
171
|
+
textAlign: "center",
|
|
172
|
+
fontSize: "18px",
|
|
173
|
+
fontFamily: "monospace",
|
|
174
|
+
letterSpacing: "0.1em"
|
|
175
|
+
}, m = {
|
|
176
|
+
width: "100%",
|
|
177
|
+
padding: `${e.spacing.sm}px 0`,
|
|
178
|
+
backgroundColor: e.colors.primary,
|
|
179
|
+
color: "white",
|
|
180
|
+
fontWeight: 600,
|
|
181
|
+
borderRadius: e.widget.inputBorderRadius,
|
|
182
|
+
border: "none",
|
|
183
|
+
fontSize: "16px",
|
|
184
|
+
cursor: r ? "not-allowed" : "pointer",
|
|
185
|
+
opacity: r ? 0.6 : 1,
|
|
186
|
+
transition: "all 0.2s ease"
|
|
187
|
+
}, N = {
|
|
188
|
+
...m,
|
|
189
|
+
backgroundColor: "transparent",
|
|
190
|
+
color: e.colors.primary,
|
|
191
|
+
border: `1px solid ${e.colors.primary}`
|
|
192
|
+
}, U = {
|
|
193
|
+
display: "flex",
|
|
194
|
+
alignItems: "center",
|
|
195
|
+
justifyContent: "center",
|
|
196
|
+
padding: e.spacing.md,
|
|
197
|
+
borderRadius: e.widget.inputBorderRadius,
|
|
198
|
+
marginBottom: e.spacing.md,
|
|
199
|
+
backgroundColor: `${e.colors.primary}10`,
|
|
200
|
+
border: `1px solid ${e.colors.primary}30`
|
|
201
|
+
}, H = {
|
|
202
|
+
textAlign: "center",
|
|
203
|
+
paddingTop: e.spacing.md,
|
|
204
|
+
borderTop: `1px solid ${e.colors.border}`
|
|
205
|
+
}, X = {
|
|
206
|
+
fontSize: "12px",
|
|
207
|
+
color: e.colors.text.tertiary
|
|
208
|
+
}, f = (o) => {
|
|
209
|
+
o.target.style.borderColor = e.colors.primary, o.target.style.boxShadow = `0 0 0 2px ${e.colors.primary}30`;
|
|
210
|
+
}, x = (o) => {
|
|
211
|
+
o.target.style.borderColor = e.colors.border, o.target.style.boxShadow = "none";
|
|
212
|
+
};
|
|
213
|
+
return /* @__PURE__ */ c("div", { style: _, children: [
|
|
214
|
+
/* @__PURE__ */ c("div", { style: I, children: [
|
|
215
|
+
/* @__PURE__ */ t("h2", { style: q, children: s ? "🔒 Two-Factor Authentication" : "Securili Login" }),
|
|
216
|
+
!s && /* @__PURE__ */ t("p", { style: J, children: "Secure authentication powered by Securili" })
|
|
217
|
+
] }),
|
|
218
|
+
b && /* @__PURE__ */ t("div", { style: K(b.includes("✅")), children: b }),
|
|
219
|
+
!s && /* @__PURE__ */ c("div", { style: { marginBottom: e.spacing.md }, children: [
|
|
220
|
+
/* @__PURE__ */ t("label", { style: z, children: "Email Address *" }),
|
|
221
|
+
/* @__PURE__ */ t(
|
|
222
|
+
"input",
|
|
223
|
+
{
|
|
224
|
+
type: "email",
|
|
225
|
+
placeholder: "Enter your Securili email",
|
|
226
|
+
value: p,
|
|
227
|
+
onChange: (o) => j(o.target.value),
|
|
228
|
+
style: O,
|
|
229
|
+
onFocus: f,
|
|
230
|
+
onBlur: x,
|
|
231
|
+
disabled: r
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
] }),
|
|
235
|
+
!s && !C && /* @__PURE__ */ c("div", { style: { marginBottom: e.spacing.md }, children: [
|
|
236
|
+
/* @__PURE__ */ t(
|
|
237
|
+
"button",
|
|
238
|
+
{
|
|
239
|
+
onClick: () => k("login"),
|
|
240
|
+
style: m,
|
|
241
|
+
disabled: r || !p,
|
|
242
|
+
children: r ? "Processing..." : "Login with Securili"
|
|
243
|
+
}
|
|
244
|
+
),
|
|
245
|
+
/* @__PURE__ */ t(
|
|
246
|
+
"button",
|
|
247
|
+
{
|
|
248
|
+
onClick: () => k("signup"),
|
|
249
|
+
style: { ...N, marginTop: e.spacing.sm },
|
|
250
|
+
disabled: r || !p,
|
|
251
|
+
children: r ? "Processing..." : "Signup with Securili"
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
] }),
|
|
255
|
+
s && /* @__PURE__ */ c("div", { style: { marginBottom: e.spacing.md }, children: [
|
|
256
|
+
/* @__PURE__ */ t("div", { style: U, children: /* @__PURE__ */ t("p", { style: { color: e.colors.text.primary, margin: 0 }, children: "Enter your 6-digit authenticator code" }) }),
|
|
257
|
+
/* @__PURE__ */ t(
|
|
258
|
+
"input",
|
|
259
|
+
{
|
|
260
|
+
type: "text",
|
|
261
|
+
inputMode: "numeric",
|
|
262
|
+
autoComplete: "one-time-code",
|
|
263
|
+
placeholder: "Enter 2FA code",
|
|
264
|
+
value: l,
|
|
265
|
+
onChange: (o) => B($(o.target.value)),
|
|
266
|
+
style: P,
|
|
267
|
+
onFocus: f,
|
|
268
|
+
onBlur: x,
|
|
269
|
+
onKeyDown: (o) => {
|
|
270
|
+
o.key === "Enter" && l.length === 6 && !r && T();
|
|
271
|
+
},
|
|
272
|
+
disabled: r
|
|
273
|
+
}
|
|
274
|
+
),
|
|
275
|
+
/* @__PURE__ */ t(
|
|
276
|
+
"button",
|
|
277
|
+
{
|
|
278
|
+
onClick: T,
|
|
279
|
+
style: { ...m, marginTop: e.spacing.md },
|
|
280
|
+
disabled: r || l.length !== 6,
|
|
281
|
+
children: r ? "Verifying..." : "Verify 2FA Code"
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
] }),
|
|
285
|
+
C && !s && /* @__PURE__ */ c("div", { style: { marginBottom: e.spacing.md }, children: [
|
|
286
|
+
/* @__PURE__ */ t("label", { style: z, children: "Enter OTP Code" }),
|
|
287
|
+
/* @__PURE__ */ t(
|
|
288
|
+
"input",
|
|
289
|
+
{
|
|
290
|
+
type: "text",
|
|
291
|
+
inputMode: "numeric",
|
|
292
|
+
autoComplete: "one-time-code",
|
|
293
|
+
placeholder: "Enter OTP",
|
|
294
|
+
value: l,
|
|
295
|
+
onChange: (o) => B($(o.target.value)),
|
|
296
|
+
style: P,
|
|
297
|
+
onFocus: f,
|
|
298
|
+
onBlur: x,
|
|
299
|
+
onKeyDown: (o) => {
|
|
300
|
+
o.key === "Enter" && l && !r && R();
|
|
301
|
+
},
|
|
302
|
+
disabled: r
|
|
303
|
+
}
|
|
304
|
+
),
|
|
305
|
+
/* @__PURE__ */ t(
|
|
306
|
+
"button",
|
|
307
|
+
{
|
|
308
|
+
onClick: R,
|
|
309
|
+
style: { ...m, marginTop: e.spacing.md },
|
|
310
|
+
disabled: r || !l,
|
|
311
|
+
children: r ? "Verifying..." : "Confirm OTP"
|
|
312
|
+
}
|
|
313
|
+
)
|
|
314
|
+
] }),
|
|
315
|
+
/* @__PURE__ */ t("div", { style: H, children: /* @__PURE__ */ c("p", { style: X, children: [
|
|
316
|
+
"Powered by",
|
|
317
|
+
" ",
|
|
318
|
+
/* @__PURE__ */ t("span", { style: { color: e.colors.primary }, children: "Securili" })
|
|
319
|
+
] }) })
|
|
320
|
+
] });
|
|
321
|
+
};
|
|
322
|
+
export {
|
|
323
|
+
Y as SecuriliWidget,
|
|
324
|
+
e as Theme,
|
|
325
|
+
Y as default
|
|
326
|
+
};
|
|
327
|
+
//# sourceMappingURL=securili-auth-widget.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"securili-auth-widget.es.js","sources":["../src/constants/Theme.ts","../src/SecuriliWidget.tsx"],"sourcesContent":["/**\n * Securili design tokens.\n * Centralised so the widget's look stays consistent and themeable.\n */\nexport const Theme = {\n colors: {\n background: '#0D0D0D',\n panel: '#1C1C1E',\n primary: '#50c371',\n primaryHover: '#178636',\n text: {\n primary: '#FFFFFF',\n secondary: 'rgba(255, 255, 255, 0.85)',\n tertiary: 'rgba(255, 255, 255, 0.65)',\n },\n success: '#24B44E',\n error: '#FF4444',\n warning: '#FFAA00',\n info: '#007AFF',\n border: 'rgba(255, 255, 255, 0.1)',\n shadow: 'rgba(0, 0, 0, 0.3)',\n },\n spacing: {\n xs: 4,\n sm: 8,\n md: 16,\n lg: 24,\n xl: 32,\n xxl: 48,\n },\n borderRadius: {\n sm: 8,\n md: 12,\n lg: 16,\n xl: 20,\n },\n widget: {\n maxWidth: '400px',\n padding: '32px',\n borderRadius: '16px',\n fontFamily: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\",\n titleFontSize: '24px',\n subtitleFontSize: '14px',\n inputBorderRadius: '8px',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.15), 0 4px 10px rgba(0, 0, 0, 0.1)',\n },\n} as const;\n\nexport type ThemeColors = typeof Theme.colors;\nexport type ThemeSpacing = typeof Theme.spacing;\n","import React, { useState } from 'react';\nimport { Theme } from './constants/Theme';\n\nexport interface SecuriliWidgetProps {\n /** Vendor display name shown to the user and sent to the API. */\n vendor: string;\n /** Vendor identifier, sent as the `X-Vendor-Id` header. */\n vendorId: string;\n /** Called after a successful authentication, before any redirect. */\n onSuccess: () => void;\n /** Base URL of the Securili / EzAuth backend, e.g. `https://api.example.com`. */\n apiBaseUrl: string;\n /**\n * 'Securili' = full Securili login/signup (default)\n * 'twofactor' = simple 2FA challenge only\n */\n mode?: 'Securili' | 'twofactor';\n /** In twofactor mode, the vendor page supplies the user's email. */\n userEmail?: string;\n /** Opaque vendor token required to complete a 2FA challenge. */\n vendorToken?: string;\n}\n\n/**\n * Only allow http(s) redirects returned by the backend.\n * Prevents open-redirect / `javascript:` / `data:` injection via `redirect_url`.\n */\nfunction safeRedirect(rawUrl: unknown): void {\n if (typeof rawUrl !== 'string') return;\n try {\n const url = new URL(rawUrl, window.location.origin);\n if (url.protocol === 'http:' || url.protocol === 'https:') {\n window.location.assign(url.toString());\n }\n } catch {\n /* malformed URL – ignore */\n }\n}\n\nexport const SecuriliWidget: React.FC<SecuriliWidgetProps> = ({\n vendor,\n vendorId,\n onSuccess,\n apiBaseUrl,\n mode = 'Securili',\n userEmail,\n vendorToken,\n}) => {\n const isTwoFactor = mode === 'twofactor';\n\n const [email, setEmail] = useState<string>(() =>\n isTwoFactor && userEmail ? userEmail : ''\n );\n const [otpSent, setOtpSent] = useState(false);\n const [code, setCode] = useState('');\n const [status, setStatus] = useState('');\n const [signupMode, setSignupMode] = useState(false);\n const [loading, setLoading] = useState(false);\n\n // Trim a trailing slash so `${apiBaseUrl}${endpoint}` never double-slashes.\n const baseUrl = apiBaseUrl.replace(/\\/+$/, '');\n\n const postJson = (endpoint: string, body: Record<string, unknown>) =>\n fetch(`${baseUrl}${endpoint}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Vendor-Id': vendorId,\n },\n credentials: 'omit',\n body: JSON.stringify(body),\n });\n\n // Securili mode: request a login or signup OTP.\n const handleRequestOtp = async (action: 'login' | 'signup') => {\n if (isTwoFactor) return;\n setSignupMode(action === 'signup');\n setStatus('');\n setLoading(true);\n\n const endpoint =\n action === 'signup'\n ? '/api/user-to-vendor-signup-request/'\n : '/api/generate-otp-for-vendors/';\n\n try {\n const res = await postJson(endpoint, { email, vendor });\n const data = await res.json();\n if (res.ok) {\n setOtpSent(true);\n setStatus(\n action === 'signup'\n ? '✅ Signup OTP sent! Check your Securili portal.'\n : '✅ Login OTP sent. Please enter it below.'\n );\n } else {\n setStatus(`❌ ${data.error || data.message}`);\n }\n } catch {\n setStatus('❌ Network error.');\n } finally {\n setLoading(false);\n }\n };\n\n // Securili mode: verify the login or signup OTP.\n const handleVerifyOtp = async () => {\n if (isTwoFactor) return;\n setStatus('');\n setLoading(true);\n\n const endpoint = signupMode\n ? '/api/confirm-vendor-signup/'\n : '/api/verify-login-otp/';\n\n try {\n const res = await postJson(endpoint, { email, vendor, otp: code });\n const data = await res.json();\n if (res.ok && data.redirect_url) {\n setStatus(\n signupMode\n ? '✅ Signup complete! Redirecting...'\n : '✅ Login successful! Redirecting...'\n );\n onSuccess();\n setTimeout(() => safeRedirect(data.redirect_url), 500);\n } else {\n setStatus(`❌ ${data.error || data.message}`);\n }\n } catch {\n setStatus('❌ OTP verification failed.');\n } finally {\n setLoading(false);\n }\n };\n\n // Two-factor mode: verify the authenticator code.\n const handleVerifyTwoFactor = async () => {\n if (!isTwoFactor) return;\n if (!vendorToken) {\n setStatus('❌ Vendor token missing');\n return;\n }\n setStatus('');\n setLoading(true);\n\n try {\n const res = await postJson('/api/vendors/2fa/verify/', {\n email,\n vendor,\n code,\n vendor_token: vendorToken,\n });\n const data = await res.json();\n if (res.ok && data.redirect_url) {\n onSuccess();\n safeRedirect(data.redirect_url);\n } else {\n setStatus(`❌ ${data.error || data.message}`);\n }\n } catch {\n setStatus('❌ Verification failed.');\n } finally {\n setLoading(false);\n }\n };\n\n const onlyDigits = (value: string) => value.replace(/\\D/g, '').slice(0, 6);\n\n // ---- styles ----\n const cardStyle: React.CSSProperties = {\n backgroundColor: Theme.colors.panel,\n border: `1px solid ${Theme.colors.border}`,\n borderRadius: Theme.widget.borderRadius,\n boxShadow: Theme.widget.boxShadow,\n padding: Theme.widget.padding,\n width: '100%',\n maxWidth: Theme.widget.maxWidth,\n margin: '0 auto',\n fontFamily: Theme.widget.fontFamily,\n };\n const headerStyle: React.CSSProperties = {\n textAlign: 'center',\n marginBottom: Theme.spacing.lg,\n };\n const titleStyle: React.CSSProperties = {\n fontSize: Theme.widget.titleFontSize,\n fontWeight: 'bold',\n marginBottom: Theme.spacing.sm,\n color: Theme.colors.text.primary,\n };\n const subtitleStyle: React.CSSProperties = {\n color: Theme.colors.text.secondary,\n fontSize: Theme.widget.subtitleFontSize,\n };\n const statusStyle = (ok: boolean): React.CSSProperties => ({\n padding: Theme.spacing.sm,\n borderRadius: Theme.widget.inputBorderRadius,\n marginBottom: Theme.spacing.md,\n fontSize: '14px',\n fontWeight: 500,\n border: `1px solid ${ok ? Theme.colors.success : Theme.colors.error}`,\n backgroundColor: ok ? `${Theme.colors.success}15` : `${Theme.colors.error}15`,\n color: ok ? Theme.colors.success : Theme.colors.error,\n });\n const labelStyle: React.CSSProperties = {\n display: 'block',\n fontSize: '14px',\n fontWeight: 500,\n marginBottom: Theme.spacing.sm,\n color: Theme.colors.text.primary,\n };\n const inputStyle: React.CSSProperties = {\n width: '100%',\n padding: Theme.spacing.sm,\n border: `1px solid ${Theme.colors.border}`,\n borderRadius: Theme.widget.inputBorderRadius,\n backgroundColor: Theme.colors.background,\n color: Theme.colors.text.primary,\n fontSize: '16px',\n transition: 'all 0.2s ease',\n outline: 'none',\n boxSizing: 'border-box',\n };\n const codeInputStyle: React.CSSProperties = {\n ...inputStyle,\n textAlign: 'center',\n fontSize: '18px',\n fontFamily: 'monospace',\n letterSpacing: '0.1em',\n };\n const primaryButton: React.CSSProperties = {\n width: '100%',\n padding: `${Theme.spacing.sm}px 0`,\n backgroundColor: Theme.colors.primary,\n color: 'white',\n fontWeight: 600,\n borderRadius: Theme.widget.inputBorderRadius,\n border: 'none',\n fontSize: '16px',\n cursor: loading ? 'not-allowed' : 'pointer',\n opacity: loading ? 0.6 : 1,\n transition: 'all 0.2s ease',\n };\n const secondaryButton: React.CSSProperties = {\n ...primaryButton,\n backgroundColor: 'transparent',\n color: Theme.colors.primary,\n border: `1px solid ${Theme.colors.primary}`,\n };\n const noticeStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: Theme.spacing.md,\n borderRadius: Theme.widget.inputBorderRadius,\n marginBottom: Theme.spacing.md,\n backgroundColor: `${Theme.colors.primary}10`,\n border: `1px solid ${Theme.colors.primary}30`,\n };\n const footerStyle: React.CSSProperties = {\n textAlign: 'center',\n paddingTop: Theme.spacing.md,\n borderTop: `1px solid ${Theme.colors.border}`,\n };\n const footerTextStyle: React.CSSProperties = {\n fontSize: '12px',\n color: Theme.colors.text.tertiary,\n };\n\n const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {\n e.target.style.borderColor = Theme.colors.primary;\n e.target.style.boxShadow = `0 0 0 2px ${Theme.colors.primary}30`;\n };\n const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {\n e.target.style.borderColor = Theme.colors.border;\n e.target.style.boxShadow = 'none';\n };\n\n return (\n <div style={cardStyle}>\n <div style={headerStyle}>\n <h2 style={titleStyle}>\n {isTwoFactor ? '🔒 Two-Factor Authentication' : 'Securili Login'}\n </h2>\n {!isTwoFactor && (\n <p style={subtitleStyle}>Secure authentication powered by Securili</p>\n )}\n </div>\n\n {status && <div style={statusStyle(status.includes('✅'))}>{status}</div>}\n\n {!isTwoFactor && (\n <div style={{ marginBottom: Theme.spacing.md }}>\n <label style={labelStyle}>Email Address *</label>\n <input\n type=\"email\"\n placeholder=\"Enter your Securili email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n style={inputStyle}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={loading}\n />\n </div>\n )}\n\n {!isTwoFactor && !otpSent && (\n <div style={{ marginBottom: Theme.spacing.md }}>\n <button\n onClick={() => handleRequestOtp('login')}\n style={primaryButton}\n disabled={loading || !email}\n >\n {loading ? 'Processing...' : 'Login with Securili'}\n </button>\n <button\n onClick={() => handleRequestOtp('signup')}\n style={{ ...secondaryButton, marginTop: Theme.spacing.sm }}\n disabled={loading || !email}\n >\n {loading ? 'Processing...' : 'Signup with Securili'}\n </button>\n </div>\n )}\n\n {isTwoFactor && (\n <div style={{ marginBottom: Theme.spacing.md }}>\n <div style={noticeStyle}>\n <p style={{ color: Theme.colors.text.primary, margin: 0 }}>\n Enter your 6-digit authenticator code\n </p>\n </div>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n placeholder=\"Enter 2FA code\"\n value={code}\n onChange={(e) => setCode(onlyDigits(e.target.value))}\n style={codeInputStyle}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && code.length === 6 && !loading) {\n handleVerifyTwoFactor();\n }\n }}\n disabled={loading}\n />\n <button\n onClick={handleVerifyTwoFactor}\n style={{ ...primaryButton, marginTop: Theme.spacing.md }}\n disabled={loading || code.length !== 6}\n >\n {loading ? 'Verifying...' : 'Verify 2FA Code'}\n </button>\n </div>\n )}\n\n {otpSent && !isTwoFactor && (\n <div style={{ marginBottom: Theme.spacing.md }}>\n <label style={labelStyle}>Enter OTP Code</label>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n placeholder=\"Enter OTP\"\n value={code}\n onChange={(e) => setCode(onlyDigits(e.target.value))}\n style={codeInputStyle}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && code && !loading) {\n handleVerifyOtp();\n }\n }}\n disabled={loading}\n />\n <button\n onClick={handleVerifyOtp}\n style={{ ...primaryButton, marginTop: Theme.spacing.md }}\n disabled={loading || !code}\n >\n {loading ? 'Verifying...' : 'Confirm OTP'}\n </button>\n </div>\n )}\n\n <div style={footerStyle}>\n <p style={footerTextStyle}>\n Powered by{' '}\n <span style={{ color: Theme.colors.primary }}>Securili</span>\n </p>\n </div>\n </div>\n );\n};\n\nexport default SecuriliWidget;\n"],"names":["Theme","safeRedirect","rawUrl","url","SecuriliWidget","vendor","vendorId","onSuccess","apiBaseUrl","mode","userEmail","vendorToken","isTwoFactor","email","setEmail","useState","otpSent","setOtpSent","code","setCode","status","setStatus","signupMode","setSignupMode","loading","setLoading","baseUrl","postJson","endpoint","body","handleRequestOtp","action","res","data","handleVerifyOtp","handleVerifyTwoFactor","onlyDigits","value","cardStyle","headerStyle","titleStyle","subtitleStyle","statusStyle","ok","labelStyle","inputStyle","codeInputStyle","primaryButton","secondaryButton","noticeStyle","footerStyle","footerTextStyle","handleFocus","e","handleBlur","jsxs","jsx"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(s,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],t):(s=typeof globalThis<"u"?globalThis:s||self,t(s.SecuriliWidget={},s.jsxRuntime,s.React))})(this,(function(s,t,c){"use strict";const e={colors:{background:"#0D0D0D",panel:"#1C1C1E",primary:"#50c371",primaryHover:"#178636",text:{primary:"#FFFFFF",secondary:"rgba(255, 255, 255, 0.85)",tertiary:"rgba(255, 255, 255, 0.65)"},success:"#24B44E",error:"#FF4444",warning:"#FFAA00",info:"#007AFF",border:"rgba(255, 255, 255, 0.1)",shadow:"rgba(0, 0, 0, 0.3)"},spacing:{xs:4,sm:8,md:16,lg:24,xl:32,xxl:48},borderRadius:{sm:8,md:12,lg:16,xl:20},widget:{maxWidth:"400px",padding:"32px",borderRadius:"16px",fontFamily:"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",titleFontSize:"24px",subtitleFontSize:"14px",inputBorderRadius:"8px",boxShadow:"0 10px 25px rgba(0, 0, 0, 0.15), 0 4px 10px rgba(0, 0, 0, 0.1)"}};function x(p){if(typeof p=="string")try{const y=new URL(p,window.location.origin);(y.protocol==="http:"||y.protocol==="https:")&&window.location.assign(y.toString())}catch{}}const w=({vendor:p,vendorId:y,onSuccess:F,apiBaseUrl:V,mode:D="Securili",userEmail:C,vendorToken:v})=>{const l=D==="twofactor",[g,M]=c.useState(()=>l&&C?C:""),[B,_]=c.useState(!1),[a,T]=c.useState(""),[m,i]=c.useState(""),[k,L]=c.useState(!1),[r,u]=c.useState(!1),q=V.replace(/\/+$/,""),h=(o,n)=>fetch(`${q}${o}`,{method:"POST",headers:{"Content-Type":"application/json","X-Vendor-Id":y},credentials:"omit",body:JSON.stringify(n)}),$=async o=>{if(l)return;L(o==="signup"),i(""),u(!0);const n=o==="signup"?"/api/user-to-vendor-signup-request/":"/api/generate-otp-for-vendors/";try{const d=await h(n,{email:g,vendor:p}),W=await d.json();d.ok?(_(!0),i(o==="signup"?"✅ Signup OTP sent! Check your Securili portal.":"✅ Login OTP sent. Please enter it below.")):i(`❌ ${W.error||W.message}`)}catch{i("❌ Network error.")}finally{u(!1)}},z=async()=>{if(l)return;i(""),u(!0);const o=k?"/api/confirm-vendor-signup/":"/api/verify-login-otp/";try{const n=await h(o,{email:g,vendor:p,otp:a}),d=await n.json();n.ok&&d.redirect_url?(i(k?"✅ Signup complete! Redirecting...":"✅ Login successful! Redirecting..."),F(),setTimeout(()=>x(d.redirect_url),500)):i(`❌ ${d.error||d.message}`)}catch{i("❌ OTP verification failed.")}finally{u(!1)}},O=async()=>{if(l){if(!v){i("❌ Vendor token missing");return}i(""),u(!0);try{const o=await h("/api/vendors/2fa/verify/",{email:g,vendor:p,code:a,vendor_token:v}),n=await o.json();o.ok&&n.redirect_url?(F(),x(n.redirect_url)):i(`❌ ${n.error||n.message}`)}catch{i("❌ Verification failed.")}finally{u(!1)}}},P=o=>o.replace(/\D/g,"").slice(0,6),I={backgroundColor:e.colors.panel,border:`1px solid ${e.colors.border}`,borderRadius:e.widget.borderRadius,boxShadow:e.widget.boxShadow,padding:e.widget.padding,width:"100%",maxWidth:e.widget.maxWidth,margin:"0 auto",fontFamily:e.widget.fontFamily},J={textAlign:"center",marginBottom:e.spacing.lg},K={fontSize:e.widget.titleFontSize,fontWeight:"bold",marginBottom:e.spacing.sm,color:e.colors.text.primary},N={color:e.colors.text.secondary,fontSize:e.widget.subtitleFontSize},U=o=>({padding:e.spacing.sm,borderRadius:e.widget.inputBorderRadius,marginBottom:e.spacing.md,fontSize:"14px",fontWeight:500,border:`1px solid ${o?e.colors.success:e.colors.error}`,backgroundColor:o?`${e.colors.success}15`:`${e.colors.error}15`,color:o?e.colors.success:e.colors.error}),j={display:"block",fontSize:"14px",fontWeight:500,marginBottom:e.spacing.sm,color:e.colors.text.primary},A={width:"100%",padding:e.spacing.sm,border:`1px solid ${e.colors.border}`,borderRadius:e.widget.inputBorderRadius,backgroundColor:e.colors.background,color:e.colors.text.primary,fontSize:"16px",transition:"all 0.2s ease",outline:"none",boxSizing:"border-box"},E={...A,textAlign:"center",fontSize:"18px",fontFamily:"monospace",letterSpacing:"0.1em"},f={width:"100%",padding:`${e.spacing.sm}px 0`,backgroundColor:e.colors.primary,color:"white",fontWeight:600,borderRadius:e.widget.inputBorderRadius,border:"none",fontSize:"16px",cursor:r?"not-allowed":"pointer",opacity:r?.6:1,transition:"all 0.2s ease"},H={...f,backgroundColor:"transparent",color:e.colors.primary,border:`1px solid ${e.colors.primary}`},X={display:"flex",alignItems:"center",justifyContent:"center",padding:e.spacing.md,borderRadius:e.widget.inputBorderRadius,marginBottom:e.spacing.md,backgroundColor:`${e.colors.primary}10`,border:`1px solid ${e.colors.primary}30`},G={textAlign:"center",paddingTop:e.spacing.md,borderTop:`1px solid ${e.colors.border}`},Q={fontSize:"12px",color:e.colors.text.tertiary},b=o=>{o.target.style.borderColor=e.colors.primary,o.target.style.boxShadow=`0 0 0 2px ${e.colors.primary}30`},S=o=>{o.target.style.borderColor=e.colors.border,o.target.style.boxShadow="none"};return t.jsxs("div",{style:I,children:[t.jsxs("div",{style:J,children:[t.jsx("h2",{style:K,children:l?"🔒 Two-Factor Authentication":"Securili Login"}),!l&&t.jsx("p",{style:N,children:"Secure authentication powered by Securili"})]}),m&&t.jsx("div",{style:U(m.includes("✅")),children:m}),!l&&t.jsxs("div",{style:{marginBottom:e.spacing.md},children:[t.jsx("label",{style:j,children:"Email Address *"}),t.jsx("input",{type:"email",placeholder:"Enter your Securili email",value:g,onChange:o=>M(o.target.value),style:A,onFocus:b,onBlur:S,disabled:r})]}),!l&&!B&&t.jsxs("div",{style:{marginBottom:e.spacing.md},children:[t.jsx("button",{onClick:()=>$("login"),style:f,disabled:r||!g,children:r?"Processing...":"Login with Securili"}),t.jsx("button",{onClick:()=>$("signup"),style:{...H,marginTop:e.spacing.sm},disabled:r||!g,children:r?"Processing...":"Signup with Securili"})]}),l&&t.jsxs("div",{style:{marginBottom:e.spacing.md},children:[t.jsx("div",{style:X,children:t.jsx("p",{style:{color:e.colors.text.primary,margin:0},children:"Enter your 6-digit authenticator code"})}),t.jsx("input",{type:"text",inputMode:"numeric",autoComplete:"one-time-code",placeholder:"Enter 2FA code",value:a,onChange:o=>T(P(o.target.value)),style:E,onFocus:b,onBlur:S,onKeyDown:o=>{o.key==="Enter"&&a.length===6&&!r&&O()},disabled:r}),t.jsx("button",{onClick:O,style:{...f,marginTop:e.spacing.md},disabled:r||a.length!==6,children:r?"Verifying...":"Verify 2FA Code"})]}),B&&!l&&t.jsxs("div",{style:{marginBottom:e.spacing.md},children:[t.jsx("label",{style:j,children:"Enter OTP Code"}),t.jsx("input",{type:"text",inputMode:"numeric",autoComplete:"one-time-code",placeholder:"Enter OTP",value:a,onChange:o=>T(P(o.target.value)),style:E,onFocus:b,onBlur:S,onKeyDown:o=>{o.key==="Enter"&&a&&!r&&z()},disabled:r}),t.jsx("button",{onClick:z,style:{...f,marginTop:e.spacing.md},disabled:r||!a,children:r?"Verifying...":"Confirm OTP"})]}),t.jsx("div",{style:G,children:t.jsxs("p",{style:Q,children:["Powered by"," ",t.jsx("span",{style:{color:e.colors.primary},children:"Securili"})]})})]})};s.SecuriliWidget=w,s.Theme=e,s.default=w,Object.defineProperties(s,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
|
|
2
|
+
//# sourceMappingURL=securili-auth-widget.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"securili-auth-widget.umd.js","sources":["../src/constants/Theme.ts","../src/SecuriliWidget.tsx"],"sourcesContent":["/**\n * Securili design tokens.\n * Centralised so the widget's look stays consistent and themeable.\n */\nexport const Theme = {\n colors: {\n background: '#0D0D0D',\n panel: '#1C1C1E',\n primary: '#50c371',\n primaryHover: '#178636',\n text: {\n primary: '#FFFFFF',\n secondary: 'rgba(255, 255, 255, 0.85)',\n tertiary: 'rgba(255, 255, 255, 0.65)',\n },\n success: '#24B44E',\n error: '#FF4444',\n warning: '#FFAA00',\n info: '#007AFF',\n border: 'rgba(255, 255, 255, 0.1)',\n shadow: 'rgba(0, 0, 0, 0.3)',\n },\n spacing: {\n xs: 4,\n sm: 8,\n md: 16,\n lg: 24,\n xl: 32,\n xxl: 48,\n },\n borderRadius: {\n sm: 8,\n md: 12,\n lg: 16,\n xl: 20,\n },\n widget: {\n maxWidth: '400px',\n padding: '32px',\n borderRadius: '16px',\n fontFamily: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\",\n titleFontSize: '24px',\n subtitleFontSize: '14px',\n inputBorderRadius: '8px',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.15), 0 4px 10px rgba(0, 0, 0, 0.1)',\n },\n} as const;\n\nexport type ThemeColors = typeof Theme.colors;\nexport type ThemeSpacing = typeof Theme.spacing;\n","import React, { useState } from 'react';\nimport { Theme } from './constants/Theme';\n\nexport interface SecuriliWidgetProps {\n /** Vendor display name shown to the user and sent to the API. */\n vendor: string;\n /** Vendor identifier, sent as the `X-Vendor-Id` header. */\n vendorId: string;\n /** Called after a successful authentication, before any redirect. */\n onSuccess: () => void;\n /** Base URL of the Securili / EzAuth backend, e.g. `https://api.example.com`. */\n apiBaseUrl: string;\n /**\n * 'Securili' = full Securili login/signup (default)\n * 'twofactor' = simple 2FA challenge only\n */\n mode?: 'Securili' | 'twofactor';\n /** In twofactor mode, the vendor page supplies the user's email. */\n userEmail?: string;\n /** Opaque vendor token required to complete a 2FA challenge. */\n vendorToken?: string;\n}\n\n/**\n * Only allow http(s) redirects returned by the backend.\n * Prevents open-redirect / `javascript:` / `data:` injection via `redirect_url`.\n */\nfunction safeRedirect(rawUrl: unknown): void {\n if (typeof rawUrl !== 'string') return;\n try {\n const url = new URL(rawUrl, window.location.origin);\n if (url.protocol === 'http:' || url.protocol === 'https:') {\n window.location.assign(url.toString());\n }\n } catch {\n /* malformed URL – ignore */\n }\n}\n\nexport const SecuriliWidget: React.FC<SecuriliWidgetProps> = ({\n vendor,\n vendorId,\n onSuccess,\n apiBaseUrl,\n mode = 'Securili',\n userEmail,\n vendorToken,\n}) => {\n const isTwoFactor = mode === 'twofactor';\n\n const [email, setEmail] = useState<string>(() =>\n isTwoFactor && userEmail ? userEmail : ''\n );\n const [otpSent, setOtpSent] = useState(false);\n const [code, setCode] = useState('');\n const [status, setStatus] = useState('');\n const [signupMode, setSignupMode] = useState(false);\n const [loading, setLoading] = useState(false);\n\n // Trim a trailing slash so `${apiBaseUrl}${endpoint}` never double-slashes.\n const baseUrl = apiBaseUrl.replace(/\\/+$/, '');\n\n const postJson = (endpoint: string, body: Record<string, unknown>) =>\n fetch(`${baseUrl}${endpoint}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Vendor-Id': vendorId,\n },\n credentials: 'omit',\n body: JSON.stringify(body),\n });\n\n // Securili mode: request a login or signup OTP.\n const handleRequestOtp = async (action: 'login' | 'signup') => {\n if (isTwoFactor) return;\n setSignupMode(action === 'signup');\n setStatus('');\n setLoading(true);\n\n const endpoint =\n action === 'signup'\n ? '/api/user-to-vendor-signup-request/'\n : '/api/generate-otp-for-vendors/';\n\n try {\n const res = await postJson(endpoint, { email, vendor });\n const data = await res.json();\n if (res.ok) {\n setOtpSent(true);\n setStatus(\n action === 'signup'\n ? '✅ Signup OTP sent! Check your Securili portal.'\n : '✅ Login OTP sent. Please enter it below.'\n );\n } else {\n setStatus(`❌ ${data.error || data.message}`);\n }\n } catch {\n setStatus('❌ Network error.');\n } finally {\n setLoading(false);\n }\n };\n\n // Securili mode: verify the login or signup OTP.\n const handleVerifyOtp = async () => {\n if (isTwoFactor) return;\n setStatus('');\n setLoading(true);\n\n const endpoint = signupMode\n ? '/api/confirm-vendor-signup/'\n : '/api/verify-login-otp/';\n\n try {\n const res = await postJson(endpoint, { email, vendor, otp: code });\n const data = await res.json();\n if (res.ok && data.redirect_url) {\n setStatus(\n signupMode\n ? '✅ Signup complete! Redirecting...'\n : '✅ Login successful! Redirecting...'\n );\n onSuccess();\n setTimeout(() => safeRedirect(data.redirect_url), 500);\n } else {\n setStatus(`❌ ${data.error || data.message}`);\n }\n } catch {\n setStatus('❌ OTP verification failed.');\n } finally {\n setLoading(false);\n }\n };\n\n // Two-factor mode: verify the authenticator code.\n const handleVerifyTwoFactor = async () => {\n if (!isTwoFactor) return;\n if (!vendorToken) {\n setStatus('❌ Vendor token missing');\n return;\n }\n setStatus('');\n setLoading(true);\n\n try {\n const res = await postJson('/api/vendors/2fa/verify/', {\n email,\n vendor,\n code,\n vendor_token: vendorToken,\n });\n const data = await res.json();\n if (res.ok && data.redirect_url) {\n onSuccess();\n safeRedirect(data.redirect_url);\n } else {\n setStatus(`❌ ${data.error || data.message}`);\n }\n } catch {\n setStatus('❌ Verification failed.');\n } finally {\n setLoading(false);\n }\n };\n\n const onlyDigits = (value: string) => value.replace(/\\D/g, '').slice(0, 6);\n\n // ---- styles ----\n const cardStyle: React.CSSProperties = {\n backgroundColor: Theme.colors.panel,\n border: `1px solid ${Theme.colors.border}`,\n borderRadius: Theme.widget.borderRadius,\n boxShadow: Theme.widget.boxShadow,\n padding: Theme.widget.padding,\n width: '100%',\n maxWidth: Theme.widget.maxWidth,\n margin: '0 auto',\n fontFamily: Theme.widget.fontFamily,\n };\n const headerStyle: React.CSSProperties = {\n textAlign: 'center',\n marginBottom: Theme.spacing.lg,\n };\n const titleStyle: React.CSSProperties = {\n fontSize: Theme.widget.titleFontSize,\n fontWeight: 'bold',\n marginBottom: Theme.spacing.sm,\n color: Theme.colors.text.primary,\n };\n const subtitleStyle: React.CSSProperties = {\n color: Theme.colors.text.secondary,\n fontSize: Theme.widget.subtitleFontSize,\n };\n const statusStyle = (ok: boolean): React.CSSProperties => ({\n padding: Theme.spacing.sm,\n borderRadius: Theme.widget.inputBorderRadius,\n marginBottom: Theme.spacing.md,\n fontSize: '14px',\n fontWeight: 500,\n border: `1px solid ${ok ? Theme.colors.success : Theme.colors.error}`,\n backgroundColor: ok ? `${Theme.colors.success}15` : `${Theme.colors.error}15`,\n color: ok ? Theme.colors.success : Theme.colors.error,\n });\n const labelStyle: React.CSSProperties = {\n display: 'block',\n fontSize: '14px',\n fontWeight: 500,\n marginBottom: Theme.spacing.sm,\n color: Theme.colors.text.primary,\n };\n const inputStyle: React.CSSProperties = {\n width: '100%',\n padding: Theme.spacing.sm,\n border: `1px solid ${Theme.colors.border}`,\n borderRadius: Theme.widget.inputBorderRadius,\n backgroundColor: Theme.colors.background,\n color: Theme.colors.text.primary,\n fontSize: '16px',\n transition: 'all 0.2s ease',\n outline: 'none',\n boxSizing: 'border-box',\n };\n const codeInputStyle: React.CSSProperties = {\n ...inputStyle,\n textAlign: 'center',\n fontSize: '18px',\n fontFamily: 'monospace',\n letterSpacing: '0.1em',\n };\n const primaryButton: React.CSSProperties = {\n width: '100%',\n padding: `${Theme.spacing.sm}px 0`,\n backgroundColor: Theme.colors.primary,\n color: 'white',\n fontWeight: 600,\n borderRadius: Theme.widget.inputBorderRadius,\n border: 'none',\n fontSize: '16px',\n cursor: loading ? 'not-allowed' : 'pointer',\n opacity: loading ? 0.6 : 1,\n transition: 'all 0.2s ease',\n };\n const secondaryButton: React.CSSProperties = {\n ...primaryButton,\n backgroundColor: 'transparent',\n color: Theme.colors.primary,\n border: `1px solid ${Theme.colors.primary}`,\n };\n const noticeStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: Theme.spacing.md,\n borderRadius: Theme.widget.inputBorderRadius,\n marginBottom: Theme.spacing.md,\n backgroundColor: `${Theme.colors.primary}10`,\n border: `1px solid ${Theme.colors.primary}30`,\n };\n const footerStyle: React.CSSProperties = {\n textAlign: 'center',\n paddingTop: Theme.spacing.md,\n borderTop: `1px solid ${Theme.colors.border}`,\n };\n const footerTextStyle: React.CSSProperties = {\n fontSize: '12px',\n color: Theme.colors.text.tertiary,\n };\n\n const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {\n e.target.style.borderColor = Theme.colors.primary;\n e.target.style.boxShadow = `0 0 0 2px ${Theme.colors.primary}30`;\n };\n const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {\n e.target.style.borderColor = Theme.colors.border;\n e.target.style.boxShadow = 'none';\n };\n\n return (\n <div style={cardStyle}>\n <div style={headerStyle}>\n <h2 style={titleStyle}>\n {isTwoFactor ? '🔒 Two-Factor Authentication' : 'Securili Login'}\n </h2>\n {!isTwoFactor && (\n <p style={subtitleStyle}>Secure authentication powered by Securili</p>\n )}\n </div>\n\n {status && <div style={statusStyle(status.includes('✅'))}>{status}</div>}\n\n {!isTwoFactor && (\n <div style={{ marginBottom: Theme.spacing.md }}>\n <label style={labelStyle}>Email Address *</label>\n <input\n type=\"email\"\n placeholder=\"Enter your Securili email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n style={inputStyle}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={loading}\n />\n </div>\n )}\n\n {!isTwoFactor && !otpSent && (\n <div style={{ marginBottom: Theme.spacing.md }}>\n <button\n onClick={() => handleRequestOtp('login')}\n style={primaryButton}\n disabled={loading || !email}\n >\n {loading ? 'Processing...' : 'Login with Securili'}\n </button>\n <button\n onClick={() => handleRequestOtp('signup')}\n style={{ ...secondaryButton, marginTop: Theme.spacing.sm }}\n disabled={loading || !email}\n >\n {loading ? 'Processing...' : 'Signup with Securili'}\n </button>\n </div>\n )}\n\n {isTwoFactor && (\n <div style={{ marginBottom: Theme.spacing.md }}>\n <div style={noticeStyle}>\n <p style={{ color: Theme.colors.text.primary, margin: 0 }}>\n Enter your 6-digit authenticator code\n </p>\n </div>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n placeholder=\"Enter 2FA code\"\n value={code}\n onChange={(e) => setCode(onlyDigits(e.target.value))}\n style={codeInputStyle}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && code.length === 6 && !loading) {\n handleVerifyTwoFactor();\n }\n }}\n disabled={loading}\n />\n <button\n onClick={handleVerifyTwoFactor}\n style={{ ...primaryButton, marginTop: Theme.spacing.md }}\n disabled={loading || code.length !== 6}\n >\n {loading ? 'Verifying...' : 'Verify 2FA Code'}\n </button>\n </div>\n )}\n\n {otpSent && !isTwoFactor && (\n <div style={{ marginBottom: Theme.spacing.md }}>\n <label style={labelStyle}>Enter OTP Code</label>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n placeholder=\"Enter OTP\"\n value={code}\n onChange={(e) => setCode(onlyDigits(e.target.value))}\n style={codeInputStyle}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && code && !loading) {\n handleVerifyOtp();\n }\n }}\n disabled={loading}\n />\n <button\n onClick={handleVerifyOtp}\n style={{ ...primaryButton, marginTop: Theme.spacing.md }}\n disabled={loading || !code}\n >\n {loading ? 'Verifying...' : 'Confirm OTP'}\n </button>\n </div>\n )}\n\n <div style={footerStyle}>\n <p style={footerTextStyle}>\n Powered by{' '}\n <span style={{ color: Theme.colors.primary }}>Securili</span>\n </p>\n </div>\n </div>\n );\n};\n\nexport default SecuriliWidget;\n"],"names":["Theme","safeRedirect","rawUrl","url","SecuriliWidget","vendor","vendorId","onSuccess","apiBaseUrl","mode","userEmail","vendorToken","isTwoFactor","email","setEmail","useState","otpSent","setOtpSent","code","setCode","status","setStatus","signupMode","setSignupMode","loading","setLoading","baseUrl","postJson","endpoint","body","handleRequestOtp","action","res","data","handleVerifyOtp","handleVerifyTwoFactor","onlyDigits","value","cardStyle","headerStyle","titleStyle","subtitleStyle","statusStyle","ok","labelStyle","inputStyle","codeInputStyle","primaryButton","secondaryButton","noticeStyle","footerStyle","footerTextStyle","handleFocus","e","handleBlur","jsxs","jsx"],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { default as default_2 } from 'react';
|
|
2
|
+
|
|
3
|
+
declare const SecuriliWidget: default_2.FC<SecuriliWidgetProps>;
|
|
4
|
+
export { SecuriliWidget }
|
|
5
|
+
export default SecuriliWidget;
|
|
6
|
+
|
|
7
|
+
export declare interface SecuriliWidgetProps {
|
|
8
|
+
/** Vendor display name shown to the user and sent to the API. */
|
|
9
|
+
vendor: string;
|
|
10
|
+
/** Vendor identifier, sent as the `X-Vendor-Id` header. */
|
|
11
|
+
vendorId: string;
|
|
12
|
+
/** Called after a successful authentication, before any redirect. */
|
|
13
|
+
onSuccess: () => void;
|
|
14
|
+
/** Base URL of the Securili / EzAuth backend, e.g. `https://api.example.com`. */
|
|
15
|
+
apiBaseUrl: string;
|
|
16
|
+
/**
|
|
17
|
+
* 'Securili' = full Securili login/signup (default)
|
|
18
|
+
* 'twofactor' = simple 2FA challenge only
|
|
19
|
+
*/
|
|
20
|
+
mode?: 'Securili' | 'twofactor';
|
|
21
|
+
/** In twofactor mode, the vendor page supplies the user's email. */
|
|
22
|
+
userEmail?: string;
|
|
23
|
+
/** Opaque vendor token required to complete a 2FA challenge. */
|
|
24
|
+
vendorToken?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Securili design tokens.
|
|
29
|
+
* Centralised so the widget's look stays consistent and themeable.
|
|
30
|
+
*/
|
|
31
|
+
export declare const Theme: {
|
|
32
|
+
readonly colors: {
|
|
33
|
+
readonly background: "#0D0D0D";
|
|
34
|
+
readonly panel: "#1C1C1E";
|
|
35
|
+
readonly primary: "#50c371";
|
|
36
|
+
readonly primaryHover: "#178636";
|
|
37
|
+
readonly text: {
|
|
38
|
+
readonly primary: "#FFFFFF";
|
|
39
|
+
readonly secondary: "rgba(255, 255, 255, 0.85)";
|
|
40
|
+
readonly tertiary: "rgba(255, 255, 255, 0.65)";
|
|
41
|
+
};
|
|
42
|
+
readonly success: "#24B44E";
|
|
43
|
+
readonly error: "#FF4444";
|
|
44
|
+
readonly warning: "#FFAA00";
|
|
45
|
+
readonly info: "#007AFF";
|
|
46
|
+
readonly border: "rgba(255, 255, 255, 0.1)";
|
|
47
|
+
readonly shadow: "rgba(0, 0, 0, 0.3)";
|
|
48
|
+
};
|
|
49
|
+
readonly spacing: {
|
|
50
|
+
readonly xs: 4;
|
|
51
|
+
readonly sm: 8;
|
|
52
|
+
readonly md: 16;
|
|
53
|
+
readonly lg: 24;
|
|
54
|
+
readonly xl: 32;
|
|
55
|
+
readonly xxl: 48;
|
|
56
|
+
};
|
|
57
|
+
readonly borderRadius: {
|
|
58
|
+
readonly sm: 8;
|
|
59
|
+
readonly md: 12;
|
|
60
|
+
readonly lg: 16;
|
|
61
|
+
readonly xl: 20;
|
|
62
|
+
};
|
|
63
|
+
readonly widget: {
|
|
64
|
+
readonly maxWidth: "400px";
|
|
65
|
+
readonly padding: "32px";
|
|
66
|
+
readonly borderRadius: "16px";
|
|
67
|
+
readonly fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
|
|
68
|
+
readonly titleFontSize: "24px";
|
|
69
|
+
readonly subtitleFontSize: "14px";
|
|
70
|
+
readonly inputBorderRadius: "8px";
|
|
71
|
+
readonly boxShadow: "0 10px 25px rgba(0, 0, 0, 0.15), 0 4px 10px rgba(0, 0, 0, 0.1)";
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export declare type ThemeColors = typeof Theme.colors;
|
|
76
|
+
|
|
77
|
+
export declare type ThemeSpacing = typeof Theme.spacing;
|
|
78
|
+
|
|
79
|
+
export { }
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "securili-auth-vendor-package",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Securili authentication widget for vendor sites (login, signup, and 2FA via EzAuth).",
|
|
5
|
+
"main": "dist/securili-auth-widget.umd.js",
|
|
6
|
+
"module": "dist/securili-auth-widget.es.js",
|
|
7
|
+
"types": "dist/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/types/index.d.ts",
|
|
11
|
+
"import": "./dist/securili-auth-widget.es.js",
|
|
12
|
+
"require": "./dist/securili-auth-widget.umd.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"securili",
|
|
21
|
+
"ezauth",
|
|
22
|
+
"otp",
|
|
23
|
+
"2fa",
|
|
24
|
+
"plugin",
|
|
25
|
+
"login",
|
|
26
|
+
"vendor",
|
|
27
|
+
"react"
|
|
28
|
+
],
|
|
29
|
+
"author": "Securili Team",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"type": "module",
|
|
32
|
+
"scripts": {
|
|
33
|
+
"clean": "rimraf dist",
|
|
34
|
+
"build": "vite build",
|
|
35
|
+
"build:watch": "vite build --watch",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"prepublishOnly": "npm run clean && npm run typecheck && npm run build"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
41
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/react": "^19.2.7",
|
|
45
|
+
"@types/react-dom": "^19.2.0",
|
|
46
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
47
|
+
"react": "^18.2.0",
|
|
48
|
+
"react-dom": "^18.2.0",
|
|
49
|
+
"rimraf": "^6.0.1",
|
|
50
|
+
"typescript": "^5.3.0",
|
|
51
|
+
"vite": "^7.2.6",
|
|
52
|
+
"vite-plugin-dts": "^4.5.3"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
}
|
|
57
|
+
}
|