@vivinkv28/strapi-2fa-admin-plugin 0.1.10 → 0.1.13
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/dist/server/index.js
CHANGED
|
@@ -74,6 +74,16 @@ const runtimeRequire = createRequire(__filename);
|
|
|
74
74
|
var strapiSessionAuth = runtimeRequire(resolveSessionAuthPath());
|
|
75
75
|
const sessionAuth$1 = strapiSessionAuth;
|
|
76
76
|
const getService = () => strapi.plugin("admin-2fa").service("auth");
|
|
77
|
+
const APPLICATION_ERROR_STATUS = {
|
|
78
|
+
ApplicationError: 400,
|
|
79
|
+
ValidationError: 400,
|
|
80
|
+
UnauthorizedError: 401,
|
|
81
|
+
ForbiddenError: 403,
|
|
82
|
+
NotFoundError: 404,
|
|
83
|
+
PayloadTooLargeError: 413,
|
|
84
|
+
RateLimitError: 429,
|
|
85
|
+
NotImplementedError: 501
|
|
86
|
+
};
|
|
77
87
|
const setRefreshCookie = (ctx, refreshToken, cookieOptions) => {
|
|
78
88
|
ctx.cookies.set(sessionAuth$1.REFRESH_COOKIE_NAME, refreshToken, cookieOptions);
|
|
79
89
|
};
|
|
@@ -87,32 +97,69 @@ const getClientIp = (ctx) => {
|
|
|
87
97
|
}
|
|
88
98
|
return String(ctx.request.ip ?? ctx.ip ?? "").trim();
|
|
89
99
|
};
|
|
100
|
+
const sendApplicationError = (ctx, error2) => {
|
|
101
|
+
const derivedStatus = typeof error2?.status === "number" && error2.status >= 400 && error2.status < 500 ? error2.status : APPLICATION_ERROR_STATUS[error2?.name] ?? 400;
|
|
102
|
+
ctx.status = derivedStatus;
|
|
103
|
+
ctx.body = {
|
|
104
|
+
data: null,
|
|
105
|
+
error: {
|
|
106
|
+
status: derivedStatus,
|
|
107
|
+
name: error2?.name ?? "ApplicationError",
|
|
108
|
+
message: error2?.message ?? "Request failed",
|
|
109
|
+
details: error2?.details ?? {}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
};
|
|
90
113
|
var auth$3 = {
|
|
91
114
|
async login(ctx) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
115
|
+
try {
|
|
116
|
+
const result = await getService().createChallenge(ctx.request.body ?? {}, {
|
|
117
|
+
clientIp: getClientIp(ctx)
|
|
118
|
+
});
|
|
119
|
+
ctx.body = { data: result };
|
|
120
|
+
} catch (error2) {
|
|
121
|
+
if (error2?.name && APPLICATION_ERROR_STATUS[error2.name]) {
|
|
122
|
+
sendApplicationError(ctx, error2);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
throw error2;
|
|
126
|
+
}
|
|
96
127
|
},
|
|
97
128
|
async resend(ctx) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
129
|
+
try {
|
|
130
|
+
const result = await getService().resendChallenge(ctx.request.body ?? {}, {
|
|
131
|
+
clientIp: getClientIp(ctx)
|
|
132
|
+
});
|
|
133
|
+
ctx.body = { data: result };
|
|
134
|
+
} catch (error2) {
|
|
135
|
+
if (error2?.name && APPLICATION_ERROR_STATUS[error2.name]) {
|
|
136
|
+
sendApplicationError(ctx, error2);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
throw error2;
|
|
140
|
+
}
|
|
102
141
|
},
|
|
103
142
|
async verify(ctx) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
143
|
+
try {
|
|
144
|
+
const result = await getService().verifyChallenge(ctx.request.body ?? {}, {
|
|
145
|
+
secureRequest: ctx.request.secure,
|
|
146
|
+
clientIp: getClientIp(ctx)
|
|
147
|
+
});
|
|
148
|
+
setRefreshCookie(ctx, result.refreshToken, result.cookieOptions);
|
|
149
|
+
ctx.body = {
|
|
150
|
+
data: {
|
|
151
|
+
token: result.accessToken,
|
|
152
|
+
accessToken: result.accessToken,
|
|
153
|
+
user: result.user
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
} catch (error2) {
|
|
157
|
+
if (error2?.name && APPLICATION_ERROR_STATUS[error2.name]) {
|
|
158
|
+
sendApplicationError(ctx, error2);
|
|
159
|
+
return;
|
|
114
160
|
}
|
|
115
|
-
|
|
161
|
+
throw error2;
|
|
162
|
+
}
|
|
116
163
|
}
|
|
117
164
|
};
|
|
118
165
|
const auth$2 = auth$3;
|
|
@@ -19149,21 +19196,21 @@ class ForbiddenError extends ApplicationError$1 {
|
|
|
19149
19196
|
this.message = message;
|
|
19150
19197
|
}
|
|
19151
19198
|
}
|
|
19152
|
-
class UnauthorizedError extends ApplicationError$1 {
|
|
19199
|
+
let UnauthorizedError$1 = class UnauthorizedError extends ApplicationError$1 {
|
|
19153
19200
|
constructor(message = "Unauthorized", details) {
|
|
19154
19201
|
super(message, details);
|
|
19155
19202
|
this.name = "UnauthorizedError";
|
|
19156
19203
|
this.message = message;
|
|
19157
19204
|
}
|
|
19158
|
-
}
|
|
19159
|
-
class RateLimitError extends ApplicationError$1 {
|
|
19205
|
+
};
|
|
19206
|
+
let RateLimitError$1 = class RateLimitError extends ApplicationError$1 {
|
|
19160
19207
|
constructor(message = "Too many requests, please try again later.", details) {
|
|
19161
19208
|
super(message, details);
|
|
19162
19209
|
this.name = "RateLimitError";
|
|
19163
19210
|
this.message = message;
|
|
19164
19211
|
this.details = details || {};
|
|
19165
19212
|
}
|
|
19166
|
-
}
|
|
19213
|
+
};
|
|
19167
19214
|
class PayloadTooLargeError extends ApplicationError$1 {
|
|
19168
19215
|
constructor(message = "Entity too large", details) {
|
|
19169
19216
|
super(message, details);
|
|
@@ -19196,8 +19243,8 @@ const errors$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
19196
19243
|
PaginationError,
|
|
19197
19244
|
PayloadTooLargeError,
|
|
19198
19245
|
PolicyError,
|
|
19199
|
-
RateLimitError,
|
|
19200
|
-
UnauthorizedError,
|
|
19246
|
+
RateLimitError: RateLimitError$1,
|
|
19247
|
+
UnauthorizedError: UnauthorizedError$1,
|
|
19201
19248
|
ValidationError: ValidationError$1,
|
|
19202
19249
|
YupValidationError
|
|
19203
19250
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
@@ -43795,7 +43842,7 @@ const require$$1 = /* @__PURE__ */ getAugmentedNamespace(dist);
|
|
|
43795
43842
|
const crypto = require$$1__default$1.default;
|
|
43796
43843
|
const { errors } = require$$1;
|
|
43797
43844
|
const sessionAuth = strapiSessionAuth;
|
|
43798
|
-
const { ApplicationError: ApplicationError2, ValidationError: ValidationError3 } = errors;
|
|
43845
|
+
const { ApplicationError: ApplicationError2, RateLimitError: RateLimitError2, UnauthorizedError: UnauthorizedError2, ValidationError: ValidationError3 } = errors;
|
|
43799
43846
|
const STORE_NAME = "admin-otp-login";
|
|
43800
43847
|
const STORE_KEY_PREFIX = "challenge:";
|
|
43801
43848
|
const RATE_LIMIT_KEY_PREFIX = "rate-limit:";
|
|
@@ -43914,11 +43961,11 @@ const deleteChallenge = async (store, challengeId) => {
|
|
|
43914
43961
|
const getChallenge = async (store, challengeId) => {
|
|
43915
43962
|
const challenge = await store.get({ key: getStoreKey(challengeId) });
|
|
43916
43963
|
if (!challenge) {
|
|
43917
|
-
throw new
|
|
43964
|
+
throw new UnauthorizedError2("OTP session not found. Please log in again.");
|
|
43918
43965
|
}
|
|
43919
43966
|
if (new Date(challenge.expiresAt).getTime() <= Date.now()) {
|
|
43920
43967
|
await deleteChallenge(store, challengeId);
|
|
43921
|
-
throw new
|
|
43968
|
+
throw new UnauthorizedError2("OTP expired. Please log in again.");
|
|
43922
43969
|
}
|
|
43923
43970
|
return challenge;
|
|
43924
43971
|
};
|
|
@@ -43941,7 +43988,7 @@ const registerRateLimitHit = async (store, config2, action, scope, identifier) =
|
|
|
43941
43988
|
return;
|
|
43942
43989
|
}
|
|
43943
43990
|
if (existing.count >= limit) {
|
|
43944
|
-
throw new
|
|
43991
|
+
throw new RateLimitError2("Too many authentication attempts. Please wait a few minutes and try again.");
|
|
43945
43992
|
}
|
|
43946
43993
|
await store.set({
|
|
43947
43994
|
key,
|
|
@@ -44020,7 +44067,7 @@ var auth$1 = () => ({
|
|
|
44020
44067
|
});
|
|
44021
44068
|
logDuration(config2, "checkCredentials", credentialsStartedAt);
|
|
44022
44069
|
if (!user) {
|
|
44023
|
-
throw new
|
|
44070
|
+
throw new UnauthorizedError2(info?.message ?? "Invalid credentials");
|
|
44024
44071
|
}
|
|
44025
44072
|
const challengeId = crypto.randomUUID();
|
|
44026
44073
|
const code = createOtpCode(config2.otpDigits);
|
|
@@ -44069,7 +44116,7 @@ var auth$1 = () => ({
|
|
|
44069
44116
|
await registerRateLimitHit(store, config2, "resend", "email", current.email);
|
|
44070
44117
|
if (current.resendCount >= config2.maxResends) {
|
|
44071
44118
|
await deleteChallenge(store, challengeId);
|
|
44072
|
-
throw new
|
|
44119
|
+
throw new RateLimitError2("Maximum OTP resend attempts exceeded. Please log in again.");
|
|
44073
44120
|
}
|
|
44074
44121
|
const code = createOtpCode(config2.otpDigits);
|
|
44075
44122
|
const salt = crypto.randomBytes(16).toString("hex");
|
|
@@ -44112,7 +44159,7 @@ var auth$1 = () => ({
|
|
|
44112
44159
|
await registerRateLimitHit(store, config2, "verify", "email", challenge.email);
|
|
44113
44160
|
if (challenge.attempts >= config2.maxAttempts) {
|
|
44114
44161
|
await deleteChallenge(store, challengeId);
|
|
44115
|
-
throw new
|
|
44162
|
+
throw new RateLimitError2("Maximum OTP attempts exceeded. Please log in again.");
|
|
44116
44163
|
}
|
|
44117
44164
|
const hashStartedAt = now();
|
|
44118
44165
|
const computedHash = await createOtpHash(challengeId, code, challenge.salt);
|
|
@@ -44125,7 +44172,7 @@ var auth$1 = () => ({
|
|
|
44125
44172
|
const nextAttempts = challenge.attempts + 1;
|
|
44126
44173
|
if (nextAttempts >= config2.maxAttempts) {
|
|
44127
44174
|
await deleteChallenge(store, challengeId);
|
|
44128
|
-
throw new
|
|
44175
|
+
throw new RateLimitError2("Maximum OTP attempts exceeded. Please log in again.");
|
|
44129
44176
|
}
|
|
44130
44177
|
const storeStartedAt = now();
|
|
44131
44178
|
await store.set({
|
|
@@ -44139,7 +44186,7 @@ var auth$1 = () => ({
|
|
|
44139
44186
|
challengeId,
|
|
44140
44187
|
attempts: nextAttempts
|
|
44141
44188
|
});
|
|
44142
|
-
throw new
|
|
44189
|
+
throw new UnauthorizedError2("Invalid OTP code");
|
|
44143
44190
|
}
|
|
44144
44191
|
const deleteStartedAt = now();
|
|
44145
44192
|
await deleteChallenge(store, challengeId);
|
package/dist/server/index.mjs
CHANGED
|
@@ -60,6 +60,16 @@ const runtimeRequire = createRequire(__filename);
|
|
|
60
60
|
var strapiSessionAuth = runtimeRequire(resolveSessionAuthPath());
|
|
61
61
|
const sessionAuth$1 = strapiSessionAuth;
|
|
62
62
|
const getService = () => strapi.plugin("admin-2fa").service("auth");
|
|
63
|
+
const APPLICATION_ERROR_STATUS = {
|
|
64
|
+
ApplicationError: 400,
|
|
65
|
+
ValidationError: 400,
|
|
66
|
+
UnauthorizedError: 401,
|
|
67
|
+
ForbiddenError: 403,
|
|
68
|
+
NotFoundError: 404,
|
|
69
|
+
PayloadTooLargeError: 413,
|
|
70
|
+
RateLimitError: 429,
|
|
71
|
+
NotImplementedError: 501
|
|
72
|
+
};
|
|
63
73
|
const setRefreshCookie = (ctx, refreshToken, cookieOptions) => {
|
|
64
74
|
ctx.cookies.set(sessionAuth$1.REFRESH_COOKIE_NAME, refreshToken, cookieOptions);
|
|
65
75
|
};
|
|
@@ -73,32 +83,69 @@ const getClientIp = (ctx) => {
|
|
|
73
83
|
}
|
|
74
84
|
return String(ctx.request.ip ?? ctx.ip ?? "").trim();
|
|
75
85
|
};
|
|
86
|
+
const sendApplicationError = (ctx, error2) => {
|
|
87
|
+
const derivedStatus = typeof error2?.status === "number" && error2.status >= 400 && error2.status < 500 ? error2.status : APPLICATION_ERROR_STATUS[error2?.name] ?? 400;
|
|
88
|
+
ctx.status = derivedStatus;
|
|
89
|
+
ctx.body = {
|
|
90
|
+
data: null,
|
|
91
|
+
error: {
|
|
92
|
+
status: derivedStatus,
|
|
93
|
+
name: error2?.name ?? "ApplicationError",
|
|
94
|
+
message: error2?.message ?? "Request failed",
|
|
95
|
+
details: error2?.details ?? {}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
};
|
|
76
99
|
var auth$3 = {
|
|
77
100
|
async login(ctx) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
101
|
+
try {
|
|
102
|
+
const result = await getService().createChallenge(ctx.request.body ?? {}, {
|
|
103
|
+
clientIp: getClientIp(ctx)
|
|
104
|
+
});
|
|
105
|
+
ctx.body = { data: result };
|
|
106
|
+
} catch (error2) {
|
|
107
|
+
if (error2?.name && APPLICATION_ERROR_STATUS[error2.name]) {
|
|
108
|
+
sendApplicationError(ctx, error2);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
throw error2;
|
|
112
|
+
}
|
|
82
113
|
},
|
|
83
114
|
async resend(ctx) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
115
|
+
try {
|
|
116
|
+
const result = await getService().resendChallenge(ctx.request.body ?? {}, {
|
|
117
|
+
clientIp: getClientIp(ctx)
|
|
118
|
+
});
|
|
119
|
+
ctx.body = { data: result };
|
|
120
|
+
} catch (error2) {
|
|
121
|
+
if (error2?.name && APPLICATION_ERROR_STATUS[error2.name]) {
|
|
122
|
+
sendApplicationError(ctx, error2);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
throw error2;
|
|
126
|
+
}
|
|
88
127
|
},
|
|
89
128
|
async verify(ctx) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
129
|
+
try {
|
|
130
|
+
const result = await getService().verifyChallenge(ctx.request.body ?? {}, {
|
|
131
|
+
secureRequest: ctx.request.secure,
|
|
132
|
+
clientIp: getClientIp(ctx)
|
|
133
|
+
});
|
|
134
|
+
setRefreshCookie(ctx, result.refreshToken, result.cookieOptions);
|
|
135
|
+
ctx.body = {
|
|
136
|
+
data: {
|
|
137
|
+
token: result.accessToken,
|
|
138
|
+
accessToken: result.accessToken,
|
|
139
|
+
user: result.user
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
} catch (error2) {
|
|
143
|
+
if (error2?.name && APPLICATION_ERROR_STATUS[error2.name]) {
|
|
144
|
+
sendApplicationError(ctx, error2);
|
|
145
|
+
return;
|
|
100
146
|
}
|
|
101
|
-
|
|
147
|
+
throw error2;
|
|
148
|
+
}
|
|
102
149
|
}
|
|
103
150
|
};
|
|
104
151
|
const auth$2 = auth$3;
|
|
@@ -19135,21 +19182,21 @@ class ForbiddenError extends ApplicationError$1 {
|
|
|
19135
19182
|
this.message = message;
|
|
19136
19183
|
}
|
|
19137
19184
|
}
|
|
19138
|
-
class UnauthorizedError extends ApplicationError$1 {
|
|
19185
|
+
let UnauthorizedError$1 = class UnauthorizedError extends ApplicationError$1 {
|
|
19139
19186
|
constructor(message = "Unauthorized", details) {
|
|
19140
19187
|
super(message, details);
|
|
19141
19188
|
this.name = "UnauthorizedError";
|
|
19142
19189
|
this.message = message;
|
|
19143
19190
|
}
|
|
19144
|
-
}
|
|
19145
|
-
class RateLimitError extends ApplicationError$1 {
|
|
19191
|
+
};
|
|
19192
|
+
let RateLimitError$1 = class RateLimitError extends ApplicationError$1 {
|
|
19146
19193
|
constructor(message = "Too many requests, please try again later.", details) {
|
|
19147
19194
|
super(message, details);
|
|
19148
19195
|
this.name = "RateLimitError";
|
|
19149
19196
|
this.message = message;
|
|
19150
19197
|
this.details = details || {};
|
|
19151
19198
|
}
|
|
19152
|
-
}
|
|
19199
|
+
};
|
|
19153
19200
|
class PayloadTooLargeError extends ApplicationError$1 {
|
|
19154
19201
|
constructor(message = "Entity too large", details) {
|
|
19155
19202
|
super(message, details);
|
|
@@ -19182,8 +19229,8 @@ const errors$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
19182
19229
|
PaginationError,
|
|
19183
19230
|
PayloadTooLargeError,
|
|
19184
19231
|
PolicyError,
|
|
19185
|
-
RateLimitError,
|
|
19186
|
-
UnauthorizedError,
|
|
19232
|
+
RateLimitError: RateLimitError$1,
|
|
19233
|
+
UnauthorizedError: UnauthorizedError$1,
|
|
19187
19234
|
ValidationError: ValidationError$1,
|
|
19188
19235
|
YupValidationError
|
|
19189
19236
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
@@ -43781,7 +43828,7 @@ const require$$1 = /* @__PURE__ */ getAugmentedNamespace(dist);
|
|
|
43781
43828
|
const crypto = require$$1$2;
|
|
43782
43829
|
const { errors } = require$$1;
|
|
43783
43830
|
const sessionAuth = strapiSessionAuth;
|
|
43784
|
-
const { ApplicationError: ApplicationError2, ValidationError: ValidationError3 } = errors;
|
|
43831
|
+
const { ApplicationError: ApplicationError2, RateLimitError: RateLimitError2, UnauthorizedError: UnauthorizedError2, ValidationError: ValidationError3 } = errors;
|
|
43785
43832
|
const STORE_NAME = "admin-otp-login";
|
|
43786
43833
|
const STORE_KEY_PREFIX = "challenge:";
|
|
43787
43834
|
const RATE_LIMIT_KEY_PREFIX = "rate-limit:";
|
|
@@ -43900,11 +43947,11 @@ const deleteChallenge = async (store, challengeId) => {
|
|
|
43900
43947
|
const getChallenge = async (store, challengeId) => {
|
|
43901
43948
|
const challenge = await store.get({ key: getStoreKey(challengeId) });
|
|
43902
43949
|
if (!challenge) {
|
|
43903
|
-
throw new
|
|
43950
|
+
throw new UnauthorizedError2("OTP session not found. Please log in again.");
|
|
43904
43951
|
}
|
|
43905
43952
|
if (new Date(challenge.expiresAt).getTime() <= Date.now()) {
|
|
43906
43953
|
await deleteChallenge(store, challengeId);
|
|
43907
|
-
throw new
|
|
43954
|
+
throw new UnauthorizedError2("OTP expired. Please log in again.");
|
|
43908
43955
|
}
|
|
43909
43956
|
return challenge;
|
|
43910
43957
|
};
|
|
@@ -43927,7 +43974,7 @@ const registerRateLimitHit = async (store, config2, action, scope, identifier) =
|
|
|
43927
43974
|
return;
|
|
43928
43975
|
}
|
|
43929
43976
|
if (existing.count >= limit) {
|
|
43930
|
-
throw new
|
|
43977
|
+
throw new RateLimitError2("Too many authentication attempts. Please wait a few minutes and try again.");
|
|
43931
43978
|
}
|
|
43932
43979
|
await store.set({
|
|
43933
43980
|
key,
|
|
@@ -44006,7 +44053,7 @@ var auth$1 = () => ({
|
|
|
44006
44053
|
});
|
|
44007
44054
|
logDuration(config2, "checkCredentials", credentialsStartedAt);
|
|
44008
44055
|
if (!user) {
|
|
44009
|
-
throw new
|
|
44056
|
+
throw new UnauthorizedError2(info?.message ?? "Invalid credentials");
|
|
44010
44057
|
}
|
|
44011
44058
|
const challengeId = crypto.randomUUID();
|
|
44012
44059
|
const code = createOtpCode(config2.otpDigits);
|
|
@@ -44055,7 +44102,7 @@ var auth$1 = () => ({
|
|
|
44055
44102
|
await registerRateLimitHit(store, config2, "resend", "email", current.email);
|
|
44056
44103
|
if (current.resendCount >= config2.maxResends) {
|
|
44057
44104
|
await deleteChallenge(store, challengeId);
|
|
44058
|
-
throw new
|
|
44105
|
+
throw new RateLimitError2("Maximum OTP resend attempts exceeded. Please log in again.");
|
|
44059
44106
|
}
|
|
44060
44107
|
const code = createOtpCode(config2.otpDigits);
|
|
44061
44108
|
const salt = crypto.randomBytes(16).toString("hex");
|
|
@@ -44098,7 +44145,7 @@ var auth$1 = () => ({
|
|
|
44098
44145
|
await registerRateLimitHit(store, config2, "verify", "email", challenge.email);
|
|
44099
44146
|
if (challenge.attempts >= config2.maxAttempts) {
|
|
44100
44147
|
await deleteChallenge(store, challengeId);
|
|
44101
|
-
throw new
|
|
44148
|
+
throw new RateLimitError2("Maximum OTP attempts exceeded. Please log in again.");
|
|
44102
44149
|
}
|
|
44103
44150
|
const hashStartedAt = now();
|
|
44104
44151
|
const computedHash = await createOtpHash(challengeId, code, challenge.salt);
|
|
@@ -44111,7 +44158,7 @@ var auth$1 = () => ({
|
|
|
44111
44158
|
const nextAttempts = challenge.attempts + 1;
|
|
44112
44159
|
if (nextAttempts >= config2.maxAttempts) {
|
|
44113
44160
|
await deleteChallenge(store, challengeId);
|
|
44114
|
-
throw new
|
|
44161
|
+
throw new RateLimitError2("Maximum OTP attempts exceeded. Please log in again.");
|
|
44115
44162
|
}
|
|
44116
44163
|
const storeStartedAt = now();
|
|
44117
44164
|
await store.set({
|
|
@@ -44125,7 +44172,7 @@ var auth$1 = () => ({
|
|
|
44125
44172
|
challengeId,
|
|
44126
44173
|
attempts: nextAttempts
|
|
44127
44174
|
});
|
|
44128
|
-
throw new
|
|
44175
|
+
throw new UnauthorizedError2("Invalid OTP code");
|
|
44129
44176
|
}
|
|
44130
44177
|
const deleteStartedAt = now();
|
|
44131
44178
|
await deleteChallenge(store, challengeId);
|
|
@@ -35,8 +35,23 @@ function _interopNamespaceDefault(e) {
|
|
|
35
35
|
return Object.freeze(n);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
39
|
-
var yup__namespace = /*#__PURE__*/_interopNamespaceDefault(yup);
|
|
38
|
+
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
39
|
+
var yup__namespace = /*#__PURE__*/_interopNamespaceDefault(yup);
|
|
40
|
+
|
|
41
|
+
const getApiErrorMessage = (error, fallback = 'Something went wrong')=>{
|
|
42
|
+
if (!error || typeof error !== 'object') {
|
|
43
|
+
return fallback;
|
|
44
|
+
}
|
|
45
|
+
const candidates = [
|
|
46
|
+
error?.data?.error?.message,
|
|
47
|
+
error?.data?.message,
|
|
48
|
+
error?.error?.message,
|
|
49
|
+
error?.message,
|
|
50
|
+
typeof error?.error === 'string' ? error.error : undefined
|
|
51
|
+
];
|
|
52
|
+
const message = candidates.find((value)=>typeof value === 'string' && value.trim().length > 0);
|
|
53
|
+
return message ?? fallback;
|
|
54
|
+
};
|
|
40
55
|
|
|
41
56
|
const OTP_LENGTH = 6;
|
|
42
57
|
const OTP_DIGIT_INPUT_STYLE = {
|
|
@@ -240,18 +255,18 @@ const Login = ({ children })=>{
|
|
|
240
255
|
React__namespace.useEffect(()=>{
|
|
241
256
|
document.title = 'Admin Dashboard';
|
|
242
257
|
}, []);
|
|
243
|
-
const handleLogin = async (body)=>{
|
|
244
|
-
setApiError(undefined);
|
|
245
|
-
const res = await adminLoginWithOtp({
|
|
246
|
-
...body,
|
|
247
|
-
deviceId: deviceId.getOrCreateDeviceId()
|
|
248
|
-
});
|
|
249
|
-
if ('error' in res) {
|
|
250
|
-
const message = res.error
|
|
251
|
-
if (camelCase(message).toLowerCase() === 'usernotactive') {
|
|
252
|
-
navigate('/auth/oops');
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
258
|
+
const handleLogin = async (body)=>{
|
|
259
|
+
setApiError(undefined);
|
|
260
|
+
const res = await adminLoginWithOtp({
|
|
261
|
+
...body,
|
|
262
|
+
deviceId: deviceId.getOrCreateDeviceId()
|
|
263
|
+
});
|
|
264
|
+
if ('error' in res) {
|
|
265
|
+
const message = getApiErrorMessage(res.error);
|
|
266
|
+
if (camelCase(message).toLowerCase() === 'usernotactive') {
|
|
267
|
+
navigate('/auth/oops');
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
255
270
|
setApiError(message);
|
|
256
271
|
} else {
|
|
257
272
|
setOtpStep({
|
|
@@ -267,15 +282,15 @@ const Login = ({ children })=>{
|
|
|
267
282
|
return;
|
|
268
283
|
}
|
|
269
284
|
setApiError(undefined);
|
|
270
|
-
const res = await verifyAdminLoginOtp({
|
|
271
|
-
challengeId: otpStep.challengeId,
|
|
272
|
-
code
|
|
273
|
-
});
|
|
274
|
-
if ('error' in res) {
|
|
275
|
-
setApiError(res.error
|
|
276
|
-
} else {
|
|
277
|
-
toggleNotification({
|
|
278
|
-
type: 'success',
|
|
285
|
+
const res = await verifyAdminLoginOtp({
|
|
286
|
+
challengeId: otpStep.challengeId,
|
|
287
|
+
code
|
|
288
|
+
});
|
|
289
|
+
if ('error' in res) {
|
|
290
|
+
setApiError(getApiErrorMessage(res.error));
|
|
291
|
+
} else {
|
|
292
|
+
toggleNotification({
|
|
293
|
+
type: 'success',
|
|
279
294
|
title: formatMessage({
|
|
280
295
|
id: 'Auth.notification.authenticated.title',
|
|
281
296
|
defaultMessage: 'Successfully authenticated'
|
|
@@ -295,14 +310,14 @@ const Login = ({ children })=>{
|
|
|
295
310
|
return;
|
|
296
311
|
}
|
|
297
312
|
setApiError(undefined);
|
|
298
|
-
const res = await resendAdminLoginOtp({
|
|
299
|
-
challengeId: otpStep.challengeId
|
|
300
|
-
});
|
|
301
|
-
if ('error' in res) {
|
|
302
|
-
setApiError(res.error
|
|
303
|
-
} else {
|
|
304
|
-
setOtpStep({
|
|
305
|
-
...otpStep,
|
|
313
|
+
const res = await resendAdminLoginOtp({
|
|
314
|
+
challengeId: otpStep.challengeId
|
|
315
|
+
});
|
|
316
|
+
if ('error' in res) {
|
|
317
|
+
setApiError(getApiErrorMessage(res.error));
|
|
318
|
+
} else {
|
|
319
|
+
setOtpStep({
|
|
320
|
+
...otpStep,
|
|
306
321
|
expiresAt: res.data.expiresAt,
|
|
307
322
|
maskedEmail: res.data.maskedEmail
|
|
308
323
|
});
|
|
@@ -13,8 +13,23 @@ import { useTypedDispatch } from '../../../core/store/hooks.mjs';
|
|
|
13
13
|
import { useNotification } from '../../../features/Notifications.mjs';
|
|
14
14
|
import { login as loginAction } from '../../../reducer.mjs';
|
|
15
15
|
import { useAdminLoginWithOtpMutation, useVerifyAdminLoginOtpMutation, useResendAdminLoginOtpMutation } from '../../../services/auth.mjs';
|
|
16
|
-
import { getOrCreateDeviceId } from '../../../utils/deviceId.mjs';
|
|
17
|
-
import { translatedErrors as errorsTrads } from '../../../utils/translatedErrors.mjs';
|
|
16
|
+
import { getOrCreateDeviceId } from '../../../utils/deviceId.mjs';
|
|
17
|
+
import { translatedErrors as errorsTrads } from '../../../utils/translatedErrors.mjs';
|
|
18
|
+
|
|
19
|
+
const getApiErrorMessage = (error, fallback = 'Something went wrong')=>{
|
|
20
|
+
if (!error || typeof error !== 'object') {
|
|
21
|
+
return fallback;
|
|
22
|
+
}
|
|
23
|
+
const candidates = [
|
|
24
|
+
error?.data?.error?.message,
|
|
25
|
+
error?.data?.message,
|
|
26
|
+
error?.error?.message,
|
|
27
|
+
error?.message,
|
|
28
|
+
typeof error?.error === 'string' ? error.error : undefined
|
|
29
|
+
];
|
|
30
|
+
const message = candidates.find((value)=>typeof value === 'string' && value.trim().length > 0);
|
|
31
|
+
return message ?? fallback;
|
|
32
|
+
};
|
|
18
33
|
|
|
19
34
|
const OTP_LENGTH = 6;
|
|
20
35
|
const OTP_DIGIT_INPUT_STYLE = {
|
|
@@ -218,18 +233,18 @@ const Login = ({ children })=>{
|
|
|
218
233
|
React.useEffect(()=>{
|
|
219
234
|
document.title = 'Admin Dashboard';
|
|
220
235
|
}, []);
|
|
221
|
-
const handleLogin = async (body)=>{
|
|
222
|
-
setApiError(undefined);
|
|
223
|
-
const res = await adminLoginWithOtp({
|
|
224
|
-
...body,
|
|
225
|
-
deviceId: getOrCreateDeviceId()
|
|
226
|
-
});
|
|
227
|
-
if ('error' in res) {
|
|
228
|
-
const message = res.error
|
|
229
|
-
if (camelCase(message).toLowerCase() === 'usernotactive') {
|
|
230
|
-
navigate('/auth/oops');
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
236
|
+
const handleLogin = async (body)=>{
|
|
237
|
+
setApiError(undefined);
|
|
238
|
+
const res = await adminLoginWithOtp({
|
|
239
|
+
...body,
|
|
240
|
+
deviceId: getOrCreateDeviceId()
|
|
241
|
+
});
|
|
242
|
+
if ('error' in res) {
|
|
243
|
+
const message = getApiErrorMessage(res.error);
|
|
244
|
+
if (camelCase(message).toLowerCase() === 'usernotactive') {
|
|
245
|
+
navigate('/auth/oops');
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
233
248
|
setApiError(message);
|
|
234
249
|
} else {
|
|
235
250
|
setOtpStep({
|
|
@@ -245,15 +260,15 @@ const Login = ({ children })=>{
|
|
|
245
260
|
return;
|
|
246
261
|
}
|
|
247
262
|
setApiError(undefined);
|
|
248
|
-
const res = await verifyAdminLoginOtp({
|
|
249
|
-
challengeId: otpStep.challengeId,
|
|
250
|
-
code
|
|
251
|
-
});
|
|
252
|
-
if ('error' in res) {
|
|
253
|
-
setApiError(res.error
|
|
254
|
-
} else {
|
|
255
|
-
toggleNotification({
|
|
256
|
-
type: 'success',
|
|
263
|
+
const res = await verifyAdminLoginOtp({
|
|
264
|
+
challengeId: otpStep.challengeId,
|
|
265
|
+
code
|
|
266
|
+
});
|
|
267
|
+
if ('error' in res) {
|
|
268
|
+
setApiError(getApiErrorMessage(res.error));
|
|
269
|
+
} else {
|
|
270
|
+
toggleNotification({
|
|
271
|
+
type: 'success',
|
|
257
272
|
title: formatMessage({
|
|
258
273
|
id: 'Auth.notification.authenticated.title',
|
|
259
274
|
defaultMessage: 'Successfully authenticated'
|
|
@@ -273,14 +288,14 @@ const Login = ({ children })=>{
|
|
|
273
288
|
return;
|
|
274
289
|
}
|
|
275
290
|
setApiError(undefined);
|
|
276
|
-
const res = await resendAdminLoginOtp({
|
|
277
|
-
challengeId: otpStep.challengeId
|
|
278
|
-
});
|
|
279
|
-
if ('error' in res) {
|
|
280
|
-
setApiError(res.error
|
|
281
|
-
} else {
|
|
282
|
-
setOtpStep({
|
|
283
|
-
...otpStep,
|
|
291
|
+
const res = await resendAdminLoginOtp({
|
|
292
|
+
challengeId: otpStep.challengeId
|
|
293
|
+
});
|
|
294
|
+
if ('error' in res) {
|
|
295
|
+
setApiError(getApiErrorMessage(res.error));
|
|
296
|
+
} else {
|
|
297
|
+
setOtpStep({
|
|
298
|
+
...otpStep,
|
|
284
299
|
expiresAt: res.data.expiresAt,
|
|
285
300
|
maskedEmail: res.data.maskedEmail
|
|
286
301
|
});
|