@explita/cloud-auth-client 0.0.1 → 0.0.2
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 +25 -2
- package/dist/components/change-password.js +13 -11
- package/dist/components/login-form.js +3 -4
- package/dist/components/optional-otp.js +2 -2
- package/dist/components/reset-password.js +4 -5
- package/dist/components/settings.js +4 -1
- package/dist/components/signup-form.js +20 -21
- package/dist/components/toggle-2fa.js +3 -3
- package/dist/components/toggle-account-status.js +18 -11
- package/dist/components/user-card.js +5 -5
- package/dist/contexts/auth-provider.js +22 -17
- package/dist/hooks/use-token-refresher.js +11 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/lib/api-client.js +1 -1
- package/dist/lib/api-server.js +3 -4
- package/dist/lib/constants.d.ts +9 -0
- package/dist/lib/constants.js +12 -0
- package/dist/lib/error.js +1 -1
- package/dist/lib/refresh-helper.d.ts +0 -6
- package/dist/lib/refresh-helper.js +12 -22
- package/dist/lib/utils.d.ts +3 -1
- package/dist/lib/utils.js +19 -14
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/{cookie.js → next-cookie-override.js} +3 -3
- package/dist/server/role.d.ts +2 -4
- package/dist/server/role.js +3 -1
- package/dist/server/server-token.js +5 -5
- package/dist/server/users-accounts.d.ts +2 -4
- package/dist/server/users-accounts.js +4 -2
- package/dist/types.d.ts +6 -73
- package/package.json +9 -9
- /package/dist/server/{cookie.d.ts → next-cookie-override.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -78,7 +78,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
|
|
78
78
|
loginPath: "/login", //default is /login
|
|
79
79
|
signupPath: "/signup", //default is /signup
|
|
80
80
|
resetPasswordPath: "/reset-password", //default is /reset-password
|
|
81
|
-
cookieOverride: cookieOverrideHandler, //included in the server-side functions and only needed if your app
|
|
81
|
+
cookieOverride: cookieOverrideHandler, //included in the server-side functions and only needed if your app needs to use anything from @explita/cloud-auth-client/server, e.g: getServerSession() - only in Next.js
|
|
82
82
|
disableLoading: true, //default is false
|
|
83
83
|
excludedPaths: [], //array of strings of relative paths, all the paths mentioned above are already excluded.
|
|
84
84
|
}}
|
|
@@ -165,7 +165,7 @@ LoggedOut; //check if user is logged out
|
|
|
165
165
|
```typescript
|
|
166
166
|
"use server";
|
|
167
167
|
|
|
168
|
-
import {
|
|
168
|
+
import type {
|
|
169
169
|
NewRole,
|
|
170
170
|
ResetPasswordWithToken,
|
|
171
171
|
ResetPasswordWithUserId,
|
|
@@ -227,6 +227,29 @@ export async function assignPermissions(roleId: string, permissions: string[]) {
|
|
|
227
227
|
|
|
228
228
|
---
|
|
229
229
|
|
|
230
|
+
### Using Node/Express Backend
|
|
231
|
+
|
|
232
|
+
If you're using a Node.js/Express backend and need to authenticate requests between your frontend and backend, use [cloud-auth-express](https://www.npmjs.com/package/@explita/cloud-auth-express) to verify tokens server-side.
|
|
233
|
+
|
|
234
|
+
Make sure your frontend sends the token along in each request:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { getToken } from "@explita/cloud-auth-client";
|
|
238
|
+
|
|
239
|
+
const response = await fetch("/api/your-route", {
|
|
240
|
+
method: "POST",
|
|
241
|
+
headers: {
|
|
242
|
+
Authorization: `Bearer ${getToken()}`,
|
|
243
|
+
},
|
|
244
|
+
body: JSON.stringify(data),
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
> ⚠️ **Note:** This section only applies if you're doing separate frontend/backend communication.
|
|
249
|
+
> If you're building a fullstack app (e.g. with Next.js using Server Actions or API routes), you don't need to handle this manually — it's already managed internally by cloud-auth-client.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
230
253
|
### Getting started
|
|
231
254
|
|
|
232
255
|
Sign up for an account at [Explita Cloud](https://cloud.explita.ng), create a project, add auth service and get your API key from the dashboard and add it to your environment variables.
|
|
@@ -44,7 +44,6 @@ const message_1 = require("./message");
|
|
|
44
44
|
const must_login_1 = require("./must-login");
|
|
45
45
|
const utils_1 = require("../lib/utils");
|
|
46
46
|
function ChangePassword({ user, open, onOpenChange, onSubmit, onSuccess, }) {
|
|
47
|
-
var _a, _b;
|
|
48
47
|
const { user: loggedInUser } = (0, auth_provider_1.useAuth)();
|
|
49
48
|
const [state, handleSubmit, isPending] = (0, react_1.useActionState)(async function (prevState, formData) {
|
|
50
49
|
if (!onSubmit || typeof onSubmit !== "function") {
|
|
@@ -55,11 +54,11 @@ function ChangePassword({ user, open, onOpenChange, onSubmit, onSuccess, }) {
|
|
|
55
54
|
}
|
|
56
55
|
try {
|
|
57
56
|
const result = await onSubmit({
|
|
58
|
-
userId:
|
|
57
|
+
userId: user?.id || loggedInUser.id,
|
|
59
58
|
password: formData.get("password"),
|
|
60
59
|
confirmPassword: formData.get("confirmPassword"),
|
|
61
60
|
});
|
|
62
|
-
onSuccess
|
|
61
|
+
onSuccess?.();
|
|
63
62
|
return result;
|
|
64
63
|
}
|
|
65
64
|
catch (error) {
|
|
@@ -70,7 +69,10 @@ function ChangePassword({ user, open, onOpenChange, onSubmit, onSuccess, }) {
|
|
|
70
69
|
onOpenChange(state);
|
|
71
70
|
(0, utils_1.unstuckPointerEvents)();
|
|
72
71
|
} },
|
|
73
|
-
react_1.default.createElement(dialog_1.DialogContent, null, !loggedInUser ? (react_1.default.createElement(must_login_1.MustLogin, { onOpenChange:
|
|
72
|
+
react_1.default.createElement(dialog_1.DialogContent, null, !loggedInUser ? (react_1.default.createElement(must_login_1.MustLogin, { onOpenChange: (state) => {
|
|
73
|
+
onOpenChange(state);
|
|
74
|
+
(0, utils_1.unstuckPointerEvents)();
|
|
75
|
+
} })) : (react_1.default.createElement("form", { action: handleSubmit },
|
|
74
76
|
react_1.default.createElement(dialog_1.DialogHeader, null,
|
|
75
77
|
!user || loggedInUser.id === user.id ? (react_1.default.createElement(dialog_1.DialogTitle, null, "Change your password")) : (react_1.default.createElement(dialog_1.DialogTitle, null,
|
|
76
78
|
"Change password for ",
|
|
@@ -78,22 +80,22 @@ function ChangePassword({ user, open, onOpenChange, onSubmit, onSuccess, }) {
|
|
|
78
80
|
" ",
|
|
79
81
|
user.lastName)),
|
|
80
82
|
react_1.default.createElement(dialog_1.DialogDescription, null, "Please enter the new password"),
|
|
81
|
-
user && loggedInUser.id !==
|
|
83
|
+
user && loggedInUser.id !== user?.id && (react_1.default.createElement(dialog_1.DialogDescription, { className: "ecpauth:mt-2 ecpauth:text-base" },
|
|
82
84
|
"Username: ",
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
user?.username || user?.email))),
|
|
86
|
+
state?.status === "success" ? (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mt-2 ecpauth:font-semibold ecpauth:text-base", variant: "success" })) : (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mt-2" })),
|
|
85
87
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-6 ecpauth:my-5" },
|
|
86
88
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
87
89
|
react_1.default.createElement(label_1.Label, { htmlFor: "password" }, "New Password"),
|
|
88
90
|
react_1.default.createElement(input_1.Input, { id: "password", type: "password", name: "password" }),
|
|
89
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
90
|
-
?
|
|
91
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
92
|
+
? state?.errors?.password
|
|
91
93
|
: "" })),
|
|
92
94
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
93
95
|
react_1.default.createElement(label_1.Label, { htmlFor: "confirmPassword" }, "Confirm New Password"),
|
|
94
96
|
react_1.default.createElement(input_1.Input, { id: "confirmPassword", type: "password", name: "confirmPassword" }),
|
|
95
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
96
|
-
?
|
|
97
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
98
|
+
? state?.errors?.confirmPassword
|
|
97
99
|
: "" }))),
|
|
98
100
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:justify-end ecpauth:gap-2" },
|
|
99
101
|
react_1.default.createElement(button_1.Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false) }, "Cancel"),
|
|
@@ -45,7 +45,6 @@ const auth_provider_1 = require("../contexts/auth-provider");
|
|
|
45
45
|
const loader_1 = require("./loader");
|
|
46
46
|
const message_1 = require("./message");
|
|
47
47
|
function Login({ className, ...props }) {
|
|
48
|
-
var _a, _b;
|
|
49
48
|
const { login, error, computedRouteContext, isAuthenticated } = (0, auth_provider_1.useAuth)();
|
|
50
49
|
const [authToken, setAuthToken] = react_1.default.useState(null);
|
|
51
50
|
const { resetPasswordUrl, signupUrl, loginUrl } = computedRouteContext;
|
|
@@ -86,14 +85,14 @@ function Login({ className, ...props }) {
|
|
|
86
85
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-6" },
|
|
87
86
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
88
87
|
react_1.default.createElement(label_1.Label, { htmlFor: "email" }, "Email/Username"),
|
|
89
|
-
react_1.default.createElement(input_1.Input, { id: "email", type: "text", placeholder: "email@example.com", name: "email", defaultValue: state
|
|
90
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
88
|
+
react_1.default.createElement(input_1.Input, { id: "email", type: "text", placeholder: "email@example.com", name: "email", defaultValue: state?.email }),
|
|
89
|
+
react_1.default.createElement(message_1.Message, { message: error?.errors?.email })),
|
|
91
90
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
92
91
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:items-center" },
|
|
93
92
|
react_1.default.createElement(label_1.Label, { htmlFor: "password" }, "Password"),
|
|
94
93
|
resetPasswordUrl && (react_1.default.createElement("a", { href: resetPasswordUrl, className: "ecpauth:ml-auto ecpauth:inline-block ecpauth:text-sm ecpauth:underline-offset-4 ecpauth:hover:underline" }, "Forgot your password?"))),
|
|
95
94
|
react_1.default.createElement(input_1.Input, { id: "password", type: "password", name: "password" }),
|
|
96
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
95
|
+
react_1.default.createElement(message_1.Message, { message: error?.errors?.password })),
|
|
97
96
|
react_1.default.createElement(button_1.Button, { type: "submit", className: "ecpauth:w-full ecpauth:cursor-pointer", disabled: isPending }, isPending ? "Logging in..." : "Login")),
|
|
98
97
|
signupUrl && (react_1.default.createElement("div", { className: "ecpauth:mt-4 ecpauth:text-center ecpauth:text-sm" },
|
|
99
98
|
"Don't have an account?",
|
|
@@ -55,10 +55,10 @@ function OptionalOTPInner({ user, onVerified, onCanceled }) {
|
|
|
55
55
|
return null; // or a fallback UI
|
|
56
56
|
}
|
|
57
57
|
const otp = useOTP({
|
|
58
|
-
referenceId: user
|
|
58
|
+
referenceId: user?.id,
|
|
59
59
|
onVerified,
|
|
60
60
|
onCanceled,
|
|
61
|
-
sentTo: user
|
|
61
|
+
sentTo: user?.email,
|
|
62
62
|
channel: "email",
|
|
63
63
|
});
|
|
64
64
|
(0, react_1.useEffect)(() => {
|
|
@@ -44,7 +44,6 @@ const label_1 = require("./ui/label");
|
|
|
44
44
|
const message_1 = require("./message");
|
|
45
45
|
const auth_provider_1 = require("../contexts/auth-provider");
|
|
46
46
|
function ResetPassword({ className, onChangePassword, ...props }) {
|
|
47
|
-
var _a;
|
|
48
47
|
const { sendPasswordResetRequest, computedRouteContext } = (0, auth_provider_1.useAuth)();
|
|
49
48
|
const [token, setToken] = (0, react_1.useState)(null);
|
|
50
49
|
const { loginUrl } = computedRouteContext;
|
|
@@ -77,7 +76,7 @@ function ResetPassword({ className, onChangePassword, ...props }) {
|
|
|
77
76
|
}, undefined);
|
|
78
77
|
(0, react_1.useEffect)(() => {
|
|
79
78
|
let timer;
|
|
80
|
-
if (
|
|
79
|
+
if (state?.status === "success") {
|
|
81
80
|
if (loginUrl) {
|
|
82
81
|
timer = setTimeout(() => {
|
|
83
82
|
window.location.href = loginUrl;
|
|
@@ -87,21 +86,21 @@ function ResetPassword({ className, onChangePassword, ...props }) {
|
|
|
87
86
|
return () => {
|
|
88
87
|
clearTimeout(timer);
|
|
89
88
|
};
|
|
90
|
-
}, [state
|
|
89
|
+
}, [state?.status]);
|
|
91
90
|
return (react_1.default.createElement("div", { className: (0, utils_1.cn)("ecpauth:flex ecpauth:flex-col ecpauth:gap-6 ecpauth:w-full ecpauth:max-w-sm", className), ...props },
|
|
92
91
|
react_1.default.createElement(card_1.Card, null,
|
|
93
92
|
react_1.default.createElement(card_1.CardHeader, null,
|
|
94
93
|
react_1.default.createElement(card_1.CardTitle, { className: "ecpauth:text-2xl" }, "Reset Password"),
|
|
95
94
|
react_1.default.createElement(card_1.CardDescription, null, "Provide your details to reset your password.")),
|
|
96
95
|
react_1.default.createElement(card_1.CardContent, null,
|
|
97
|
-
|
|
96
|
+
state?.status === "success" ? (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mb-4 ecpauth:font-semibold ecpauth:text-base", variant: "success" })) : (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mb-4" })),
|
|
98
97
|
!token ? (react_1.default.createElement("form", { action: handleSubmit },
|
|
99
98
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-6" },
|
|
100
99
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
101
100
|
react_1.default.createElement(label_1.Label, { htmlFor: "email" }, "Email"),
|
|
102
101
|
react_1.default.createElement(input_1.Input, { id: "email", type: "email", placeholder: "email@example.com", name: "email", defaultValue:
|
|
103
102
|
//@ts-ignore
|
|
104
|
-
|
|
103
|
+
state?.form?.email })),
|
|
105
104
|
react_1.default.createElement(button_1.Button, { type: "submit", className: "ecpauth:w-full ecpauth:cursor-pointer", disabled: isPending }, "Send Reset Password Link")))) : (react_1.default.createElement("form", { action: handleSubmit },
|
|
106
105
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-6" },
|
|
107
106
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
@@ -16,7 +16,10 @@ function Settings({ open, onOpenChange, onToggle2FA }) {
|
|
|
16
16
|
onOpenChange(state);
|
|
17
17
|
(0, utils_1.unstuckPointerEvents)();
|
|
18
18
|
} },
|
|
19
|
-
react_1.default.createElement(dialog_1.DialogContent, null, !user ? (react_1.default.createElement(must_login_1.MustLogin, { onOpenChange:
|
|
19
|
+
react_1.default.createElement(dialog_1.DialogContent, null, !user ? (react_1.default.createElement(must_login_1.MustLogin, { onOpenChange: (state) => {
|
|
20
|
+
onOpenChange(state);
|
|
21
|
+
(0, utils_1.unstuckPointerEvents)();
|
|
22
|
+
} })) : (react_1.default.createElement("div", { className: "ecpauth:relative" },
|
|
20
23
|
react_1.default.createElement(dialog_1.DialogHeader, { className: "ecpauth:border-b ecpauth:border-gray-200 ecpauth:dark:border-gray-800 ecpauth:pb-2" },
|
|
21
24
|
react_1.default.createElement(dialog_1.DialogTitle, { className: "ecpauth:flex ecpauth:items-center ecpauth:justify-between" },
|
|
22
25
|
react_1.default.createElement("span", { className: "ecpauth:text-stone-800 ecpauth:dark:text-stone-50" }, "Account Settings")),
|
|
@@ -44,7 +44,6 @@ const label_1 = require("./ui/label");
|
|
|
44
44
|
const message_1 = require("./message");
|
|
45
45
|
const auth_provider_1 = require("../contexts/auth-provider");
|
|
46
46
|
function Signup({ className, onSubmit, groupId, metaData, acceptUsername = true, ...props }) {
|
|
47
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
48
47
|
const { computedRouteContext } = (0, auth_provider_1.useAuth)();
|
|
49
48
|
const { loginUrl } = computedRouteContext;
|
|
50
49
|
const [state, handleSubmit, isPending] = (0, react_1.useActionState)(async function (prevState, formData) {
|
|
@@ -76,59 +75,59 @@ function Signup({ className, onSubmit, groupId, metaData, acceptUsername = true,
|
|
|
76
75
|
}, undefined);
|
|
77
76
|
(0, react_1.useEffect)(() => {
|
|
78
77
|
let timer;
|
|
79
|
-
if (
|
|
78
|
+
if (state?.status === "success" && loginUrl) {
|
|
80
79
|
timer = setTimeout(() => {
|
|
81
|
-
window.location.href = `${loginUrl}?authToken=${state
|
|
80
|
+
window.location.href = `${loginUrl}?authToken=${state?.authToken}`;
|
|
82
81
|
}, 2000);
|
|
83
82
|
}
|
|
84
83
|
return () => {
|
|
85
84
|
clearTimeout(timer);
|
|
86
85
|
};
|
|
87
|
-
}, [state
|
|
86
|
+
}, [state?.status, loginUrl]);
|
|
88
87
|
return (react_1.default.createElement("div", { className: (0, utils_1.cn)("ecpauth:flex ecpauth:flex-col ecpauth:gap-6 ecpauth:w-full ecpauth:max-w-sm", className), ...props },
|
|
89
88
|
react_1.default.createElement(card_1.Card, null,
|
|
90
89
|
react_1.default.createElement(card_1.CardHeader, null,
|
|
91
90
|
react_1.default.createElement(card_1.CardTitle, { className: "ecpauth:text-2xl" }, "Sign Up"),
|
|
92
91
|
react_1.default.createElement(card_1.CardDescription, null, "Provide your details to sign up from your account.")),
|
|
93
92
|
react_1.default.createElement(card_1.CardContent, null,
|
|
94
|
-
|
|
93
|
+
state?.status === "success" ? (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mb-4 ecpauth:font-semibold ecpauth:text-lg", variant: "success" })) : (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mb-4" })),
|
|
95
94
|
react_1.default.createElement("form", { action: handleSubmit },
|
|
96
95
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-6" },
|
|
97
96
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
98
97
|
react_1.default.createElement(label_1.Label, { htmlFor: "firstName" }, "First Name"),
|
|
99
|
-
react_1.default.createElement(input_1.Input, { id: "firstName", type: "text", placeholder: "First Name", name: "firstName", defaultValue:
|
|
100
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
101
|
-
?
|
|
98
|
+
react_1.default.createElement(input_1.Input, { id: "firstName", type: "text", placeholder: "First Name", name: "firstName", defaultValue: state?.form?.firstName }),
|
|
99
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
100
|
+
? state?.errors?.firstName
|
|
102
101
|
: "" })),
|
|
103
102
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
104
103
|
react_1.default.createElement(label_1.Label, { htmlFor: "lastName" }, "Last Name"),
|
|
105
|
-
react_1.default.createElement(input_1.Input, { id: "lastName", type: "text", placeholder: "Last Name", name: "lastName", defaultValue:
|
|
106
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
107
|
-
?
|
|
104
|
+
react_1.default.createElement(input_1.Input, { id: "lastName", type: "text", placeholder: "Last Name", name: "lastName", defaultValue: state?.form?.lastName }),
|
|
105
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
106
|
+
? state?.errors?.lastName
|
|
108
107
|
: "" })),
|
|
109
108
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
110
109
|
react_1.default.createElement(label_1.Label, { htmlFor: "email" }, "Email"),
|
|
111
|
-
react_1.default.createElement(input_1.Input, { id: "email", type: "email", placeholder: "email@example.com", name: "email", defaultValue:
|
|
112
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
113
|
-
?
|
|
110
|
+
react_1.default.createElement(input_1.Input, { id: "email", type: "email", placeholder: "email@example.com", name: "email", defaultValue: state?.form?.email }),
|
|
111
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
112
|
+
? state?.errors?.email
|
|
114
113
|
: "" })),
|
|
115
114
|
acceptUsername && (react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
116
115
|
react_1.default.createElement(label_1.Label, { htmlFor: "username" }, "Username"),
|
|
117
|
-
react_1.default.createElement(input_1.Input, { id: "username", type: "text", placeholder: "Username", name: "username", defaultValue:
|
|
118
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
119
|
-
?
|
|
116
|
+
react_1.default.createElement(input_1.Input, { id: "username", type: "text", placeholder: "Username", name: "username", defaultValue: state?.form?.username }),
|
|
117
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
118
|
+
? state?.errors?.username
|
|
120
119
|
: "" }))),
|
|
121
120
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
122
121
|
react_1.default.createElement(label_1.Label, { htmlFor: "password" }, "Password"),
|
|
123
122
|
react_1.default.createElement(input_1.Input, { id: "password", type: "password", name: "password" }),
|
|
124
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
125
|
-
?
|
|
123
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
124
|
+
? state?.errors?.password
|
|
126
125
|
: "" })),
|
|
127
126
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
128
127
|
react_1.default.createElement(label_1.Label, { htmlFor: "confirmPassword" }, "Confirm Password"),
|
|
129
128
|
react_1.default.createElement(input_1.Input, { id: "confirmPassword", type: "password", name: "confirmPassword" }),
|
|
130
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
131
|
-
?
|
|
129
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
130
|
+
? state?.errors?.confirmPassword
|
|
132
131
|
: "" })),
|
|
133
132
|
react_1.default.createElement(button_1.Button, { type: "submit", className: "ecpauth:w-full ecpauth:cursor-pointer", disabled: isPending }, isPending ? "Signing up..." : "Sign Up")),
|
|
134
133
|
loginUrl && (react_1.default.createElement("div", { className: "ecpauth:mt-4 ecpauth:text-center ecpauth:text-sm" },
|
|
@@ -64,15 +64,15 @@ function Toggle2FA({ onSubmit }) {
|
|
|
64
64
|
return (react_1.default.createElement("div", { className: "ecpauth:text-stone-800 ecpauth:dark:text-stone-50" },
|
|
65
65
|
isPending && react_1.default.createElement(loader_1.Loader, null),
|
|
66
66
|
react_1.default.createElement("p", { className: "ecpauth:font-semibold ecpauth:text-lg ecpauth:mb-2" }, "Toggle Two Factor Authentication"),
|
|
67
|
-
|
|
67
|
+
state?.status === "success" ? (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:my-2 ecpauth:font-semibold ecpauth:text-base", variant: "success" })) : (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:my-2" })),
|
|
68
68
|
react_1.default.createElement("p", null,
|
|
69
69
|
"This action will enable or disable 2FA on your account. If enabled, you\u2019ll be asked for a code sent to ",
|
|
70
|
-
react_1.default.createElement("strong", null, user
|
|
70
|
+
react_1.default.createElement("strong", null, user?.email),
|
|
71
71
|
" during login."),
|
|
72
72
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:items-center ecpauth:justify-between ecpauth:py-3 ecpauth:my-3 ecpauth:border-y ecpauth:border-gray-200 ecpauth:dark:border-gray-800" },
|
|
73
73
|
react_1.default.createElement("p", { className: "ecpauth:flex-1 ecpauth:mb-0" }, "You can enable or disable your Two Factor Authentication"),
|
|
74
74
|
react_1.default.createElement("div", { className: "ecpauth:w-40 ecpauth:flex ecpauth:justify-end ecpauth:pe-3" },
|
|
75
|
-
react_1.default.createElement(switch_1.Switch, { checked: !!
|
|
75
|
+
react_1.default.createElement(switch_1.Switch, { checked: !!user?.with2fa, onCheckedChange: () => startTransition(handleSubmit), disabled: !canUseOtp }))),
|
|
76
76
|
!canUseOtp && (react_1.default.createElement("p", { className: "ecpauth:text-red-500 ecpauth:my-2 ecpauth:text-sm" },
|
|
77
77
|
"2FA is not available. Please install",
|
|
78
78
|
" ",
|
|
@@ -40,6 +40,7 @@ const button_1 = require("./ui/button");
|
|
|
40
40
|
const auth_provider_1 = require("../contexts/auth-provider");
|
|
41
41
|
const message_1 = require("./message");
|
|
42
42
|
const must_login_1 = require("./must-login");
|
|
43
|
+
const utils_1 = require("../lib/utils");
|
|
43
44
|
function ToggleAccountStatus({ user, open, onOpenChange, onSuccess, onSubmit, }) {
|
|
44
45
|
const { user: loggedInUser } = (0, auth_provider_1.useAuth)();
|
|
45
46
|
const [state, handleSubmit, isPending] = (0, react_1.useActionState)(async function () {
|
|
@@ -57,35 +58,41 @@ function ToggleAccountStatus({ user, open, onOpenChange, onSuccess, onSubmit, })
|
|
|
57
58
|
}
|
|
58
59
|
try {
|
|
59
60
|
const result = await onSubmit(user.id);
|
|
60
|
-
onSuccess
|
|
61
|
+
onSuccess?.();
|
|
61
62
|
return result;
|
|
62
63
|
}
|
|
63
64
|
catch (error) {
|
|
64
65
|
console.log(error);
|
|
65
66
|
}
|
|
66
67
|
}, undefined);
|
|
67
|
-
return (react_1.default.createElement(dialog_1.Dialog, { open: open, onOpenChange:
|
|
68
|
-
|
|
68
|
+
return (react_1.default.createElement(dialog_1.Dialog, { open: open, onOpenChange: (state) => {
|
|
69
|
+
onOpenChange(state);
|
|
70
|
+
(0, utils_1.unstuckPointerEvents)();
|
|
71
|
+
} },
|
|
72
|
+
react_1.default.createElement(dialog_1.DialogContent, null, !loggedInUser ? (react_1.default.createElement(must_login_1.MustLogin, { onOpenChange: (state) => {
|
|
73
|
+
onOpenChange(state);
|
|
74
|
+
(0, utils_1.unstuckPointerEvents)();
|
|
75
|
+
} })) : (react_1.default.createElement("form", { action: handleSubmit },
|
|
69
76
|
react_1.default.createElement(dialog_1.DialogHeader, null,
|
|
70
77
|
react_1.default.createElement(dialog_1.DialogTitle, null,
|
|
71
|
-
"Toggle account status (",
|
|
72
|
-
user
|
|
73
|
-
" ",
|
|
74
|
-
user
|
|
78
|
+
"Toggle account status (",
|
|
79
|
+
user?.firstName,
|
|
80
|
+
" ",
|
|
81
|
+
user?.lastName,
|
|
75
82
|
")"),
|
|
76
|
-
|
|
83
|
+
state?.status === "success" ? (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:my-2 ecpauth:font-semibold ecpauth:text-base", variant: "success" })) : (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:my-2" }))),
|
|
77
84
|
react_1.default.createElement(dialog_1.DialogDescription, { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-2 ecpauth:text-gray-700 ecpauth:dark:text-gray-50 ecpauth:mt-4" },
|
|
78
85
|
react_1.default.createElement("span", null,
|
|
79
86
|
"This action will",
|
|
80
87
|
" ",
|
|
81
|
-
react_1.default.createElement("strong", null,
|
|
88
|
+
react_1.default.createElement("strong", null, user?.isActive ? "suspend" : "unsuspend"),
|
|
82
89
|
" the user."),
|
|
83
90
|
react_1.default.createElement("span", null,
|
|
84
91
|
"You can always",
|
|
85
92
|
" ",
|
|
86
|
-
react_1.default.createElement("strong", null,
|
|
93
|
+
react_1.default.createElement("strong", null, user?.isActive ? "unsuspend" : "suspend"),
|
|
87
94
|
" the user if you change your mind.")),
|
|
88
95
|
react_1.default.createElement(dialog_1.DialogFooter, { className: "ecpauth:flex ecpauth:justify-center ecpauth:gap-2 ecpauth:mt-4" },
|
|
89
96
|
react_1.default.createElement(button_1.Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false) }, "Cancel"),
|
|
90
|
-
react_1.default.createElement(button_1.Button, { disabled: isPending, type: "submit" },
|
|
97
|
+
react_1.default.createElement(button_1.Button, { disabled: isPending, type: "submit" }, user?.isActive ? "Suspend" : "Unsuspend")))))));
|
|
91
98
|
}
|
|
@@ -59,9 +59,9 @@ function Trigger() {
|
|
|
59
59
|
return (react_1.default.createElement(dropdown_menu_1.DropdownMenuTrigger, { className: "ecpauth:flex ecpauth:items-center ecpauth:gap-2 ecpauth:p-1 ecpauth:pe-4 ecpauth:rounded-lg ecpauth:border ecpauth:border-gray-200 ecpauth:dark:border-gray-800 ecpauth:cursor-pointer ecpauth:bg-white ecpauth:hover:bg-gray-100 ecpauth:dark:bg-gray-800 ecpauth:dark:hover:bg-gray-800/80 ecpauth:dark:hover:text-white ecpauth:outline-none ecpauth:focus:outline-none" }, authLoading ? ("Please wait...") : (react_1.default.createElement(react_1.default.Fragment, null,
|
|
60
60
|
react_1.default.createElement("div", { className: "ecpauth:size-12 ecpauth:rounded-full ecpauth:bg-gray-200" }),
|
|
61
61
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:text-left" },
|
|
62
|
-
react_1.default.createElement("span", { className: "ecpauth:font-semibold ecpauth:text-stone-800 ecpauth:dark:text-stone-50" },
|
|
63
|
-
user
|
|
64
|
-
" ",
|
|
65
|
-
user
|
|
66
|
-
react_1.default.createElement("span", { className: "ecpauth:text-sm ecpauth:text-gray-500" }, user
|
|
62
|
+
react_1.default.createElement("span", { className: "ecpauth:font-semibold ecpauth:text-stone-800 ecpauth:dark:text-stone-50" },
|
|
63
|
+
user?.firstName,
|
|
64
|
+
" ",
|
|
65
|
+
user?.lastName),
|
|
66
|
+
react_1.default.createElement("span", { className: "ecpauth:text-sm ecpauth:text-gray-500" }, user?.email))))));
|
|
67
67
|
}
|
|
@@ -42,7 +42,7 @@ const loader_1 = require("../components/loader");
|
|
|
42
42
|
const error_1 = require("../lib/error");
|
|
43
43
|
const use_token_refresher_1 = require("../hooks/use-token-refresher");
|
|
44
44
|
const optional_otp_wrapper_1 = require("../components/optional-otp-wrapper");
|
|
45
|
-
const
|
|
45
|
+
const constants_1 = require("../lib/constants");
|
|
46
46
|
const AuthContext = (0, react_1.createContext)(undefined);
|
|
47
47
|
function AuthProvider({ children, config }) {
|
|
48
48
|
const [twoFAData, setTwoFAData] = (0, react_1.useState)(null);
|
|
@@ -53,12 +53,11 @@ function AuthProvider({ children, config }) {
|
|
|
53
53
|
const [transition, startTransition] = (0, react_1.useTransition)();
|
|
54
54
|
const withFullObject = (0, utils_1.shouldPassFullUserObject)();
|
|
55
55
|
const computedRouteContext = (0, utils_1.buildRouteContext)(config);
|
|
56
|
-
const { loginUrl, resetPasswordUrl, dashboardUrl,
|
|
56
|
+
const { loginUrl, resetPasswordUrl, dashboardUrl, returnPathRaw, isExcluded, } = computedRouteContext;
|
|
57
57
|
const { disableLoading = false } = config || {};
|
|
58
58
|
async function redirectAfterLogin(authToken, tempRefreshToken) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
localStorage.setItem(refresh_helper_1.AUTH_TOKEN_KEY, authToken);
|
|
59
|
+
await config?.cookieOverride?.(tempRefreshToken);
|
|
60
|
+
localStorage.setItem(constants_1.AUTH_TOKEN_KEY, authToken);
|
|
62
61
|
const returnPath = new URLSearchParams(window.location.search).get("returnPath");
|
|
63
62
|
window.location.href = returnPath ? returnPath : dashboardUrl;
|
|
64
63
|
}
|
|
@@ -72,7 +71,7 @@ function AuthProvider({ children, config }) {
|
|
|
72
71
|
if (!navigator.onLine) {
|
|
73
72
|
try {
|
|
74
73
|
const payload = (0, utils_1.parseJwt)(token);
|
|
75
|
-
setUser(payload
|
|
74
|
+
setUser(payload?.user);
|
|
76
75
|
setIsAuthenticated(true);
|
|
77
76
|
}
|
|
78
77
|
catch (e) {
|
|
@@ -143,24 +142,30 @@ function AuthProvider({ children, config }) {
|
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
144
|
async function logout(reason = "", params) {
|
|
146
|
-
|
|
145
|
+
let logoutSucceeded = false;
|
|
147
146
|
try {
|
|
148
147
|
await (0, api_1.apiFactory)("/logout");
|
|
148
|
+
logoutSucceeded = true;
|
|
149
149
|
}
|
|
150
150
|
catch (err) {
|
|
151
|
-
|
|
151
|
+
if ([401, 403].includes(err.status)) {
|
|
152
|
+
logoutSucceeded = true;
|
|
153
|
+
}
|
|
154
|
+
console.error("Logout failed:", err);
|
|
152
155
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
localStorage.removeItem(
|
|
156
|
-
localStorage.removeItem(
|
|
157
|
-
|
|
156
|
+
const forceLogout = reason === "user_logout";
|
|
157
|
+
if (logoutSucceeded || forceLogout) {
|
|
158
|
+
localStorage.removeItem(constants_1.AUTH_TOKEN_KEY);
|
|
159
|
+
localStorage.removeItem(constants_1.LEADING_TAB_KEY);
|
|
160
|
+
localStorage.removeItem(constants_1.AUTH_RETRY_STATE_KEY);
|
|
161
|
+
await config?.cookieOverride?.("");
|
|
158
162
|
const fullParams = new URLSearchParams({
|
|
163
|
+
returnPath: returnPathRaw,
|
|
159
164
|
reason,
|
|
160
165
|
...params,
|
|
161
166
|
}).toString();
|
|
162
167
|
if (typeof window !== "undefined") {
|
|
163
|
-
window.location.href = `${loginUrl}?${
|
|
168
|
+
window.location.href = `${loginUrl}?${fullParams}`;
|
|
164
169
|
}
|
|
165
170
|
}
|
|
166
171
|
}
|
|
@@ -175,9 +180,8 @@ function AuthProvider({ children, config }) {
|
|
|
175
180
|
return res;
|
|
176
181
|
},
|
|
177
182
|
onTokenRefreshed: (authToken, tempRefreshToken) => {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
(_a = config === null || config === void 0 ? void 0 : config.cookieOverride) === null || _a === void 0 ? void 0 : _a.call(config, tempRefreshToken);
|
|
183
|
+
localStorage.setItem(constants_1.AUTH_TOKEN_KEY, authToken);
|
|
184
|
+
config?.cookieOverride?.(tempRefreshToken);
|
|
181
185
|
},
|
|
182
186
|
revalidateUserWhenOnline: fetchCurrentUser,
|
|
183
187
|
onRefreshFailed: async () => await logout("session_expired"),
|
|
@@ -198,6 +202,7 @@ function AuthProvider({ children, config }) {
|
|
|
198
202
|
logout: (params) => logout("user_logout", params),
|
|
199
203
|
hasPermission: (permission) => (0, utils_1.hasPermission)(user, permission),
|
|
200
204
|
revalidate: () => startTransition(fetchCurrentUser),
|
|
205
|
+
getToken: () => (0, utils_1.getClientToken)(),
|
|
201
206
|
} }, children)));
|
|
202
207
|
}
|
|
203
208
|
function useAuth() {
|
|
@@ -4,6 +4,7 @@ exports.useTokenRefresher = useTokenRefresher;
|
|
|
4
4
|
const utils_1 = require("../lib/utils");
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const refresh_helper_1 = require("../lib/refresh-helper");
|
|
7
|
+
const constants_1 = require("../lib/constants");
|
|
7
8
|
function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefreshed, revalidateUserWhenOnline, config, }) {
|
|
8
9
|
const refreshing = (0, react_1.useRef)(false);
|
|
9
10
|
const interval = (0, react_1.useRef)(null);
|
|
@@ -26,7 +27,7 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
|
|
|
26
27
|
refreshing.current = true;
|
|
27
28
|
const token = (0, utils_1.getClientToken)();
|
|
28
29
|
if (!token && !isExcluded) {
|
|
29
|
-
onRefreshFailed
|
|
30
|
+
onRefreshFailed?.();
|
|
30
31
|
refreshing.current = false;
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
@@ -40,7 +41,7 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
|
|
|
40
41
|
const payload = (0, utils_1.parseJwt)(token);
|
|
41
42
|
const expiresAt = payload.exp * 1000;
|
|
42
43
|
const timeLeft = expiresAt - Date.now();
|
|
43
|
-
if (timeLeft <
|
|
44
|
+
if (timeLeft < constants_1.thresholdMs) {
|
|
44
45
|
shouldRefresh = true;
|
|
45
46
|
}
|
|
46
47
|
else {
|
|
@@ -49,22 +50,22 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
|
|
|
49
50
|
}
|
|
50
51
|
catch (e) {
|
|
51
52
|
console.warn("[ecp-auth] Invalid token format, skipping refresh.");
|
|
52
|
-
onRefreshFailed
|
|
53
|
+
onRefreshFailed?.();
|
|
53
54
|
}
|
|
54
55
|
if (shouldRefresh) {
|
|
55
56
|
const { authToken, tempRefreshToken } = await refreshTokenRequest();
|
|
56
57
|
if (!authToken) {
|
|
57
|
-
onRefreshFailed
|
|
58
|
+
onRefreshFailed?.();
|
|
58
59
|
}
|
|
59
60
|
else {
|
|
60
|
-
onTokenRefreshed
|
|
61
|
+
onTokenRefreshed?.(authToken, tempRefreshToken);
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
catch (err) {
|
|
65
66
|
const success = await (0, refresh_helper_1.tryRefreshWithRetry)(refreshTokenRequest);
|
|
66
67
|
if (!success) {
|
|
67
|
-
onRefreshFailed
|
|
68
|
+
onRefreshFailed?.();
|
|
68
69
|
console.warn("[ecp-auth] All retry attempts failed, triggering logout.");
|
|
69
70
|
}
|
|
70
71
|
}
|
|
@@ -74,7 +75,7 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
|
|
|
74
75
|
}
|
|
75
76
|
(0, react_1.useEffect)(() => {
|
|
76
77
|
refreshIfNeeded(); // run once on mount
|
|
77
|
-
interval.current = setInterval(refreshIfNeeded,
|
|
78
|
+
interval.current = setInterval(refreshIfNeeded, constants_1.intervalMs);
|
|
78
79
|
function onVisible() {
|
|
79
80
|
if (document.visibilityState === "visible") {
|
|
80
81
|
refreshIfNeeded();
|
|
@@ -96,11 +97,10 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
|
|
|
96
97
|
}, []);
|
|
97
98
|
(0, react_1.useEffect)(() => {
|
|
98
99
|
function onStorage(e) {
|
|
99
|
-
if (e.key ===
|
|
100
|
+
if (e.key === constants_1.LEADING_TAB_KEY) {
|
|
100
101
|
// If leader changes, check if we need to act
|
|
101
|
-
// Optionally, you can trigger a refresh of local state here
|
|
102
102
|
}
|
|
103
|
-
if (e.key ===
|
|
103
|
+
if (e.key === constants_1.AUTH_RETRY_STATE_KEY) {
|
|
104
104
|
// Optionally, react to retry state changes
|
|
105
105
|
}
|
|
106
106
|
}
|
|
@@ -112,7 +112,7 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
|
|
|
112
112
|
return;
|
|
113
113
|
const renewInterval = setInterval(() => {
|
|
114
114
|
(0, refresh_helper_1.renewLeadership)();
|
|
115
|
-
},
|
|
115
|
+
}, constants_1.LEADER_TIMEOUT / 2); // Renew halfway before timeout
|
|
116
116
|
return () => clearInterval(renewInterval);
|
|
117
117
|
}, [refresh_helper_1.isLeader]);
|
|
118
118
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * from "./contexts/auth-provider";
|
|
2
2
|
export * from "./components/message";
|
|
3
|
-
export { buildRouteContext } from "./lib/utils";
|
|
4
|
-
export type { AuthConfig,
|
|
3
|
+
export { buildRouteContext, getClientToken as getToken } from "./lib/utils";
|
|
4
|
+
export type { AuthConfig, User, Role, Status, NewRole, UpdateUser, GeneralResponse, FormResponse, VerifyUser, VerifyUserResponse, ResetPasswordWithToken, ResetPasswordWithUserId, Signup, Login, } from "./types";
|
package/dist/index.js
CHANGED
|
@@ -14,8 +14,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.buildRouteContext = void 0;
|
|
17
|
+
exports.getToken = exports.buildRouteContext = void 0;
|
|
18
18
|
__exportStar(require("./contexts/auth-provider"), exports);
|
|
19
19
|
__exportStar(require("./components/message"), exports);
|
|
20
20
|
var utils_1 = require("./lib/utils");
|
|
21
21
|
Object.defineProperty(exports, "buildRouteContext", { enumerable: true, get: function () { return utils_1.buildRouteContext; } });
|
|
22
|
+
Object.defineProperty(exports, "getToken", { enumerable: true, get: function () { return utils_1.getClientToken; } });
|
package/dist/lib/api-client.js
CHANGED
|
@@ -10,7 +10,7 @@ async function apiClient(path, options) {
|
|
|
10
10
|
throw new Error("Invalid API URL or API key. Please set ECP_AUTH_PUBLIC_TOKEN (or the VITE_/NEXT_PUBLIC_ equivalent) in your .env file. Check admin dashboard for env variables.");
|
|
11
11
|
}
|
|
12
12
|
const token = (0, utils_1.getClientToken)(); // from localStorage
|
|
13
|
-
const { method = "POST", body, headers, ...rest } = options
|
|
13
|
+
const { method = "POST", body, headers, ...rest } = options ?? {};
|
|
14
14
|
const res = await fetch(`${apiUrl}${path}`, {
|
|
15
15
|
method,
|
|
16
16
|
body: body ? JSON.stringify(body) : undefined,
|
package/dist/lib/api-server.js
CHANGED
|
@@ -6,13 +6,12 @@ const error_1 = require("./error");
|
|
|
6
6
|
const server_token_1 = require("../server/server-token");
|
|
7
7
|
// SERVER: secure-only calls using private key
|
|
8
8
|
async function apiServer(apiPath, options) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if ((!apiUrl || !apiKey) && !(options === null || options === void 0 ? void 0 : options.adminCall)) {
|
|
9
|
+
let [apiUrl, apiKey] = atob(process.env.ECP_AUTH_PRIVATE_TOKEN ?? "").split("$");
|
|
10
|
+
if ((!apiUrl || !apiKey) && !options?.adminCall) {
|
|
12
11
|
throw new Error("Invalid API URL or API key, please set ECP_AUTH_PRIVATE_TOKEN in your .env file. Check admin dashboard for env variables.");
|
|
13
12
|
}
|
|
14
13
|
let token = await (0, server_token_1.getServerToken)();
|
|
15
|
-
const { method = "POST", body, headers, adminCall, ...rest } = options
|
|
14
|
+
const { method = "POST", body, headers, adminCall, ...rest } = options ?? {};
|
|
16
15
|
const res = await fetch(`${apiUrl}${apiPath}`, {
|
|
17
16
|
method,
|
|
18
17
|
body: body ? JSON.stringify(body) : undefined,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const AUTH_TOKEN_KEY = "_ecp_auth_token";
|
|
2
|
+
export declare const AUTH_RETRY_STATE_KEY = "_ecp_auth_retry_state";
|
|
3
|
+
export declare const LEADING_TAB_KEY = "_ecp_leading_tab";
|
|
4
|
+
export declare const LEADER_TIMEOUT: number;
|
|
5
|
+
export declare const thresholdMs: number;
|
|
6
|
+
export declare const intervalMs: number;
|
|
7
|
+
export declare const retryDelayMs = 60000;
|
|
8
|
+
export declare const maxRetries = 5;
|
|
9
|
+
export declare const backoffFactor = 2;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.backoffFactor = exports.maxRetries = exports.retryDelayMs = exports.intervalMs = exports.thresholdMs = exports.LEADER_TIMEOUT = exports.LEADING_TAB_KEY = exports.AUTH_RETRY_STATE_KEY = exports.AUTH_TOKEN_KEY = void 0;
|
|
4
|
+
exports.AUTH_TOKEN_KEY = "_ecp_auth_token";
|
|
5
|
+
exports.AUTH_RETRY_STATE_KEY = "_ecp_auth_retry_state";
|
|
6
|
+
exports.LEADING_TAB_KEY = "_ecp_leading_tab";
|
|
7
|
+
exports.LEADER_TIMEOUT = 2 * 60 * 1000;
|
|
8
|
+
exports.thresholdMs = 2 * 60_000;
|
|
9
|
+
exports.intervalMs = 1 * 60_000;
|
|
10
|
+
exports.retryDelayMs = 60_000;
|
|
11
|
+
exports.maxRetries = 5;
|
|
12
|
+
exports.backoffFactor = 2;
|
package/dist/lib/error.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
import { UseTokenRefresherOptions } from "../types";
|
|
2
|
-
export declare const AUTH_TOKEN_KEY = "_ecp_auth_token";
|
|
3
|
-
export declare const AUTH_RETRY_STATE_KEY = "_ecp_auth_retry_state";
|
|
4
|
-
export declare const LEADING_TAB_KEY = "_ecp_leading_tab";
|
|
5
|
-
export declare const LEADER_TIMEOUT: number;
|
|
6
|
-
export declare const thresholdMs: number;
|
|
7
|
-
export declare const intervalMs: number;
|
|
8
2
|
export declare function tryRefreshWithRetry(refreshTokenRequest: UseTokenRefresherOptions["refreshTokenRequest"]): Promise<boolean>;
|
|
9
3
|
export declare function isLeader(): boolean | undefined;
|
|
10
4
|
export declare function becomeLeader(): void;
|
|
@@ -1,38 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.intervalMs = exports.thresholdMs = exports.LEADER_TIMEOUT = exports.LEADING_TAB_KEY = exports.AUTH_RETRY_STATE_KEY = exports.AUTH_TOKEN_KEY = void 0;
|
|
4
3
|
exports.tryRefreshWithRetry = tryRefreshWithRetry;
|
|
5
4
|
exports.isLeader = isLeader;
|
|
6
5
|
exports.becomeLeader = becomeLeader;
|
|
7
6
|
exports.renewLeadership = renewLeadership;
|
|
8
7
|
exports.tryToBecomeLeader = tryToBecomeLeader;
|
|
9
8
|
const utils_1 = require("./utils");
|
|
10
|
-
|
|
11
|
-
exports.AUTH_RETRY_STATE_KEY = "_ecp_auth_retry_state";
|
|
12
|
-
exports.LEADING_TAB_KEY = "_ecp_leading_tab";
|
|
13
|
-
exports.LEADER_TIMEOUT = 2 * 60 * 1000;
|
|
14
|
-
exports.thresholdMs = 2 * 60000;
|
|
15
|
-
exports.intervalMs = 1 * 60000;
|
|
16
|
-
const retryDelayMs = 60000;
|
|
17
|
-
const maxRetries = 5;
|
|
18
|
-
const backoffFactor = 2;
|
|
9
|
+
const constants_1 = require("./constants");
|
|
19
10
|
async function tryRefreshWithRetry(refreshTokenRequest) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
for (let attempt = (_a = savedState === null || savedState === void 0 ? void 0 : savedState.attemptNumber) !== null && _a !== void 0 ? _a : 1; attempt <= maxRetries; attempt++) {
|
|
11
|
+
const savedState = JSON.parse(localStorage.getItem(constants_1.AUTH_RETRY_STATE_KEY) || "{}");
|
|
12
|
+
for (let attempt = savedState?.attemptNumber ?? 1; attempt <= constants_1.maxRetries; attempt++) {
|
|
23
13
|
try {
|
|
24
14
|
await refreshTokenRequest();
|
|
25
|
-
localStorage.removeItem(
|
|
15
|
+
localStorage.removeItem(constants_1.AUTH_RETRY_STATE_KEY);
|
|
26
16
|
return true; // ✅ success!
|
|
27
17
|
}
|
|
28
18
|
catch (err) {
|
|
29
19
|
console.warn(`[ecp-auth] Silent refresh attempt ${attempt} failed`, err);
|
|
30
|
-
if (attempt < maxRetries) {
|
|
31
|
-
localStorage.setItem(
|
|
20
|
+
if (attempt < constants_1.maxRetries) {
|
|
21
|
+
localStorage.setItem(constants_1.AUTH_RETRY_STATE_KEY, JSON.stringify({
|
|
32
22
|
lastAttempt: Date.now(),
|
|
33
23
|
attemptNumber: attempt,
|
|
34
24
|
}));
|
|
35
|
-
const delay = retryDelayMs * Math.pow(backoffFactor, attempt - 1);
|
|
25
|
+
const delay = constants_1.retryDelayMs * Math.pow(constants_1.backoffFactor, attempt - 1);
|
|
36
26
|
console.info(`[ecp-auth] Retrying in ${delay}ms...`);
|
|
37
27
|
await new Promise((res) => setTimeout(res, delay));
|
|
38
28
|
}
|
|
@@ -50,11 +40,11 @@ const TAB_ID = (() => {
|
|
|
50
40
|
function isLeader() {
|
|
51
41
|
if (typeof localStorage === "undefined")
|
|
52
42
|
return;
|
|
53
|
-
const leaderData = JSON.parse(localStorage.getItem(
|
|
43
|
+
const leaderData = JSON.parse(localStorage.getItem(constants_1.LEADING_TAB_KEY) || "{}");
|
|
54
44
|
if (!leaderData.id || !leaderData.timestamp)
|
|
55
45
|
return false;
|
|
56
46
|
// If leader is stale, allow takeover
|
|
57
|
-
if (Date.now() - leaderData.timestamp >
|
|
47
|
+
if (Date.now() - leaderData.timestamp > constants_1.LEADER_TIMEOUT)
|
|
58
48
|
return false;
|
|
59
49
|
return leaderData.id === TAB_ID;
|
|
60
50
|
}
|
|
@@ -62,7 +52,7 @@ function becomeLeader() {
|
|
|
62
52
|
const authToken = (0, utils_1.getClientToken)();
|
|
63
53
|
if (!authToken)
|
|
64
54
|
return;
|
|
65
|
-
localStorage.setItem(
|
|
55
|
+
localStorage.setItem(constants_1.LEADING_TAB_KEY, JSON.stringify({ id: TAB_ID, timestamp: Date.now() }));
|
|
66
56
|
}
|
|
67
57
|
function renewLeadership() {
|
|
68
58
|
// Called periodically by leader to keep leadership
|
|
@@ -73,8 +63,8 @@ function renewLeadership() {
|
|
|
73
63
|
function tryToBecomeLeader() {
|
|
74
64
|
if (typeof localStorage === "undefined")
|
|
75
65
|
return;
|
|
76
|
-
const leaderData = JSON.parse(localStorage.getItem(
|
|
77
|
-
if (!leaderData.id || Date.now() - leaderData.timestamp >
|
|
66
|
+
const leaderData = JSON.parse(localStorage.getItem(constants_1.LEADING_TAB_KEY) || "{}");
|
|
67
|
+
if (!leaderData.id || Date.now() - leaderData.timestamp > constants_1.LEADER_TIMEOUT) {
|
|
78
68
|
becomeLeader();
|
|
79
69
|
return true;
|
|
80
70
|
}
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthConfig, User } from "../types";
|
|
1
|
+
import { AuthConfig, QueryOpts, User } from "../types";
|
|
2
2
|
export declare function cn(...classes: (string | false | null | undefined)[]): string;
|
|
3
3
|
export declare function unstuckPointerEvents(): void;
|
|
4
4
|
export declare function buildRouteContext(config?: AuthConfig, currentPath?: string): {
|
|
@@ -7,6 +7,7 @@ export declare function buildRouteContext(config?: AuthConfig, currentPath?: str
|
|
|
7
7
|
dashboardUrl: string;
|
|
8
8
|
resetPasswordUrl: string;
|
|
9
9
|
returnPath: string;
|
|
10
|
+
returnPathRaw: string;
|
|
10
11
|
currentPath: string;
|
|
11
12
|
excludedPaths: string[];
|
|
12
13
|
isExcluded: boolean;
|
|
@@ -14,6 +15,7 @@ export declare function buildRouteContext(config?: AuthConfig, currentPath?: str
|
|
|
14
15
|
export declare function getClientToken(): string;
|
|
15
16
|
export declare function hasPermission(user: User | null, permission: string): boolean;
|
|
16
17
|
export declare function parseMessage(message: string): string;
|
|
18
|
+
export declare function parseGroupId(data?: QueryOpts["groupIds"]): string;
|
|
17
19
|
export declare function parseJwt(token: string): any;
|
|
18
20
|
export declare function isOtpAvailable(): boolean;
|
|
19
21
|
export declare function shouldPassFullUserObject(): boolean;
|
package/dist/lib/utils.js
CHANGED
|
@@ -6,11 +6,13 @@ exports.buildRouteContext = buildRouteContext;
|
|
|
6
6
|
exports.getClientToken = getClientToken;
|
|
7
7
|
exports.hasPermission = hasPermission;
|
|
8
8
|
exports.parseMessage = parseMessage;
|
|
9
|
+
exports.parseGroupId = parseGroupId;
|
|
9
10
|
exports.parseJwt = parseJwt;
|
|
10
11
|
exports.isOtpAvailable = isOtpAvailable;
|
|
11
12
|
exports.shouldPassFullUserObject = shouldPassFullUserObject;
|
|
12
13
|
exports.getPublicTokenEnv = getPublicTokenEnv;
|
|
13
14
|
exports.getPrivateTokenEnv = getPrivateTokenEnv;
|
|
15
|
+
const constants_1 = require("./constants");
|
|
14
16
|
function cn(...classes) {
|
|
15
17
|
return classes.filter(Boolean).join(" ");
|
|
16
18
|
}
|
|
@@ -30,7 +32,6 @@ const defaultAuthConfig = {
|
|
|
30
32
|
excludedPaths: [],
|
|
31
33
|
};
|
|
32
34
|
function buildRouteContext(config, currentPath = "") {
|
|
33
|
-
var _a;
|
|
34
35
|
const finalConfig = { ...defaultAuthConfig, ...config };
|
|
35
36
|
const hostOverride = finalConfig.hostOverride;
|
|
36
37
|
currentPath =
|
|
@@ -50,7 +51,7 @@ function buildRouteContext(config, currentPath = "") {
|
|
|
50
51
|
const excludedPaths = [
|
|
51
52
|
normalize(loginUrl),
|
|
52
53
|
normalize(signupUrl),
|
|
53
|
-
...(
|
|
54
|
+
...(config?.excludedPaths ?? []),
|
|
54
55
|
];
|
|
55
56
|
try {
|
|
56
57
|
if (resetPasswordUrl) {
|
|
@@ -62,15 +63,16 @@ function buildRouteContext(config, currentPath = "") {
|
|
|
62
63
|
}
|
|
63
64
|
const normalizedCurrentPath = normalize(currentPath);
|
|
64
65
|
const isExcluded = excludedPaths.includes(normalizedCurrentPath);
|
|
65
|
-
const
|
|
66
|
-
?
|
|
67
|
-
:
|
|
66
|
+
const returnPathRaw = typeof window !== "undefined"
|
|
67
|
+
? `${window.location.pathname}${window.location.search}`
|
|
68
|
+
: currentPath;
|
|
68
69
|
return {
|
|
69
70
|
loginUrl,
|
|
70
71
|
signupUrl,
|
|
71
72
|
dashboardUrl,
|
|
72
73
|
resetPasswordUrl,
|
|
73
|
-
returnPath,
|
|
74
|
+
returnPath: encodeURIComponent(returnPathRaw),
|
|
75
|
+
returnPathRaw,
|
|
74
76
|
currentPath: normalizedCurrentPath,
|
|
75
77
|
excludedPaths,
|
|
76
78
|
isExcluded,
|
|
@@ -78,34 +80,38 @@ function buildRouteContext(config, currentPath = "") {
|
|
|
78
80
|
}
|
|
79
81
|
function getAbsoluteUrl(pathOrUrl, hostOverride) {
|
|
80
82
|
try {
|
|
81
|
-
const url = new URL(pathOrUrl, hostOverride
|
|
83
|
+
const url = new URL(pathOrUrl, hostOverride ?? "");
|
|
82
84
|
return url.toString();
|
|
83
85
|
}
|
|
84
86
|
catch {
|
|
85
87
|
if (typeof window !== "undefined") {
|
|
86
|
-
const base = hostOverride
|
|
88
|
+
const base = hostOverride ?? window.location.origin;
|
|
87
89
|
return new URL(pathOrUrl.replace(/^\/+/, "/"), base).toString();
|
|
88
90
|
}
|
|
89
91
|
return `http://localhost${pathOrUrl.startsWith("/") ? "" : "/"}${pathOrUrl}`;
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
function getClientToken() {
|
|
93
|
-
var _a;
|
|
94
95
|
return typeof localStorage !== "undefined"
|
|
95
|
-
?
|
|
96
|
+
? localStorage.getItem(constants_1.AUTH_TOKEN_KEY) || ""
|
|
96
97
|
: "";
|
|
97
98
|
}
|
|
98
99
|
function hasPermission(user, permission) {
|
|
99
|
-
var _a;
|
|
100
100
|
if (!user)
|
|
101
101
|
return false;
|
|
102
|
-
return
|
|
102
|
+
return user.role.permissions?.includes(permission) || user.isSuperAdmin;
|
|
103
103
|
}
|
|
104
104
|
function parseMessage(message) {
|
|
105
105
|
return message == "fetch failed" || message == "Failed to fetch"
|
|
106
106
|
? "Please check your internet connection"
|
|
107
107
|
: message;
|
|
108
108
|
}
|
|
109
|
+
function parseGroupId(data) {
|
|
110
|
+
const query = data
|
|
111
|
+
?.map((id) => id ?? "null") // keep null as "null"
|
|
112
|
+
.join(",") || "";
|
|
113
|
+
return query ? `?groupIds=${encodeURIComponent(query)}` : "";
|
|
114
|
+
}
|
|
109
115
|
function base64UrlDecode(b64url) {
|
|
110
116
|
// Replace URL-safe chars
|
|
111
117
|
let base64 = b64url.replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -143,6 +149,5 @@ function getPublicTokenEnv() {
|
|
|
143
149
|
"");
|
|
144
150
|
}
|
|
145
151
|
function getPrivateTokenEnv() {
|
|
146
|
-
|
|
147
|
-
return (_a = process.env.ECP_AUTH_PRIVATE_TOKEN) !== null && _a !== void 0 ? _a : "";
|
|
152
|
+
return process.env.ECP_AUTH_PRIVATE_TOKEN ?? "";
|
|
148
153
|
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from "./toggle-2fa";
|
|
2
2
|
export * from "./reset-password";
|
|
3
3
|
export * from "./users-accounts";
|
|
4
|
-
export * from "./cookie";
|
|
4
|
+
export * from "./next-cookie-override";
|
|
5
5
|
export * from "./server-session";
|
|
6
6
|
export * from "./server-token";
|
|
7
7
|
export * from "./role";
|
package/dist/server/index.js
CHANGED
|
@@ -17,7 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./toggle-2fa"), exports);
|
|
18
18
|
__exportStar(require("./reset-password"), exports);
|
|
19
19
|
__exportStar(require("./users-accounts"), exports);
|
|
20
|
-
__exportStar(require("./cookie"), exports);
|
|
20
|
+
__exportStar(require("./next-cookie-override"), exports);
|
|
21
21
|
__exportStar(require("./server-session"), exports);
|
|
22
22
|
__exportStar(require("./server-token"), exports);
|
|
23
23
|
__exportStar(require("./role"), exports);
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
"use server";
|
|
2
1
|
"use strict";
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
3
|
exports.setCookie = setCookie;
|
|
4
|
+
const constants_1 = require("../lib/constants");
|
|
5
5
|
async function setCookie(value) {
|
|
6
6
|
try {
|
|
7
7
|
// Dynamically import to avoid bundling next in non-Next environments
|
|
8
8
|
const { cookies } = require("next/headers");
|
|
9
9
|
const cookieStore = await cookies();
|
|
10
10
|
if (!value) {
|
|
11
|
-
cookieStore.delete(
|
|
11
|
+
cookieStore.delete(constants_1.AUTH_TOKEN_KEY);
|
|
12
12
|
}
|
|
13
13
|
else {
|
|
14
|
-
cookieStore.set(
|
|
14
|
+
cookieStore.set(constants_1.AUTH_TOKEN_KEY, value, {
|
|
15
15
|
httpOnly: true,
|
|
16
16
|
sameSite: "lax",
|
|
17
17
|
secure: process.env.NODE_ENV === "production",
|
package/dist/server/role.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { Role, NewRole, FormResponse } from "../types";
|
|
2
|
-
export declare function getRoles(options?:
|
|
3
|
-
groupId: string;
|
|
4
|
-
}): Promise<Role[]>;
|
|
1
|
+
import { Role, NewRole, FormResponse, QueryOpts } from "../types";
|
|
2
|
+
export declare function getRoles(options?: QueryOpts): Promise<Role[]>;
|
|
5
3
|
export declare function addRole(role: NewRole): Promise<FormResponse<Role>>;
|
|
6
4
|
export declare function updateRole(roleId: string, role: NewRole): Promise<FormResponse<Role>>;
|
|
7
5
|
export declare function assignPermission(roleId: string, permissions: string[]): Promise<FormResponse<Role>>;
|
package/dist/server/role.js
CHANGED
|
@@ -7,9 +7,11 @@ exports.updateRole = updateRole;
|
|
|
7
7
|
exports.assignPermission = assignPermission;
|
|
8
8
|
const api_1 = require("../lib/api");
|
|
9
9
|
const error_1 = require("../lib/error");
|
|
10
|
+
const utils_1 = require("../lib/utils");
|
|
10
11
|
async function getRoles(options) {
|
|
11
12
|
try {
|
|
12
|
-
const
|
|
13
|
+
const query = (0, utils_1.parseGroupId)(options?.groupIds);
|
|
14
|
+
const res = await (0, api_1.apiFactory)(`/roles${query}`, {
|
|
13
15
|
method: "GET",
|
|
14
16
|
});
|
|
15
17
|
return res.data;
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.getServerToken = getServerToken;
|
|
5
|
+
const constants_1 = require("../lib/constants");
|
|
5
6
|
async function getServerToken(source) {
|
|
6
|
-
|
|
7
|
-
const cookieName = "_ecp_auth_token";
|
|
7
|
+
const cookieName = constants_1.AUTH_TOKEN_KEY;
|
|
8
8
|
// Case 1: No source, assume App Router (next/headers)
|
|
9
9
|
if (!source) {
|
|
10
10
|
try {
|
|
11
11
|
// Dynamically import to avoid bundling next in non-Next environments
|
|
12
12
|
const { cookies } = require("next/headers");
|
|
13
13
|
const cookieStore = await cookies();
|
|
14
|
-
return
|
|
14
|
+
return cookieStore.get(cookieName)?.value;
|
|
15
15
|
}
|
|
16
16
|
catch {
|
|
17
17
|
return undefined;
|
|
@@ -28,8 +28,8 @@ async function getServerToken(source) {
|
|
|
28
28
|
}
|
|
29
29
|
// Case 3: API routes or custom cookie objects
|
|
30
30
|
const cookieSource = source.cookies;
|
|
31
|
-
if (typeof
|
|
32
|
-
return
|
|
31
|
+
if (typeof cookieSource?.get === "function") {
|
|
32
|
+
return cookieSource.get(cookieName)?.value; // next/headers-style
|
|
33
33
|
}
|
|
34
34
|
if (typeof cookieSource === "object") {
|
|
35
35
|
//@ts-ignore
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { VerifyUser, VerifyUserResponse, Signup, UpdateUser, User, GeneralResponse, FormResponse } from "../types";
|
|
1
|
+
import { VerifyUser, VerifyUserResponse, Signup, UpdateUser, User, GeneralResponse, FormResponse, QueryOpts } from "../types";
|
|
2
2
|
export declare function verifyUser(data: VerifyUser): Promise<VerifyUserResponse>;
|
|
3
3
|
export declare function registerUser(data: Signup, options?: {
|
|
4
4
|
isSuperAdmin?: boolean;
|
|
5
5
|
}): Promise<FormResponse<User>>;
|
|
6
6
|
export declare function updateUser(userId: string, data: UpdateUser): Promise<FormResponse<User>>;
|
|
7
|
-
export declare function getUsers(options?:
|
|
8
|
-
groupId: string;
|
|
9
|
-
}): Promise<User[]>;
|
|
7
|
+
export declare function getUsers(options?: QueryOpts): Promise<User[]>;
|
|
10
8
|
export declare function getUserById(userId: string): Promise<User | null>;
|
|
11
9
|
export declare function toggleUserStatus(userId: string): Promise<GeneralResponse>;
|
|
@@ -10,6 +10,7 @@ exports.toggleUserStatus = toggleUserStatus;
|
|
|
10
10
|
const utils_1 = require("../lib/utils");
|
|
11
11
|
const api_1 = require("../lib/api");
|
|
12
12
|
const error_1 = require("../lib/error");
|
|
13
|
+
const utils_2 = require("../lib/utils");
|
|
13
14
|
async function verifyUser(data) {
|
|
14
15
|
try {
|
|
15
16
|
const res = await (0, api_1.apiFactory)("/users/verify", {
|
|
@@ -23,7 +24,7 @@ async function verifyUser(data) {
|
|
|
23
24
|
}
|
|
24
25
|
async function registerUser(data, options) {
|
|
25
26
|
try {
|
|
26
|
-
const res = await (0, api_1.apiFactory)(`/signup?isSuperAdmin=${
|
|
27
|
+
const res = await (0, api_1.apiFactory)(`/signup?isSuperAdmin=${options?.isSuperAdmin || false}`, {
|
|
27
28
|
body: { ...data },
|
|
28
29
|
});
|
|
29
30
|
return res;
|
|
@@ -55,7 +56,8 @@ async function updateUser(userId, data) {
|
|
|
55
56
|
}
|
|
56
57
|
async function getUsers(options) {
|
|
57
58
|
try {
|
|
58
|
-
const
|
|
59
|
+
const query = (0, utils_2.parseGroupId)(options?.groupIds);
|
|
60
|
+
const res = await (0, api_1.apiFactory)(`/users${query}`, {
|
|
59
61
|
method: "GET",
|
|
60
62
|
});
|
|
61
63
|
return res.data;
|
package/dist/types.d.ts
CHANGED
|
@@ -66,7 +66,7 @@ type ComputedRouteContext = {
|
|
|
66
66
|
excludedPaths: string[];
|
|
67
67
|
isExcluded: boolean;
|
|
68
68
|
};
|
|
69
|
-
type CookieOverrideFn = (cookie
|
|
69
|
+
type CookieOverrideFn = (cookie?: string) => Promise<void>;
|
|
70
70
|
export type ApiOptions = {
|
|
71
71
|
method?: "GET" | "POST" | "PUT" | "DELETE";
|
|
72
72
|
body?: Record<string, any>;
|
|
@@ -75,7 +75,7 @@ export type ApiOptions = {
|
|
|
75
75
|
};
|
|
76
76
|
export type NewRole = {
|
|
77
77
|
groupId?: string;
|
|
78
|
-
|
|
78
|
+
label: string;
|
|
79
79
|
description?: string;
|
|
80
80
|
permissions?: string[];
|
|
81
81
|
};
|
|
@@ -149,49 +149,9 @@ export type AuthContextType = {
|
|
|
149
149
|
logout: (params?: Record<string, string>) => void;
|
|
150
150
|
hasPermission: (permission: string) => boolean;
|
|
151
151
|
revalidate: () => void;
|
|
152
|
+
getToken: () => string | null;
|
|
152
153
|
computedRouteContext: ComputedRouteContext;
|
|
153
154
|
};
|
|
154
|
-
export type OTPContextReturn = {
|
|
155
|
-
processing: boolean;
|
|
156
|
-
isOpen: boolean;
|
|
157
|
-
open: () => void;
|
|
158
|
-
close: () => void;
|
|
159
|
-
onCanceled?: () => void;
|
|
160
|
-
successMessage: string;
|
|
161
|
-
errorMessage: string;
|
|
162
|
-
otpValue: string;
|
|
163
|
-
setOtpValue: (value: string) => void;
|
|
164
|
-
sendOtp: () => Promise<void>;
|
|
165
|
-
verifyOtp: () => Promise<void>;
|
|
166
|
-
isVerified: boolean;
|
|
167
|
-
id: string;
|
|
168
|
-
sendOnMount?: boolean;
|
|
169
|
-
hasSent: boolean;
|
|
170
|
-
};
|
|
171
|
-
export type OTPContextProps = {
|
|
172
|
-
id?: string;
|
|
173
|
-
userId: string;
|
|
174
|
-
email?: string;
|
|
175
|
-
sendOnMount?: boolean;
|
|
176
|
-
onVerified: (authToken: string, tempRefreshToken?: string) => void;
|
|
177
|
-
onCanceled?: () => void;
|
|
178
|
-
onSend?: () => void;
|
|
179
|
-
onError?: () => void;
|
|
180
|
-
};
|
|
181
|
-
export type Otp = {
|
|
182
|
-
id: string;
|
|
183
|
-
userId: string;
|
|
184
|
-
createdAt: Date;
|
|
185
|
-
serviceId: string | null;
|
|
186
|
-
sentTo: string;
|
|
187
|
-
code: string;
|
|
188
|
-
channel: Channel;
|
|
189
|
-
used: boolean;
|
|
190
|
-
success: boolean;
|
|
191
|
-
reason: Reason | null;
|
|
192
|
-
expiresAt: Date;
|
|
193
|
-
verifiedAt: Date | null;
|
|
194
|
-
};
|
|
195
155
|
export type User = {
|
|
196
156
|
id: string;
|
|
197
157
|
roleId: string | null;
|
|
@@ -229,33 +189,6 @@ export type Role = {
|
|
|
229
189
|
updatedAt: Date;
|
|
230
190
|
isSuperAdmin: boolean;
|
|
231
191
|
};
|
|
232
|
-
export type Project = {
|
|
233
|
-
id: string;
|
|
234
|
-
projectName: string;
|
|
235
|
-
createdAt: string;
|
|
236
|
-
updatedAt: string;
|
|
237
|
-
isActive: boolean;
|
|
238
|
-
slug: string;
|
|
239
|
-
services: Service[];
|
|
240
|
-
};
|
|
241
|
-
export type Service = {
|
|
242
|
-
id: string;
|
|
243
|
-
label: string;
|
|
244
|
-
type: ServiceType;
|
|
245
|
-
publicKey: string;
|
|
246
|
-
privateKey: string;
|
|
247
|
-
isLive: boolean;
|
|
248
|
-
isActive: boolean;
|
|
249
|
-
lastUsed: string | null;
|
|
250
|
-
createdAt: string;
|
|
251
|
-
updatedAt: string;
|
|
252
|
-
envs: {
|
|
253
|
-
privateEnv: string;
|
|
254
|
-
publicEnv: string;
|
|
255
|
-
combined: string;
|
|
256
|
-
};
|
|
257
|
-
slug: string;
|
|
258
|
-
};
|
|
259
192
|
export type AuthError = {
|
|
260
193
|
errors: Record<string, string>;
|
|
261
194
|
message: string;
|
|
@@ -277,8 +210,8 @@ export type UseTokenRefresherOptions = {
|
|
|
277
210
|
revalidateUserWhenOnline?: () => void;
|
|
278
211
|
config?: AuthConfig;
|
|
279
212
|
};
|
|
213
|
+
export type QueryOpts = {
|
|
214
|
+
groupIds?: (string | number | null)[];
|
|
215
|
+
};
|
|
280
216
|
export type Status = "ACTIVE" | "INACTIVE";
|
|
281
|
-
export type ServiceType = "auth" | "database" | "otp" | "storage";
|
|
282
|
-
export type Channel = "email" | "sms" | "totp" | "whatsapp";
|
|
283
|
-
export type Reason = "expired" | "invalid" | "success";
|
|
284
217
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@explita/cloud-auth-client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"author": "Explita",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "A simple authentication library for React",
|
|
@@ -34,20 +34,20 @@
|
|
|
34
34
|
"otp"
|
|
35
35
|
],
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@tailwindcss/cli": "^4.1.
|
|
38
|
-
"@types/node": "^24",
|
|
39
|
-
"@types/react": "^19",
|
|
40
|
-
"@types/react-dom": "^19",
|
|
41
|
-
"fs-extra": "^11.3.
|
|
37
|
+
"@tailwindcss/cli": "^4.1.12",
|
|
38
|
+
"@types/node": "^24.3.0",
|
|
39
|
+
"@types/react": "^19.1.10",
|
|
40
|
+
"@types/react-dom": "^19.1.7",
|
|
41
|
+
"fs-extra": "^11.3.1",
|
|
42
42
|
"rimraf": "^6.0.1",
|
|
43
|
-
"tailwindcss": "^4.1.
|
|
44
|
-
"typescript": "^5.
|
|
43
|
+
"tailwindcss": "^4.1.12",
|
|
44
|
+
"typescript": "^5.9.2"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"class-variance-authority": "^0.7.1"
|
|
48
48
|
},
|
|
49
49
|
"optionalDependencies": {
|
|
50
|
-
"@explita/cloud-otp-client": "
|
|
50
|
+
"@explita/cloud-otp-client": "^0.0.1"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@radix-ui/react-dialog": "^1",
|
|
File without changes
|