@jskit-ai/auth-core 0.1.157 → 0.1.158
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.json +30 -45
- package/src/client/index.js +0 -1
- package/src/client/providers/AccessCoreClientProvider.js +11 -12
- package/src/server/actions/auth.contributor.js +40 -37
- package/src/server/authExtensions.js +155 -0
- package/src/server/authPolicyContextResolverRegistry.js +9 -120
- package/src/server/authServiceDecoratorRegistry.js +9 -64
- package/src/server/deferredAuthService.js +32 -0
- package/src/server/lib/actionContextContributor.js +2 -1
- package/src/server/providers/{AccessCoreServiceProvider.js → AuthAccessProvider.js} +11 -14
- package/src/server/providers/AuthExtensionsProvider.js +14 -0
- package/src/server/providers/AuthFeature.js +15 -0
- package/src/server/providers/AuthPolicyProvider.js +79 -0
- package/test/actionContextContributor.test.js +14 -6
- package/test/authActions.test.js +60 -237
- package/test/authPolicyContextResolverRegistry.test.js +9 -67
- package/test/authServiceDecoratorRegistry.test.js +21 -47
- package/test/providerRuntime.test.js +90 -141
- package/src/client/providers/FastifyAuthPolicyClientProvider.js +0 -13
- package/src/server/providers/AuthActionsServiceProvider.js +0 -76
- package/src/server/providers/FastifyAuthPolicyServiceProvider.js +0 -108
- package/src/server/services/authSessionEventsService.js +0 -29
|
@@ -1,75 +1,21 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
|
-
import {
|
|
3
|
+
import { createAuthExtensions } from "../src/server/authExtensions.js";
|
|
4
4
|
import {
|
|
5
|
-
AUTH_POLICY_CONTEXT_RESOLVER_TAG,
|
|
6
|
-
composeAuthPolicyContextResolvers,
|
|
7
5
|
registerAuthPolicyContextResolver,
|
|
8
|
-
resolveAuthPolicyContextResolvers,
|
|
9
6
|
resolveComposedAuthPolicyContextResolver
|
|
10
7
|
} from "../src/server/authPolicyContextResolverRegistry.js";
|
|
11
8
|
|
|
12
|
-
test("auth policy context
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
registerAuthPolicyContextResolver(app, "test.auth.policy.context.permissions", () => ({
|
|
9
|
+
test("auth.extensions composes policy context resolvers in order", async () => {
|
|
10
|
+
const extensions = createAuthExtensions();
|
|
11
|
+
registerAuthPolicyContextResolver(extensions, {
|
|
16
12
|
resolverId: "permissions",
|
|
17
13
|
order: 20,
|
|
18
14
|
async resolveAuthPolicyContext() {
|
|
19
|
-
return {
|
|
20
|
-
permissions: ["alpha.read"]
|
|
21
|
-
};
|
|
15
|
+
return { permissions: ["alpha.read"] };
|
|
22
16
|
}
|
|
23
|
-
}));
|
|
24
|
-
|
|
25
|
-
registerAuthPolicyContextResolver(app, "test.auth.policy.context.workspace", () => ({
|
|
26
|
-
resolverId: "workspace",
|
|
27
|
-
order: 10,
|
|
28
|
-
async resolveAuthPolicyContext() {
|
|
29
|
-
return {
|
|
30
|
-
workspace: { id: "11" },
|
|
31
|
-
membership: { roleSid: "member" },
|
|
32
|
-
permissions: ["workspace.read"]
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
|
-
const resolvers = resolveAuthPolicyContextResolvers(app);
|
|
38
|
-
assert.deepEqual(
|
|
39
|
-
resolvers.map((entry) => entry.resolverId),
|
|
40
|
-
["workspace", "permissions"]
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const resolveContext = composeAuthPolicyContextResolvers(resolvers);
|
|
44
|
-
const context = await resolveContext({
|
|
45
|
-
actor: { id: "7" }
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
assert.deepEqual(context, {
|
|
49
|
-
workspace: { id: "11" },
|
|
50
|
-
membership: { roleSid: "member" },
|
|
51
|
-
permissions: ["workspace.read", "alpha.read"]
|
|
52
17
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
test("auth policy context resolver registry exports canonical tag", () => {
|
|
56
|
-
assert.equal(AUTH_POLICY_CONTEXT_RESOLVER_TAG, "jskit.auth.policy.context.resolvers");
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("auth policy context resolver registry resolves composed resolver directly from scope", async () => {
|
|
60
|
-
const app = createApplication();
|
|
61
|
-
|
|
62
|
-
registerAuthPolicyContextResolver(app, "test.auth.policy.context.permissions", () => ({
|
|
63
|
-
resolverId: "permissions",
|
|
64
|
-
order: 20,
|
|
65
|
-
async resolveAuthPolicyContext() {
|
|
66
|
-
return {
|
|
67
|
-
permissions: ["alpha.read"]
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
}));
|
|
71
|
-
|
|
72
|
-
registerAuthPolicyContextResolver(app, "test.auth.policy.context.workspace", () => ({
|
|
18
|
+
registerAuthPolicyContextResolver(extensions, {
|
|
73
19
|
resolverId: "workspace",
|
|
74
20
|
order: 10,
|
|
75
21
|
async resolveAuthPolicyContext() {
|
|
@@ -79,16 +25,12 @@ test("auth policy context resolver registry resolves composed resolver directly
|
|
|
79
25
|
permissions: ["workspace.read"]
|
|
80
26
|
};
|
|
81
27
|
}
|
|
82
|
-
}));
|
|
83
|
-
|
|
84
|
-
const resolveContext = resolveComposedAuthPolicyContextResolver(app);
|
|
85
|
-
const context = await resolveContext({
|
|
86
|
-
actor: { id: "7" }
|
|
87
28
|
});
|
|
88
|
-
|
|
89
|
-
assert.deepEqual(
|
|
29
|
+
const resolveContext = resolveComposedAuthPolicyContextResolver(extensions);
|
|
30
|
+
assert.deepEqual(await resolveContext({ actor: { id: "7" } }), {
|
|
90
31
|
workspace: { id: "11" },
|
|
91
32
|
membership: { roleSid: "member" },
|
|
92
33
|
permissions: ["workspace.read", "alpha.read"]
|
|
93
34
|
});
|
|
35
|
+
assert.deepEqual(extensions.diagnostics().policyContextResolverIds, ["workspace", "permissions"]);
|
|
94
36
|
});
|
|
@@ -1,65 +1,39 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
AUTH_SERVICE_DECORATOR_TAG,
|
|
6
|
-
applyAuthServiceDecorators,
|
|
7
|
-
registerAuthServiceDecorator,
|
|
8
|
-
resolveAuthServiceDecorators
|
|
9
|
-
} from "../src/server/authServiceDecoratorRegistry.js";
|
|
3
|
+
import { createAuthExtensions } from "../src/server/authExtensions.js";
|
|
4
|
+
import { applyAuthServiceDecorators, registerAuthServiceDecorator } from "../src/server/authServiceDecoratorRegistry.js";
|
|
10
5
|
|
|
11
|
-
test("auth service
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
registerAuthServiceDecorator(app, "test.auth.decorator.zeta", () => ({
|
|
6
|
+
test("auth.extensions applies service decorators in declared order", () => {
|
|
7
|
+
const extensions = createAuthExtensions();
|
|
8
|
+
registerAuthServiceDecorator(extensions, {
|
|
15
9
|
decoratorId: "zeta",
|
|
16
10
|
order: 50,
|
|
17
11
|
decorateAuthService(service) {
|
|
18
|
-
return {
|
|
19
|
-
...service,
|
|
20
|
-
trace: [...service.trace, "zeta"]
|
|
21
|
-
};
|
|
12
|
+
return { ...service, trace: [...service.trace, "zeta"] };
|
|
22
13
|
}
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
registerAuthServiceDecorator(app, "test.auth.decorator.alpha", () => ({
|
|
14
|
+
});
|
|
15
|
+
registerAuthServiceDecorator(extensions, {
|
|
26
16
|
decoratorId: "alpha",
|
|
27
17
|
order: 10,
|
|
28
18
|
decorateAuthService(service) {
|
|
29
|
-
return {
|
|
30
|
-
...service,
|
|
31
|
-
trace: [...service.trace, "alpha"]
|
|
32
|
-
};
|
|
19
|
+
return { ...service, trace: [...service.trace, "alpha"] };
|
|
33
20
|
}
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
assert.equal(decorators.length, 2);
|
|
38
|
-
assert.deepEqual(
|
|
39
|
-
decorators.map((entry) => entry.decoratorId),
|
|
40
|
-
["alpha", "zeta"]
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const decorated = applyAuthServiceDecorators(app, { trace: [] });
|
|
44
|
-
assert.deepEqual(decorated.trace, ["alpha", "zeta"]);
|
|
21
|
+
});
|
|
22
|
+
assert.deepEqual(applyAuthServiceDecorators(extensions, { trace: [] }).trace, ["alpha", "zeta"]);
|
|
23
|
+
assert.deepEqual(extensions.diagnostics().serviceDecoratorIds, ["alpha", "zeta"]);
|
|
45
24
|
});
|
|
46
25
|
|
|
47
|
-
test("auth
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
registerAuthServiceDecorator(app, "test.auth.decorator.invalid", () => ({
|
|
26
|
+
test("auth.extensions rejects invalid results and late registration", () => {
|
|
27
|
+
const extensions = createAuthExtensions();
|
|
28
|
+
registerAuthServiceDecorator(extensions, {
|
|
51
29
|
decoratorId: "invalid",
|
|
52
30
|
decorateAuthService() {
|
|
53
31
|
return null;
|
|
54
32
|
}
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
assert.throws(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("auth service decorator registry exports canonical tag", () => {
|
|
64
|
-
assert.equal(AUTH_SERVICE_DECORATOR_TAG, "jskit.auth.service.decorators");
|
|
33
|
+
});
|
|
34
|
+
assert.throws(() => applyAuthServiceDecorators(extensions, {}), /must return an auth service object/);
|
|
35
|
+
assert.throws(() => registerAuthServiceDecorator(extensions, {
|
|
36
|
+
decoratorId: "late",
|
|
37
|
+
decorateAuthService(service) { return service; }
|
|
38
|
+
}), /registration is closed/);
|
|
65
39
|
});
|
|
@@ -1,122 +1,94 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { AuthActionsServiceProvider } from "../src/server/providers/AuthActionsServiceProvider.js";
|
|
7
|
-
import { FastifyAuthPolicyServiceProvider } from "../src/server/providers/FastifyAuthPolicyServiceProvider.js";
|
|
8
|
-
import { AUTH_POLICY_CONTEXT_RESOLVER_TAG } from "../src/server/authPolicyContextResolverRegistry.js";
|
|
3
|
+
import { createActionProvider } from "@jskit-ai/kernel/server/actions";
|
|
4
|
+
import { createCapabilityRuntime, defineProvider } from "@jskit-ai/kernel/shared/capabilities";
|
|
5
|
+
import { HttpProvider } from "@jskit-ai/kernel/server/http";
|
|
9
6
|
import { createFakeFastifyPolicyRuntime } from "../../../tooling/testUtils/fakeFastify.mjs";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
]
|
|
29
|
-
]);
|
|
30
|
-
|
|
31
|
-
const app = {
|
|
32
|
-
has(token) {
|
|
33
|
-
return bag.has(token);
|
|
34
|
-
},
|
|
35
|
-
make(token) {
|
|
36
|
-
if (!bag.has(token)) {
|
|
37
|
-
throw new Error(`Missing token ${String(token)}`);
|
|
38
|
-
}
|
|
39
|
-
return bag.get(token);
|
|
40
|
-
}
|
|
7
|
+
import { AuthExtensionsProvider } from "../src/server/providers/AuthExtensionsProvider.js";
|
|
8
|
+
import { AuthFeature } from "../src/server/providers/AuthFeature.js";
|
|
9
|
+
import { AuthPolicyProvider } from "../src/server/providers/AuthPolicyProvider.js";
|
|
10
|
+
|
|
11
|
+
function createFastify() {
|
|
12
|
+
const fixture = createFakeFastifyPolicyRuntime();
|
|
13
|
+
fixture.fastify.route = () => {};
|
|
14
|
+
fixture.fastify.setErrorHandler = () => {};
|
|
15
|
+
return fixture;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function runtimeInputs(fastify) {
|
|
19
|
+
return {
|
|
20
|
+
"runtime.env": { NODE_ENV: "test" },
|
|
21
|
+
"runtime.fastify": fastify
|
|
41
22
|
};
|
|
23
|
+
}
|
|
42
24
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
25
|
+
test("AuthPolicyProvider installs Fastify policy and explicit action/visibility contributors", async () => {
|
|
26
|
+
const { fastify, state } = createFastify();
|
|
27
|
+
const runtime = createCapabilityRuntime({
|
|
28
|
+
inputs: runtimeInputs(fastify),
|
|
29
|
+
providers: [
|
|
30
|
+
createActionProvider(),
|
|
31
|
+
HttpProvider,
|
|
32
|
+
AuthExtensionsProvider,
|
|
33
|
+
AuthFeature,
|
|
34
|
+
AuthPolicyProvider
|
|
35
|
+
]
|
|
36
|
+
});
|
|
37
|
+
await runtime.start();
|
|
47
38
|
assert.ok(state.requestDecorators.has("user"));
|
|
48
39
|
assert.ok(state.requestDecorators.has("workspace"));
|
|
49
40
|
assert.ok(state.requestDecorators.has("membership"));
|
|
50
41
|
assert.ok(state.requestDecorators.has("permissions"));
|
|
51
42
|
assert.equal(typeof state.preHandler, "function");
|
|
52
43
|
assert.ok(state.registeredPlugins.length >= 3);
|
|
44
|
+
assert.ok(runtime.diagnostics().capabilityIds.includes("auth.policy"));
|
|
53
45
|
});
|
|
54
46
|
|
|
55
|
-
test("
|
|
56
|
-
const { fastify, state } =
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
47
|
+
test("AuthPolicyProvider resolves registered workspace context without a service container", async () => {
|
|
48
|
+
const { fastify, state } = createFastify();
|
|
49
|
+
const AuthService = defineProvider({
|
|
50
|
+
id: "test.auth.service",
|
|
51
|
+
provides: { service: "auth.service" },
|
|
52
|
+
setup() {
|
|
53
|
+
return {
|
|
54
|
+
service: {
|
|
55
|
+
async authenticateRequest() {
|
|
56
|
+
return { authenticated: true, actor: { id: 7 }, transientFailure: false };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const WorkspaceContext = defineProvider({
|
|
63
|
+
id: "test.workspace-auth-context",
|
|
64
|
+
requires: { extensions: "auth.extensions" },
|
|
65
|
+
setup({ extensions }) {
|
|
66
|
+
extensions.registerPolicyContextResolver({
|
|
67
|
+
resolverId: "workspace",
|
|
68
|
+
async resolveAuthPolicyContext({ actor, request }) {
|
|
67
69
|
return {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
workspace: { id: 11, slug: String(request?.params?.workspaceSlug || "").toLowerCase() },
|
|
71
|
+
membership: { roleSid: "member" },
|
|
72
|
+
permissions: actor?.id === 7 ? ["projects.read"] : []
|
|
71
73
|
};
|
|
72
74
|
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
]);
|
|
76
|
-
|
|
77
|
-
const app = {
|
|
78
|
-
has(token) {
|
|
79
|
-
return bag.has(token);
|
|
80
|
-
},
|
|
81
|
-
make(token) {
|
|
82
|
-
makeCalls.push(String(token));
|
|
83
|
-
if (!bag.has(token)) {
|
|
84
|
-
throw new Error(`Missing token ${String(token)}`);
|
|
85
|
-
}
|
|
86
|
-
return bag.get(token);
|
|
87
|
-
},
|
|
88
|
-
resolveTag(tag) {
|
|
89
|
-
resolveTagCalls.push(String(tag));
|
|
90
|
-
if (tag !== AUTH_POLICY_CONTEXT_RESOLVER_TAG) {
|
|
91
|
-
return [];
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return [
|
|
95
|
-
{
|
|
96
|
-
resolverId: "workspace",
|
|
97
|
-
order: 10,
|
|
98
|
-
async resolveAuthPolicyContext({ actor, request }) {
|
|
99
|
-
return {
|
|
100
|
-
workspace: { id: 11, slug: String(request?.params?.workspaceSlug || "").toLowerCase() },
|
|
101
|
-
membership: { roleSid: "member" },
|
|
102
|
-
permissions: actor?.id === 7 ? ["projects.read"] : []
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
async () => ({
|
|
107
|
-
permissions: ["settings.manage"]
|
|
108
|
-
})
|
|
109
|
-
];
|
|
75
|
+
});
|
|
76
|
+
return {};
|
|
110
77
|
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
78
|
+
});
|
|
79
|
+
const runtime = createCapabilityRuntime({
|
|
80
|
+
inputs: runtimeInputs(fastify),
|
|
81
|
+
providers: [
|
|
82
|
+
createActionProvider(),
|
|
83
|
+
HttpProvider,
|
|
84
|
+
AuthExtensionsProvider,
|
|
85
|
+
AuthService,
|
|
86
|
+
WorkspaceContext,
|
|
87
|
+
AuthFeature,
|
|
88
|
+
AuthPolicyProvider
|
|
89
|
+
]
|
|
90
|
+
});
|
|
91
|
+
await runtime.start();
|
|
120
92
|
const request = {
|
|
121
93
|
method: "GET",
|
|
122
94
|
raw: { url: "/api/w/acme/projects" },
|
|
@@ -129,51 +101,28 @@ test("FastifyAuthPolicyServiceProvider wires optional auth policy context resolv
|
|
|
129
101
|
}
|
|
130
102
|
}
|
|
131
103
|
};
|
|
132
|
-
|
|
133
104
|
await state.preHandler(request, {});
|
|
134
|
-
assert.
|
|
135
|
-
assert.deepEqual(
|
|
136
|
-
assert.
|
|
137
|
-
assert.equal(request.workspace?.slug, "acme");
|
|
138
|
-
assert.equal(request.membership?.roleSid, "member");
|
|
139
|
-
assert.deepEqual(request.permissions, ["settings.manage", "projects.read"]);
|
|
105
|
+
assert.deepEqual(request.workspace, { id: 11, slug: "acme" });
|
|
106
|
+
assert.deepEqual(request.membership, { roleSid: "member" });
|
|
107
|
+
assert.deepEqual(request.permissions, ["projects.read"]);
|
|
140
108
|
});
|
|
141
109
|
|
|
142
|
-
test("auth
|
|
143
|
-
const { fastify, state } =
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
app.instance("appConfig", {
|
|
147
|
-
surfaceModeAll: "all",
|
|
148
|
-
surfaceDefaultId: "home",
|
|
149
|
-
surfaceDefinitions: {
|
|
150
|
-
home: { id: "home", pagesRoot: "", enabled: true, requiresAuth: false, requiresWorkspace: false }
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
app.instance("jskit.fastify", fastify);
|
|
154
|
-
app.instance("jskit.env", { NODE_ENV: "test" });
|
|
155
|
-
|
|
156
|
-
await app.start({
|
|
110
|
+
test("auth policy denies protected routes when no auth service is installed", async () => {
|
|
111
|
+
const { fastify, state } = createFastify();
|
|
112
|
+
const runtime = createCapabilityRuntime({
|
|
113
|
+
inputs: runtimeInputs(fastify),
|
|
157
114
|
providers: [
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
115
|
+
createActionProvider(),
|
|
116
|
+
HttpProvider,
|
|
117
|
+
AuthExtensionsProvider,
|
|
118
|
+
AuthFeature,
|
|
119
|
+
AuthPolicyProvider
|
|
162
120
|
]
|
|
163
121
|
});
|
|
164
|
-
|
|
165
|
-
|
|
122
|
+
await runtime.start();
|
|
123
|
+
await assert.rejects(() => state.preHandler({
|
|
166
124
|
method: "GET",
|
|
167
125
|
raw: { url: "/api/protected" },
|
|
168
|
-
routeOptions: {
|
|
169
|
-
|
|
170
|
-
authPolicy: "required"
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
await assert.rejects(
|
|
176
|
-
() => state.preHandler(request, {}),
|
|
177
|
-
/Authentication required/
|
|
178
|
-
);
|
|
126
|
+
routeOptions: { config: { authPolicy: "required" } }
|
|
127
|
+
}, {}), /Authentication required/);
|
|
179
128
|
});
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
class FastifyAuthPolicyClientProvider {
|
|
2
|
-
static id = "auth.policy.client";
|
|
3
|
-
|
|
4
|
-
register(app) {
|
|
5
|
-
if (!app || typeof app.singleton !== "function") {
|
|
6
|
-
throw new Error("FastifyAuthPolicyClientProvider requires application singleton().");
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
boot() {}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export { FastifyAuthPolicyClientProvider };
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
2
|
-
import { buildAuthActions } from "../actions/auth.contributor.js";
|
|
3
|
-
import { createAuthSessionEventsService } from "../services/authSessionEventsService.js";
|
|
4
|
-
|
|
5
|
-
class AuthActionsServiceProvider {
|
|
6
|
-
static id = "auth.actions";
|
|
7
|
-
|
|
8
|
-
static startsAfter = ["runtime.actions"];
|
|
9
|
-
|
|
10
|
-
register(app) {
|
|
11
|
-
if (
|
|
12
|
-
!app ||
|
|
13
|
-
typeof app.actions !== "function" ||
|
|
14
|
-
typeof app.has !== "function" ||
|
|
15
|
-
typeof app.service !== "function"
|
|
16
|
-
) {
|
|
17
|
-
throw new Error("AuthActionsServiceProvider requires application actions()/has()/service().");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (!app.has("auth.session.events.service")) {
|
|
21
|
-
app.service(
|
|
22
|
-
"auth.session.events.service",
|
|
23
|
-
() => createAuthSessionEventsService(),
|
|
24
|
-
{
|
|
25
|
-
events: {
|
|
26
|
-
notifySessionChanged: [
|
|
27
|
-
{
|
|
28
|
-
type: "entity.changed",
|
|
29
|
-
source: "auth",
|
|
30
|
-
entity: "session",
|
|
31
|
-
operation: "updated",
|
|
32
|
-
entityId: ({ result }) => result?.id,
|
|
33
|
-
realtime: {
|
|
34
|
-
event: "auth.session.changed",
|
|
35
|
-
audience: "actor_user"
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
type: "entity.changed",
|
|
40
|
-
source: "users",
|
|
41
|
-
entity: "bootstrap",
|
|
42
|
-
operation: "updated",
|
|
43
|
-
entityId: ({ result }) => result?.id,
|
|
44
|
-
realtime: {
|
|
45
|
-
event: "users.bootstrap.changed",
|
|
46
|
-
audience: "actor_user"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
boot(app) {
|
|
57
|
-
// auth-core also owns the request policy used by deliberately public apps.
|
|
58
|
-
// Only contribute executable auth actions when one of the provider packages
|
|
59
|
-
// registered the service those actions require.
|
|
60
|
-
if (!app.has("authService")) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
app.actions(
|
|
65
|
-
withActionDefaults(buildAuthActions(), {
|
|
66
|
-
domain: "auth",
|
|
67
|
-
dependencies: {
|
|
68
|
-
authService: "authService",
|
|
69
|
-
authSessionEventsService: "auth.session.events.service"
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export { AuthActionsServiceProvider };
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { registerActionContextContributor } from "@jskit-ai/kernel/server/actions";
|
|
2
|
-
import { registerRouteVisibilityResolver } from "@jskit-ai/kernel/server/http";
|
|
3
|
-
import {
|
|
4
|
-
resolveComposedAuthPolicyContextResolver
|
|
5
|
-
} from "../authPolicyContextResolverRegistry.js";
|
|
6
|
-
import { parseBooleanFlag } from "../booleanFlag.js";
|
|
7
|
-
import { authPolicyPlugin } from "../lib/plugin.js";
|
|
8
|
-
import { createAuthActionContextContributor } from "../lib/actionContextContributor.js";
|
|
9
|
-
import { createAuthRouteVisibilityResolver } from "../lib/routeVisibilityResolver.js";
|
|
10
|
-
|
|
11
|
-
function parseList(value) {
|
|
12
|
-
return String(value || "")
|
|
13
|
-
.split(",")
|
|
14
|
-
.map((entry) => entry.trim())
|
|
15
|
-
.filter(Boolean);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function defaultHasPermission({ permission, permissions = [] } = {}) {
|
|
19
|
-
if (!permission) {
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
return Array.isArray(permissions) ? permissions.includes(permission) : false;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
class FastifyAuthPolicyServiceProvider {
|
|
26
|
-
static id = "auth.policy.fastify";
|
|
27
|
-
|
|
28
|
-
register(app) {
|
|
29
|
-
if (!app || typeof app.has !== "function") {
|
|
30
|
-
throw new Error("FastifyAuthPolicyServiceProvider requires application has().");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
!app.has("auth.policy.actionContextContributor") &&
|
|
35
|
-
typeof app.singleton === "function" &&
|
|
36
|
-
typeof app.tag === "function"
|
|
37
|
-
) {
|
|
38
|
-
registerActionContextContributor(app, "auth.policy.actionContextContributor", () =>
|
|
39
|
-
createAuthActionContextContributor()
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (
|
|
44
|
-
!app.has("auth.policy.routeVisibilityResolver") &&
|
|
45
|
-
typeof app.singleton === "function" &&
|
|
46
|
-
typeof app.tag === "function"
|
|
47
|
-
) {
|
|
48
|
-
registerRouteVisibilityResolver(app, "auth.policy.routeVisibilityResolver", () =>
|
|
49
|
-
createAuthRouteVisibilityResolver()
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async boot(app) {
|
|
55
|
-
if (!app || typeof app.make !== "function" || typeof app.has !== "function") {
|
|
56
|
-
throw new Error("FastifyAuthPolicyServiceProvider requires application make()/has().");
|
|
57
|
-
}
|
|
58
|
-
const env = app.has("jskit.env") ? app.make("jskit.env") : {};
|
|
59
|
-
const fastify = app.make("jskit.fastify");
|
|
60
|
-
|
|
61
|
-
const pluginDeps = {
|
|
62
|
-
resolveActor: async (request) => {
|
|
63
|
-
if (!app.has("authService")) {
|
|
64
|
-
return {
|
|
65
|
-
authenticated: false,
|
|
66
|
-
actor: null,
|
|
67
|
-
transientFailure: false
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
const authService = app.make("authService");
|
|
71
|
-
if (authService && typeof authService.authenticateRequest === "function") {
|
|
72
|
-
return authService.authenticateRequest(request);
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
authenticated: false,
|
|
76
|
-
actor: null,
|
|
77
|
-
transientFailure: false
|
|
78
|
-
};
|
|
79
|
-
},
|
|
80
|
-
hasPermission: defaultHasPermission,
|
|
81
|
-
resolveContext: async (input = {}) => {
|
|
82
|
-
const resolveContext = resolveComposedAuthPolicyContextResolver(app);
|
|
83
|
-
|
|
84
|
-
if (typeof resolveContext !== "function") {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return resolveContext(input);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const plugin = authPolicyPlugin(
|
|
93
|
-
pluginDeps,
|
|
94
|
-
{
|
|
95
|
-
nodeEnv: String(env.NODE_ENV || "development").trim() || "development",
|
|
96
|
-
apiPrefix: String(env.AUTH_API_PREFIX || "/api/").trim() || "/api/",
|
|
97
|
-
unsafeMethods: parseList(env.AUTH_CSRF_UNSAFE_METHODS),
|
|
98
|
-
csrfCookieOpts: {
|
|
99
|
-
secure: parseBooleanFlag(env.AUTH_CSRF_COOKIE_SECURE, false)
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
await plugin(fastify);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export { FastifyAuthPolicyServiceProvider };
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { normalizeOpaqueId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
-
|
|
3
|
-
function resolveActorId(context = {}) {
|
|
4
|
-
return normalizeOpaqueId(
|
|
5
|
-
context?.actor?.id ||
|
|
6
|
-
context?.actor?.appUserId ||
|
|
7
|
-
context?.actor?.providerUserId,
|
|
8
|
-
{ fallback: null }
|
|
9
|
-
);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function createAuthSessionEventsService() {
|
|
13
|
-
async function notifySessionChanged(options = {}) {
|
|
14
|
-
const actorId = resolveActorId(options?.context || {});
|
|
15
|
-
if (!actorId) {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
id: actorId
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return Object.freeze({
|
|
25
|
-
notifySessionChanged
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export { createAuthSessionEventsService };
|