@jskit-ai/auth-core 0.1.83 → 0.1.85
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/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
"packageVersion": 1,
|
|
3
3
|
"packageId": "@jskit-ai/auth-core",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.85",
|
|
5
5
|
"kind": "runtime",
|
|
6
6
|
"dependsOn": [
|
|
7
7
|
"@jskit-ai/value-app-config-shared"
|
|
@@ -69,7 +69,7 @@ export default Object.freeze({
|
|
|
69
69
|
"mutations": {
|
|
70
70
|
"dependencies": {
|
|
71
71
|
"runtime": {
|
|
72
|
-
"@jskit-ai/kernel": "0.1.
|
|
72
|
+
"@jskit-ai/kernel": "0.1.86",
|
|
73
73
|
"@fastify/cookie": "^11.0.2",
|
|
74
74
|
"@fastify/csrf-protection": "^7.1.0",
|
|
75
75
|
"@fastify/rate-limit": "^10.3.0"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/auth-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.85",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"./shared": "./src/shared/index.js",
|
|
24
24
|
"./shared/authApi": "./src/shared/authApi.js",
|
|
25
25
|
"./shared/signOutFlow": "./src/shared/signOutFlow.js",
|
|
26
|
+
"./shared/authDenied": "./src/shared/authDenied.js",
|
|
26
27
|
"./shared/authPaths": "./src/shared/authPaths.js",
|
|
27
28
|
"./shared/authConstraints": "./src/shared/authConstraints.js",
|
|
28
29
|
"./shared/authMethods": "./src/shared/authMethods.js",
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
"./shared/commands/authSessionReadCommand": "./src/shared/commands/authSessionReadCommand.js"
|
|
47
48
|
},
|
|
48
49
|
"dependencies": {
|
|
49
|
-
"@jskit-ai/kernel": "0.1.
|
|
50
|
+
"@jskit-ai/kernel": "0.1.86",
|
|
50
51
|
"@fastify/cookie": "^11.0.2",
|
|
51
52
|
"@fastify/csrf-protection": "^7.1.0",
|
|
52
53
|
"@fastify/rate-limit": "^10.3.0",
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const AUTH_DENIED_CODE_MAX_LENGTH = 80;
|
|
2
|
+
const AUTH_DENIED_MESSAGE_MAX_LENGTH = 240;
|
|
3
|
+
const AUTH_DENIED_CODE_PATTERN = "^[a-z][a-z0-9_.:-]{1,79}$";
|
|
4
|
+
|
|
5
|
+
const AUTH_DENIED_CODES = Object.freeze({
|
|
6
|
+
NOT_ALLOWLISTED: "not_allowlisted",
|
|
7
|
+
BLOCKED: "blocked"
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const AUTH_DENIED_DEFAULT_MESSAGES = Object.freeze({
|
|
11
|
+
[AUTH_DENIED_CODES.NOT_ALLOWLISTED]: "This account is not allowed to access this application.",
|
|
12
|
+
[AUTH_DENIED_CODES.BLOCKED]: "This account has been blocked from accessing this application."
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const AUTH_DENIED_LOGIN_MESSAGES = Object.freeze({
|
|
16
|
+
[AUTH_DENIED_CODES.NOT_ALLOWLISTED]:
|
|
17
|
+
"Sign-in succeeded, but this account is not allowed to access this application.",
|
|
18
|
+
[AUTH_DENIED_CODES.BLOCKED]:
|
|
19
|
+
"Sign-in succeeded, but this account has been blocked from accessing this application."
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
function normalizeAuthDeniedCode(value = "") {
|
|
23
|
+
const code = String(value || "")
|
|
24
|
+
.trim()
|
|
25
|
+
.toLowerCase();
|
|
26
|
+
if (!code || code.length > AUTH_DENIED_CODE_MAX_LENGTH) {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
return new RegExp(AUTH_DENIED_CODE_PATTERN).test(code) ? code : "";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeAuthDenied(input = null) {
|
|
33
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const code = normalizeAuthDeniedCode(input.code);
|
|
38
|
+
if (!code) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const explicitMessage = String(input.message || "").trim();
|
|
43
|
+
const message = explicitMessage || AUTH_DENIED_DEFAULT_MESSAGES[code] || "This account cannot access this application.";
|
|
44
|
+
|
|
45
|
+
return Object.freeze({
|
|
46
|
+
code,
|
|
47
|
+
message: message.slice(0, AUTH_DENIED_MESSAGE_MAX_LENGTH)
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveAuthDeniedLoginMessage(input = null) {
|
|
52
|
+
const authDenied = normalizeAuthDenied(input);
|
|
53
|
+
if (!authDenied) {
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return AUTH_DENIED_LOGIN_MESSAGES[authDenied.code] || authDenied.message;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
AUTH_DENIED_CODE_MAX_LENGTH,
|
|
62
|
+
AUTH_DENIED_CODE_PATTERN,
|
|
63
|
+
AUTH_DENIED_CODES,
|
|
64
|
+
AUTH_DENIED_DEFAULT_MESSAGES,
|
|
65
|
+
AUTH_DENIED_LOGIN_MESSAGES,
|
|
66
|
+
AUTH_DENIED_MESSAGE_MAX_LENGTH,
|
|
67
|
+
normalizeAuthDenied,
|
|
68
|
+
normalizeAuthDeniedCode,
|
|
69
|
+
resolveAuthDeniedLoginMessage
|
|
70
|
+
};
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { createSchema } from "json-rest-schema";
|
|
2
2
|
import { deepFreeze } from "@jskit-ai/kernel/shared/support/deepFreeze";
|
|
3
|
+
import {
|
|
4
|
+
AUTH_DENIED_CODE_MAX_LENGTH,
|
|
5
|
+
AUTH_DENIED_CODE_PATTERN,
|
|
6
|
+
AUTH_DENIED_MESSAGE_MAX_LENGTH
|
|
7
|
+
} from "../authDenied.js";
|
|
3
8
|
import {
|
|
4
9
|
AUTH_ACCESS_TOKEN_MAX_LENGTH,
|
|
5
10
|
AUTH_EMAIL_MAX_LENGTH,
|
|
@@ -209,6 +214,22 @@ const oauthProviderCatalogEntryOutputValidator = deepFreeze({
|
|
|
209
214
|
mode: "replace"
|
|
210
215
|
});
|
|
211
216
|
|
|
217
|
+
const authDeniedOutputSchema = createSchema({
|
|
218
|
+
code: {
|
|
219
|
+
type: "string",
|
|
220
|
+
required: true,
|
|
221
|
+
minLength: 2,
|
|
222
|
+
maxLength: AUTH_DENIED_CODE_MAX_LENGTH,
|
|
223
|
+
pattern: AUTH_DENIED_CODE_PATTERN
|
|
224
|
+
},
|
|
225
|
+
message: {
|
|
226
|
+
type: "string",
|
|
227
|
+
required: true,
|
|
228
|
+
minLength: 1,
|
|
229
|
+
maxLength: AUTH_DENIED_MESSAGE_MAX_LENGTH
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
212
233
|
const sessionOutputSchema = createSchema({
|
|
213
234
|
authenticated: { type: "boolean", required: true },
|
|
214
235
|
username: { type: "string", required: false, minLength: 1, maxLength: 120 },
|
|
@@ -222,6 +243,11 @@ const sessionOutputSchema = createSchema({
|
|
|
222
243
|
maxLength: 200
|
|
223
244
|
}
|
|
224
245
|
},
|
|
246
|
+
authDenied: {
|
|
247
|
+
type: "object",
|
|
248
|
+
required: false,
|
|
249
|
+
schema: authDeniedOutputSchema
|
|
250
|
+
},
|
|
225
251
|
csrfToken: { type: "string", required: true, minLength: 1 },
|
|
226
252
|
oauthProviders: {
|
|
227
253
|
type: "array",
|
|
@@ -306,6 +332,7 @@ export {
|
|
|
306
332
|
devLoginAsOutputValidator,
|
|
307
333
|
logoutOutputValidator,
|
|
308
334
|
oauthProviderCatalogEntryOutputValidator,
|
|
335
|
+
authDeniedOutputSchema,
|
|
309
336
|
sessionOutputValidator,
|
|
310
337
|
sessionUnavailableOutputValidator,
|
|
311
338
|
createCommandMessages
|
package/src/shared/index.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
export { createApi } from "./authApi.js";
|
|
2
2
|
export { runAuthSignOutFlow } from "./signOutFlow.js";
|
|
3
|
+
export {
|
|
4
|
+
AUTH_DENIED_CODES,
|
|
5
|
+
AUTH_DENIED_DEFAULT_MESSAGES,
|
|
6
|
+
AUTH_DENIED_LOGIN_MESSAGES,
|
|
7
|
+
normalizeAuthDenied,
|
|
8
|
+
resolveAuthDeniedLoginMessage
|
|
9
|
+
} from "./authDenied.js";
|
|
3
10
|
export { AUTH_PATHS, buildAuthOauthStartPath } from "./authPaths.js";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import {
|
|
4
|
+
AUTH_DENIED_CODES,
|
|
5
|
+
normalizeAuthDenied,
|
|
6
|
+
resolveAuthDeniedLoginMessage
|
|
7
|
+
} from "../src/shared/authDenied.js";
|
|
8
|
+
|
|
9
|
+
test("normalizeAuthDenied preserves stable denial codes and messages", () => {
|
|
10
|
+
assert.deepEqual(
|
|
11
|
+
normalizeAuthDenied({
|
|
12
|
+
code: "NOT_ALLOWLISTED",
|
|
13
|
+
message: " Custom denial. "
|
|
14
|
+
}),
|
|
15
|
+
{
|
|
16
|
+
code: AUTH_DENIED_CODES.NOT_ALLOWLISTED,
|
|
17
|
+
message: "Custom denial."
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
assert.deepEqual(
|
|
22
|
+
normalizeAuthDenied({
|
|
23
|
+
code: AUTH_DENIED_CODES.BLOCKED
|
|
24
|
+
}),
|
|
25
|
+
{
|
|
26
|
+
code: AUTH_DENIED_CODES.BLOCKED,
|
|
27
|
+
message: "This account has been blocked from accessing this application."
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
assert.equal(normalizeAuthDenied({ message: "Missing code" }), null);
|
|
32
|
+
assert.equal(normalizeAuthDenied({ code: "../bad" }), null);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("resolveAuthDeniedLoginMessage maps default denial reasons to post-login messages", () => {
|
|
36
|
+
assert.equal(
|
|
37
|
+
resolveAuthDeniedLoginMessage({ code: AUTH_DENIED_CODES.NOT_ALLOWLISTED }),
|
|
38
|
+
"Sign-in succeeded, but this account is not allowed to access this application."
|
|
39
|
+
);
|
|
40
|
+
assert.equal(
|
|
41
|
+
resolveAuthDeniedLoginMessage({ code: AUTH_DENIED_CODES.BLOCKED }),
|
|
42
|
+
"Sign-in succeeded, but this account has been blocked from accessing this application."
|
|
43
|
+
);
|
|
44
|
+
assert.equal(resolveAuthDeniedLoginMessage(null), "");
|
|
45
|
+
});
|
|
@@ -104,6 +104,9 @@ test("auth session and oauth start commands expose explicit nested response sche
|
|
|
104
104
|
assert.equal(sessionResponseSchema.properties.oauthProviders.items["x-json-rest-schema"]?.castType, "object");
|
|
105
105
|
assert.equal(Array.isArray(sessionResponseSchema.properties.oauthProviders.items.allOf), true);
|
|
106
106
|
assert.match(sessionResponseSchema.properties.oauthProviders.items.allOf[0]?.$ref || "", /^#\/definitions\//);
|
|
107
|
+
assert.equal(sessionResponseSchema.properties.authDenied["x-json-rest-schema"]?.castType, "object");
|
|
108
|
+
assert.equal(Array.isArray(sessionResponseSchema.properties.authDenied.allOf), true);
|
|
109
|
+
assert.match(sessionResponseSchema.properties.authDenied.allOf[0]?.$ref || "", /^#\/definitions\//);
|
|
107
110
|
assert.equal(Array.isArray(sessionUnavailableSchema.properties.oauthDefaultProvider.anyOf), true);
|
|
108
111
|
assert.equal(oauthStartResponseSchema.properties.provider.type, "string");
|
|
109
112
|
assert.equal(oauthStartResponseSchema.properties.returnTo.type, "string");
|