@jskit-ai/auth-core 0.1.99 → 0.1.101

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.
@@ -0,0 +1,171 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import {
4
+ normalizeAuthCapabilities,
5
+ getCapabilityFeature,
6
+ isAuthOperationSupported
7
+ } from "../src/shared/authCapabilities.js";
8
+ import {
9
+ buildSecurityStatusFromAuthMethodsStatus,
10
+ normalizeAuthSecurityStatus
11
+ } from "../src/shared/authSecurityStatus.js";
12
+ import {
13
+ buildLegacyProfileFromActor,
14
+ normalizeAuthActor,
15
+ normalizeAuthResult
16
+ } from "../src/server/authActor.js";
17
+ import {
18
+ AUTH_OPERATION_UNSUPPORTED_CODE,
19
+ createUnsupportedAuthOperationError,
20
+ isUnsupportedAuthOperationError
21
+ } from "../src/server/unsupportedOperation.js";
22
+
23
+ test("normalizeAuthCapabilities returns one provider-neutral feature shape", () => {
24
+ const capabilities = normalizeAuthCapabilities({
25
+ provider: {
26
+ id: "LOCAL",
27
+ label: ""
28
+ },
29
+ features: {
30
+ password: {
31
+ login: true,
32
+ register: true,
33
+ change: true
34
+ },
35
+ passwordRecovery: {
36
+ request: true,
37
+ complete: true,
38
+ delivery: "smtp"
39
+ },
40
+ oauthLogin: {
41
+ enabled: true,
42
+ providers: [
43
+ { id: "Google", label: "Google" },
44
+ "github",
45
+ "bad provider"
46
+ ],
47
+ defaultProvider: "github"
48
+ },
49
+ providerLinking: {
50
+ start: true
51
+ },
52
+ securityStatus: true
53
+ }
54
+ });
55
+
56
+ assert.equal(capabilities.provider.id, "local");
57
+ assert.equal(capabilities.provider.label, "Local");
58
+ assert.equal(capabilities.features.password.login, true);
59
+ assert.equal(capabilities.features.password.methodToggle, false);
60
+ assert.equal(capabilities.features.passwordRecovery.delivery, "smtp");
61
+ assert.deepEqual(capabilities.features.oauthLogin.providers, [
62
+ { id: "google", label: "Google" },
63
+ { id: "github", label: "github" }
64
+ ]);
65
+ assert.equal(capabilities.features.oauthLogin.defaultProvider, "github");
66
+ assert.equal(getCapabilityFeature(capabilities, "providerLinking.start"), true);
67
+ assert.equal(isAuthOperationSupported(capabilities, "startProviderLink"), true);
68
+ assert.equal(isAuthOperationSupported(capabilities, "unlinkProvider"), false);
69
+ assert.equal(isAuthOperationSupported(capabilities, "resetPassword"), true);
70
+ assert.equal(
71
+ isAuthOperationSupported(
72
+ {
73
+ features: {
74
+ password: {
75
+ change: true
76
+ },
77
+ passwordRecovery: {
78
+ complete: false
79
+ }
80
+ }
81
+ },
82
+ "resetPassword"
83
+ ),
84
+ false
85
+ );
86
+ });
87
+
88
+ test("normalizeAuthActor builds the stable actor and legacy profile bridge", () => {
89
+ const actor = normalizeAuthActor({
90
+ authProvider: "supabase",
91
+ authProviderUserSid: "abc-123",
92
+ email: " ADA@example.COM ",
93
+ displayName: "Ada",
94
+ appUserId: 42,
95
+ profileSource: "users"
96
+ });
97
+
98
+ assert.deepEqual(actor, {
99
+ authIdentityId: "supabase:abc-123",
100
+ provider: "supabase",
101
+ providerUserId: "abc-123",
102
+ email: "ada@example.com",
103
+ displayName: "Ada",
104
+ appUserId: "42",
105
+ profileSource: "users"
106
+ });
107
+ assert.deepEqual(buildLegacyProfileFromActor(actor), {
108
+ id: "42",
109
+ email: "ada@example.com",
110
+ displayName: "Ada",
111
+ authProvider: "supabase",
112
+ authProviderUserSid: "abc-123"
113
+ });
114
+ assert.deepEqual(normalizeAuthResult({ authenticated: true, actor }).profile, buildLegacyProfileFromActor(actor));
115
+ });
116
+
117
+ test("normalizeAuthActor preserves opaque app user ids", () => {
118
+ const actor = normalizeAuthActor({
119
+ provider: "local",
120
+ providerUserId: "provider-user-1",
121
+ email: "local@example.com",
122
+ appUserId: "app-user-1",
123
+ profileSource: "users"
124
+ });
125
+
126
+ assert.equal(actor.appUserId, "app-user-1");
127
+ assert.equal(buildLegacyProfileFromActor(actor).id, "app-user-1");
128
+ });
129
+
130
+ test("normalizeAuthSecurityStatus emits policy and legacy authPolicy aliases", () => {
131
+ const status = buildSecurityStatusFromAuthMethodsStatus(
132
+ {
133
+ methods: [
134
+ {
135
+ id: "password",
136
+ kind: "password",
137
+ configured: true,
138
+ enabled: true,
139
+ canDisable: false
140
+ }
141
+ ],
142
+ minimumEnabledMethods: 1,
143
+ enabledMethodsCount: 1
144
+ },
145
+ {
146
+ actions: {
147
+ changePassword: true,
148
+ signOutOtherSessions: true
149
+ }
150
+ }
151
+ );
152
+
153
+ assert.deepEqual(status.policy, {
154
+ minimumEnabledMethods: 1,
155
+ enabledMethodsCount: 1
156
+ });
157
+ assert.equal(status.authPolicy, status.policy);
158
+ assert.equal(status.actions.changePassword, true);
159
+ assert.equal(status.actions.linkProvider, false);
160
+ assert.deepEqual(normalizeAuthSecurityStatus(status), status);
161
+ });
162
+
163
+ test("unsupported auth operation errors are stable AppErrors", () => {
164
+ const error = createUnsupportedAuthOperationError("oauthStart");
165
+ assert.equal(error.status, 501);
166
+ assert.equal(error.code, AUTH_OPERATION_UNSUPPORTED_CODE);
167
+ assert.deepEqual(error.details, {
168
+ operation: "oauthStart"
169
+ });
170
+ assert.equal(isUnsupportedAuthOperationError(error), true);
171
+ });
@@ -104,9 +104,14 @@ 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.authCapabilities["x-json-rest-schema"]?.castType, "object");
108
+ assert.equal(Array.isArray(sessionResponseSchema.properties.authCapabilities.allOf), true);
109
+ assert.match(sessionResponseSchema.properties.authCapabilities.allOf[0]?.$ref || "", /^#\/definitions\//);
107
110
  assert.equal(sessionResponseSchema.properties.authDenied["x-json-rest-schema"]?.castType, "object");
108
111
  assert.equal(Array.isArray(sessionResponseSchema.properties.authDenied.allOf), true);
109
112
  assert.match(sessionResponseSchema.properties.authDenied.allOf[0]?.$ref || "", /^#\/definitions\//);
113
+ assert.equal(sessionUnavailableSchema.properties.authCapabilities["x-json-rest-schema"]?.castType, "object");
114
+ assert.equal(Array.isArray(sessionUnavailableSchema.properties.authCapabilities.allOf), true);
110
115
  assert.equal(Array.isArray(sessionUnavailableSchema.properties.oauthDefaultProvider.anyOf), true);
111
116
  assert.equal(oauthStartResponseSchema.properties.provider.type, "string");
112
117
  assert.equal(oauthStartResponseSchema.properties.returnTo.type, "string");
@@ -1,5 +1,9 @@
1
1
  import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
+ import { createApplication } from "@jskit-ai/kernel/_testable";
4
+ import { ActionRuntimeServiceProvider } from "@jskit-ai/kernel/server/actions";
5
+ import { AccessCoreServiceProvider } from "../src/server/providers/AccessCoreServiceProvider.js";
6
+ import { AuthActionsServiceProvider } from "../src/server/providers/AuthActionsServiceProvider.js";
3
7
  import { FastifyAuthPolicyServiceProvider } from "../src/server/providers/FastifyAuthPolicyServiceProvider.js";
4
8
  import { AUTH_POLICY_CONTEXT_RESOLVER_TAG } from "../src/server/authPolicyContextResolverRegistry.js";
5
9
  import { createFakeFastifyPolicyRuntime } from "../../../tooling/testUtils/fakeFastify.mjs";
@@ -134,3 +138,42 @@ test("FastifyAuthPolicyServiceProvider wires optional auth policy context resolv
134
138
  assert.equal(request.membership?.roleSid, "member");
135
139
  assert.deepEqual(request.permissions, ["settings.manage", "projects.read"]);
136
140
  });
141
+
142
+ test("auth-core providers boot without a selected provider and deny protected API requests", async () => {
143
+ const { fastify, state } = createFakeFastifyPolicyRuntime();
144
+ const app = createApplication();
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({
157
+ providers: [
158
+ ActionRuntimeServiceProvider,
159
+ AccessCoreServiceProvider,
160
+ AuthActionsServiceProvider,
161
+ FastifyAuthPolicyServiceProvider
162
+ ]
163
+ });
164
+
165
+ const request = {
166
+ method: "GET",
167
+ raw: { url: "/api/protected" },
168
+ routeOptions: {
169
+ config: {
170
+ authPolicy: "required"
171
+ }
172
+ }
173
+ };
174
+
175
+ await assert.rejects(
176
+ () => state.preHandler(request, {}),
177
+ /Authentication required/
178
+ );
179
+ });