@jskit-ai/auth-provider-supabase-core 0.1.98 → 0.1.100
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 +6 -6
- package/package.json +3 -3
- package/src/server/lib/accountFlows.js +6 -2
- package/src/server/lib/actions/auth.contributor.js +3 -279
- package/src/server/lib/authMethodStatus.js +3 -23
- package/src/server/lib/index.js +1 -1
- package/src/server/lib/passwordSecurityFlows.js +14 -2
- package/src/server/lib/service.js +159 -29
- package/src/server/providers/AuthSupabaseServiceProvider.js +92 -126
- package/test/auth.provider.supabase.test.js +202 -51
- package/test/providerRuntime.test.js +146 -8
- package/src/server/lib/authSessionEventsService.js +0 -20
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import * as authProviderSupabase from "../src/server/lib/index.js";
|
|
4
|
+
import { createAccountFlows } from "../src/server/lib/accountFlows.js";
|
|
5
|
+
import { createPasswordSecurityFlows } from "../src/server/lib/passwordSecurityFlows.js";
|
|
4
6
|
|
|
5
7
|
test("auth.provider.supabase exports required symbols", () => {
|
|
6
8
|
assert.equal(typeof authProviderSupabase.createService, "function");
|
|
@@ -52,8 +54,12 @@ test("clearSessionCookies clears root and API path session cookie variants", ()
|
|
|
52
54
|
[
|
|
53
55
|
{ name: "sb_access_token", path: "/", maxAge: 0 },
|
|
54
56
|
{ name: "sb_refresh_token", path: "/", maxAge: 0 },
|
|
57
|
+
{ name: "sb_recovery_access_token", path: "/", maxAge: 0 },
|
|
58
|
+
{ name: "sb_recovery_refresh_token", path: "/", maxAge: 0 },
|
|
55
59
|
{ name: "sb_access_token", path: "/api", maxAge: 0 },
|
|
56
|
-
{ name: "sb_refresh_token", path: "/api", maxAge: 0 }
|
|
60
|
+
{ name: "sb_refresh_token", path: "/api", maxAge: 0 },
|
|
61
|
+
{ name: "sb_recovery_access_token", path: "/api", maxAge: 0 },
|
|
62
|
+
{ name: "sb_recovery_refresh_token", path: "/api", maxAge: 0 }
|
|
57
63
|
]
|
|
58
64
|
);
|
|
59
65
|
});
|
|
@@ -74,10 +80,114 @@ test("clearSessionCookies preserves secure cookie clearing in production", () =>
|
|
|
74
80
|
|
|
75
81
|
authService.clearSessionCookies(reply);
|
|
76
82
|
|
|
77
|
-
assert.equal(clearCalls.length,
|
|
83
|
+
assert.equal(clearCalls.length, 8);
|
|
78
84
|
assert.equal(clearCalls.every((call) => call.options.secure === true), true);
|
|
79
85
|
});
|
|
80
86
|
|
|
87
|
+
test("supabase account flow updateDisplayName accepts the core command payload shape", async () => {
|
|
88
|
+
let updatePayload = null;
|
|
89
|
+
const flows = createAccountFlows({
|
|
90
|
+
ensureConfigured() {},
|
|
91
|
+
validationError(errors) {
|
|
92
|
+
return new Error(JSON.stringify(errors));
|
|
93
|
+
},
|
|
94
|
+
getSupabaseClient() {
|
|
95
|
+
return {
|
|
96
|
+
auth: {
|
|
97
|
+
async updateUser(payload) {
|
|
98
|
+
updatePayload = payload;
|
|
99
|
+
return {
|
|
100
|
+
data: {
|
|
101
|
+
user: {
|
|
102
|
+
id: "supabase-user-1",
|
|
103
|
+
email: "ada@example.com",
|
|
104
|
+
user_metadata: {
|
|
105
|
+
display_name: payload.data.display_name
|
|
106
|
+
},
|
|
107
|
+
app_metadata: {}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
async setSessionFromRequestCookies() {
|
|
116
|
+
return {
|
|
117
|
+
data: {
|
|
118
|
+
session: {
|
|
119
|
+
access_token: "access"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
async syncProfileFromSupabaseUser(user) {
|
|
125
|
+
return {
|
|
126
|
+
id: "profile-1",
|
|
127
|
+
email: user.email,
|
|
128
|
+
displayName: user.user_metadata.display_name,
|
|
129
|
+
authProvider: "supabase",
|
|
130
|
+
authProviderUserSid: user.id
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
mapProfileUpdateError(error) {
|
|
134
|
+
return error || new Error("profile update failed");
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const result = await flows.updateDisplayName(
|
|
139
|
+
{
|
|
140
|
+
cookies: {}
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
displayName: "Ada Lovelace"
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
assert.deepEqual(updatePayload, {
|
|
148
|
+
data: {
|
|
149
|
+
display_name: "Ada Lovelace"
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
assert.equal(result.profile.displayName, "Ada Lovelace");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("recovery sessions use recovery cookies and do not authenticate the app", async () => {
|
|
156
|
+
const authService = createServiceFixture();
|
|
157
|
+
const cookies = {};
|
|
158
|
+
const cookieOptions = {};
|
|
159
|
+
const reply = {
|
|
160
|
+
setCookie(name, value, options) {
|
|
161
|
+
cookies[name] = value;
|
|
162
|
+
cookieOptions[name] = options;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
authService.writeSessionCookies(reply, {
|
|
167
|
+
access_token: "recovery-access",
|
|
168
|
+
refresh_token: "recovery-refresh",
|
|
169
|
+
expires_in: 300,
|
|
170
|
+
purpose: "recovery"
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
assert.deepEqual(cookies, {
|
|
174
|
+
sb_recovery_access_token: "recovery-access",
|
|
175
|
+
sb_recovery_refresh_token: "recovery-refresh"
|
|
176
|
+
});
|
|
177
|
+
assert.equal(cookieOptions.sb_recovery_access_token.maxAge, 300);
|
|
178
|
+
assert.equal(cookieOptions.sb_recovery_refresh_token.maxAge, 300);
|
|
179
|
+
assert.equal(authService.hasAccessTokenCookie({ cookies }), true);
|
|
180
|
+
assert.equal(authService.hasSessionCookie({ cookies }), true);
|
|
181
|
+
|
|
182
|
+
const authResult = await authService.authenticateRequest({ cookies });
|
|
183
|
+
assert.deepEqual(authResult, {
|
|
184
|
+
authenticated: false,
|
|
185
|
+
clearSession: false,
|
|
186
|
+
session: null,
|
|
187
|
+
transientFailure: false
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
81
191
|
test("logout is local-only when no session cookies are present", async () => {
|
|
82
192
|
const authService = createServiceFixture();
|
|
83
193
|
|
|
@@ -106,62 +216,103 @@ test("logout is local-only for dev auth cookies", async () => {
|
|
|
106
216
|
});
|
|
107
217
|
});
|
|
108
218
|
|
|
109
|
-
test("
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
{},
|
|
120
|
-
{
|
|
121
|
-
requestMeta: {
|
|
122
|
-
request
|
|
123
|
-
}
|
|
219
|
+
test("supabase password reset consumes the recovery session reader", async () => {
|
|
220
|
+
let normalSessionReads = 0;
|
|
221
|
+
let recoverySessionReads = 0;
|
|
222
|
+
let signOutCalls = 0;
|
|
223
|
+
const flows = createPasswordSecurityFlows({
|
|
224
|
+
ensureConfigured() {},
|
|
225
|
+
validationError(errors) {
|
|
226
|
+
const error = new Error("Validation failed.");
|
|
227
|
+
error.errors = errors;
|
|
228
|
+
return error;
|
|
124
229
|
},
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
230
|
+
getSupabaseClient() {
|
|
231
|
+
return {
|
|
232
|
+
auth: {
|
|
233
|
+
async updateUser(input) {
|
|
234
|
+
assert.deepEqual(input, { password: "new password value" });
|
|
235
|
+
return {
|
|
236
|
+
data: {
|
|
237
|
+
user: {
|
|
238
|
+
id: "supabase-user-1",
|
|
239
|
+
email: "reset@example.com"
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
error: null
|
|
243
|
+
};
|
|
244
|
+
},
|
|
245
|
+
async signOut(options) {
|
|
246
|
+
signOutCalls += 1;
|
|
247
|
+
assert.deepEqual(options, {
|
|
248
|
+
scope: "local"
|
|
249
|
+
});
|
|
250
|
+
return {
|
|
251
|
+
error: null
|
|
252
|
+
};
|
|
253
|
+
}
|
|
136
254
|
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
255
|
+
};
|
|
256
|
+
},
|
|
257
|
+
async setSessionFromRequestCookies() {
|
|
258
|
+
normalSessionReads += 1;
|
|
259
|
+
throw new Error("normal session reader should not be used for reset.");
|
|
260
|
+
},
|
|
261
|
+
async setRecoverySessionFromRequestCookies(_request, { supabaseClient }) {
|
|
262
|
+
recoverySessionReads += 1;
|
|
263
|
+
assert.equal(typeof supabaseClient.auth.updateUser, "function");
|
|
264
|
+
return {
|
|
265
|
+
data: {
|
|
266
|
+
user: {
|
|
267
|
+
id: "supabase-user-1",
|
|
268
|
+
email: "reset@example.com"
|
|
269
|
+
},
|
|
270
|
+
session: {
|
|
271
|
+
access_token: "recovery-access",
|
|
272
|
+
refresh_token: "recovery-refresh"
|
|
273
|
+
}
|
|
144
274
|
}
|
|
145
|
-
}
|
|
275
|
+
};
|
|
276
|
+
},
|
|
277
|
+
async syncProfileFromSupabaseUser(user) {
|
|
278
|
+
return {
|
|
279
|
+
id: "1",
|
|
280
|
+
email: user.email,
|
|
281
|
+
displayName: "Reset Example",
|
|
282
|
+
authProvider: "supabase",
|
|
283
|
+
authProviderUserSid: user.id
|
|
284
|
+
};
|
|
285
|
+
},
|
|
286
|
+
async resolvePasswordSignInPolicyForUserId(userId) {
|
|
287
|
+
assert.equal(userId, "1");
|
|
288
|
+
return {
|
|
289
|
+
passwordSignInEnabled: true
|
|
290
|
+
};
|
|
291
|
+
},
|
|
292
|
+
async setPasswordSetupRequiredForUserId(userId, required) {
|
|
293
|
+
assert.equal(userId, "1");
|
|
294
|
+
assert.equal(required, false);
|
|
295
|
+
},
|
|
296
|
+
mapPasswordUpdateError(error) {
|
|
297
|
+
return error instanceof Error ? error : new Error("Password update failed.");
|
|
146
298
|
}
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
assert.deepEqual(result, {
|
|
150
|
-
ok: true,
|
|
151
|
-
clearSession: true
|
|
152
299
|
});
|
|
153
|
-
|
|
300
|
+
|
|
301
|
+
await flows.resetPassword(
|
|
154
302
|
{
|
|
155
|
-
|
|
156
|
-
|
|
303
|
+
cookies: {
|
|
304
|
+
sb_access_token: "normal-access",
|
|
305
|
+
sb_refresh_token: "normal-refresh",
|
|
306
|
+
sb_recovery_access_token: "recovery-access",
|
|
307
|
+
sb_recovery_refresh_token: "recovery-refresh"
|
|
308
|
+
}
|
|
157
309
|
},
|
|
158
310
|
{
|
|
159
|
-
|
|
160
|
-
context: {
|
|
161
|
-
requestMeta: {
|
|
162
|
-
request
|
|
163
|
-
}
|
|
164
|
-
}
|
|
311
|
+
password: "new password value"
|
|
165
312
|
}
|
|
166
|
-
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
assert.equal(recoverySessionReads, 1);
|
|
316
|
+
assert.equal(normalSessionReads, 0);
|
|
317
|
+
assert.equal(signOutCalls, 1);
|
|
167
318
|
});
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import { createApplication } from "@jskit-ai/kernel/_testable";
|
|
4
|
+
import { AuthActionsServiceProvider } from "@jskit-ai/auth-core/server/providers/AuthActionsServiceProvider";
|
|
4
5
|
import { registerAuthServiceDecorator } from "@jskit-ai/auth-core/server/authServiceDecoratorRegistry";
|
|
5
6
|
import { ActionRuntimeServiceProvider } from "@jskit-ai/kernel/server/actions";
|
|
6
7
|
import { createProviderClass } from "../../kernel/shared/runtime/application.js";
|
|
8
|
+
import { AuthProviderServiceProvider } from "../src/server/providers/AuthProviderServiceProvider.js";
|
|
7
9
|
import { AuthSupabaseServiceProvider } from "../src/server/providers/AuthSupabaseServiceProvider.js";
|
|
8
10
|
|
|
9
11
|
function createAppConfigFixture({ auth = null } = {}) {
|
|
@@ -35,7 +37,7 @@ function isBootFailureWithCause(error, pattern) {
|
|
|
35
37
|
);
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
test("auth supabase provider defaults to
|
|
40
|
+
test("auth supabase provider defaults to provider profile mode and contributes auth actions", async () => {
|
|
39
41
|
const app = createApplication();
|
|
40
42
|
app.instance("appConfig", createAppConfigFixture());
|
|
41
43
|
app.instance("jskit.env", {
|
|
@@ -69,11 +71,53 @@ test("auth supabase provider defaults to users mode and contributes auth actions
|
|
|
69
71
|
});
|
|
70
72
|
|
|
71
73
|
await app.start({
|
|
72
|
-
providers: [
|
|
74
|
+
providers: [
|
|
75
|
+
ActionRuntimeServiceProvider,
|
|
76
|
+
AuthSupabaseServiceProvider,
|
|
77
|
+
AuthProviderServiceProvider,
|
|
78
|
+
AuthActionsServiceProvider
|
|
79
|
+
]
|
|
73
80
|
});
|
|
74
81
|
|
|
75
82
|
const authService = app.make("authService");
|
|
76
83
|
assert.equal(typeof authService?.login, "function");
|
|
84
|
+
assert.deepEqual(authService.getCapabilities(), {
|
|
85
|
+
provider: {
|
|
86
|
+
id: "supabase",
|
|
87
|
+
label: "Supabase"
|
|
88
|
+
},
|
|
89
|
+
features: {
|
|
90
|
+
password: {
|
|
91
|
+
login: true,
|
|
92
|
+
register: true,
|
|
93
|
+
change: true,
|
|
94
|
+
methodToggle: false
|
|
95
|
+
},
|
|
96
|
+
passwordRecovery: {
|
|
97
|
+
request: true,
|
|
98
|
+
complete: true,
|
|
99
|
+
delivery: "smtp"
|
|
100
|
+
},
|
|
101
|
+
otp: {
|
|
102
|
+
login: true
|
|
103
|
+
},
|
|
104
|
+
oauthLogin: {
|
|
105
|
+
enabled: false,
|
|
106
|
+
providers: [],
|
|
107
|
+
defaultProvider: null
|
|
108
|
+
},
|
|
109
|
+
emailConfirmation: true,
|
|
110
|
+
profileUpdate: true,
|
|
111
|
+
providerLinking: {
|
|
112
|
+
start: false,
|
|
113
|
+
unlink: false
|
|
114
|
+
},
|
|
115
|
+
securityStatus: true,
|
|
116
|
+
signOutOtherSessions: true,
|
|
117
|
+
appProfileProjection: false,
|
|
118
|
+
devLoginAs: false
|
|
119
|
+
}
|
|
120
|
+
});
|
|
77
121
|
|
|
78
122
|
const actionExecutor = app.make("actionExecutor");
|
|
79
123
|
assert.equal(typeof actionExecutor?.execute, "function");
|
|
@@ -111,14 +155,22 @@ test("auth supabase provider registers authService in standalone mode without us
|
|
|
111
155
|
});
|
|
112
156
|
|
|
113
157
|
await app.start({
|
|
114
|
-
providers: [
|
|
158
|
+
providers: [
|
|
159
|
+
ActionRuntimeServiceProvider,
|
|
160
|
+
AuthSupabaseServiceProvider,
|
|
161
|
+
AuthProviderServiceProvider,
|
|
162
|
+
AuthActionsServiceProvider
|
|
163
|
+
]
|
|
115
164
|
});
|
|
116
165
|
|
|
117
166
|
const authService = app.make("authService");
|
|
118
167
|
assert.equal(typeof authService?.login, "function");
|
|
168
|
+
const status = await authService.getSecurityStatus();
|
|
169
|
+
assert.equal(status.actions.changePassword, true);
|
|
170
|
+
assert.equal(status.actions.setPasswordEnabled, false);
|
|
119
171
|
});
|
|
120
172
|
|
|
121
|
-
test("auth supabase provider
|
|
173
|
+
test("auth supabase provider reports password method toggle only with persistent settings repository", async () => {
|
|
122
174
|
const app = createApplication();
|
|
123
175
|
app.instance("appConfig", createAppConfigFixture());
|
|
124
176
|
app.instance("jskit.env", {
|
|
@@ -136,12 +188,61 @@ test("auth supabase provider requires users.profile.sync.service in default user
|
|
|
136
188
|
app.instance("domainEvents", {
|
|
137
189
|
async publish() {}
|
|
138
190
|
});
|
|
191
|
+
app.instance("internal.repository.user-settings", {
|
|
192
|
+
async ensureForUserId() {
|
|
193
|
+
return {
|
|
194
|
+
passwordSignInEnabled: true,
|
|
195
|
+
passwordSetupRequired: false
|
|
196
|
+
};
|
|
197
|
+
},
|
|
198
|
+
async updatePasswordSignInEnabled() {
|
|
199
|
+
return {
|
|
200
|
+
passwordSignInEnabled: true,
|
|
201
|
+
passwordSetupRequired: false
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
});
|
|
139
205
|
|
|
140
206
|
await app.start({
|
|
141
207
|
providers: [ActionRuntimeServiceProvider, AuthSupabaseServiceProvider]
|
|
142
208
|
});
|
|
143
209
|
|
|
144
|
-
|
|
210
|
+
const authService = app.make("authService");
|
|
211
|
+
assert.equal(authService.getCapabilities().features.password.methodToggle, true);
|
|
212
|
+
const status = await authService.getSecurityStatus();
|
|
213
|
+
assert.equal(status.actions.setPasswordEnabled, true);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("auth supabase provider requires users.profile.sync.service in explicit users mode", async () => {
|
|
217
|
+
const app = createApplication();
|
|
218
|
+
app.instance("appConfig", createAppConfigFixture({
|
|
219
|
+
auth: {
|
|
220
|
+
profileMode: "users"
|
|
221
|
+
}
|
|
222
|
+
}));
|
|
223
|
+
app.instance("jskit.env", {
|
|
224
|
+
AUTH_SUPABASE_URL: "https://example.supabase.co",
|
|
225
|
+
AUTH_SUPABASE_PUBLISHABLE_KEY: "sb_publishable_test_key",
|
|
226
|
+
APP_PUBLIC_URL: "http://localhost:5173",
|
|
227
|
+
NODE_ENV: "test"
|
|
228
|
+
});
|
|
229
|
+
app.instance("jskit.logger", {
|
|
230
|
+
info() {},
|
|
231
|
+
warn() {},
|
|
232
|
+
error() {},
|
|
233
|
+
debug() {}
|
|
234
|
+
});
|
|
235
|
+
app.instance("domainEvents", {
|
|
236
|
+
async publish() {}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
await assert.rejects(
|
|
240
|
+
() =>
|
|
241
|
+
app.start({
|
|
242
|
+
providers: [ActionRuntimeServiceProvider, AuthSupabaseServiceProvider]
|
|
243
|
+
}),
|
|
244
|
+
(error) => isBootFailureWithCause(error, /config\.auth\.profileMode is "users"/)
|
|
245
|
+
);
|
|
145
246
|
});
|
|
146
247
|
|
|
147
248
|
test("auth supabase provider rejects unsupported config.auth.profileMode values", async () => {
|
|
@@ -167,11 +268,42 @@ test("auth supabase provider rejects unsupported config.auth.profileMode values"
|
|
|
167
268
|
async publish() {}
|
|
168
269
|
});
|
|
169
270
|
|
|
170
|
-
await
|
|
171
|
-
|
|
271
|
+
await assert.rejects(
|
|
272
|
+
() =>
|
|
273
|
+
app.start({
|
|
274
|
+
providers: [ActionRuntimeServiceProvider, AuthSupabaseServiceProvider]
|
|
275
|
+
}),
|
|
276
|
+
(error) => isBootFailureWithCause(error, /Unsupported config\.auth\.profileMode/)
|
|
277
|
+
);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test("auth supabase provider rejects AUTH_PROVIDER mismatches", async () => {
|
|
281
|
+
const app = createApplication();
|
|
282
|
+
app.instance("appConfig", createAppConfigFixture());
|
|
283
|
+
app.instance("jskit.env", {
|
|
284
|
+
AUTH_PROVIDER: "local",
|
|
285
|
+
AUTH_SUPABASE_URL: "https://example.supabase.co",
|
|
286
|
+
AUTH_SUPABASE_PUBLISHABLE_KEY: "sb_publishable_test_key",
|
|
287
|
+
APP_PUBLIC_URL: "http://localhost:5173",
|
|
288
|
+
NODE_ENV: "test"
|
|
289
|
+
});
|
|
290
|
+
app.instance("jskit.logger", {
|
|
291
|
+
info() {},
|
|
292
|
+
warn() {},
|
|
293
|
+
error() {},
|
|
294
|
+
debug() {}
|
|
295
|
+
});
|
|
296
|
+
app.instance("domainEvents", {
|
|
297
|
+
async publish() {}
|
|
172
298
|
});
|
|
173
299
|
|
|
174
|
-
assert.
|
|
300
|
+
await assert.rejects(
|
|
301
|
+
() =>
|
|
302
|
+
app.start({
|
|
303
|
+
providers: [ActionRuntimeServiceProvider, AuthSupabaseServiceProvider]
|
|
304
|
+
}),
|
|
305
|
+
(error) => /AUTH_PROVIDER is "local"/.test(String(error.details?.cause?.message || error.message || ""))
|
|
306
|
+
);
|
|
175
307
|
});
|
|
176
308
|
|
|
177
309
|
test("auth supabase provider can boot dev auth without Supabase credentials", async () => {
|
|
@@ -223,6 +355,12 @@ test("auth supabase provider can boot dev auth without Supabase credentials", as
|
|
|
223
355
|
assert.equal(typeof authService?.devLoginAs, "function");
|
|
224
356
|
assert.equal(typeof authService?.isDevAuthBootstrapEnabled, "function");
|
|
225
357
|
assert.equal(authService.isDevAuthBootstrapEnabled(), true);
|
|
358
|
+
const capabilities = authService.getCapabilities();
|
|
359
|
+
assert.equal(capabilities.features.password.login, false);
|
|
360
|
+
assert.equal(capabilities.features.passwordRecovery.request, false);
|
|
361
|
+
assert.equal(capabilities.features.otp.login, false);
|
|
362
|
+
assert.equal(capabilities.features.signOutOtherSessions, false);
|
|
363
|
+
assert.equal(capabilities.features.devLoginAs, true);
|
|
226
364
|
|
|
227
365
|
const actionExecutor = app.make("actionExecutor");
|
|
228
366
|
const definitions = actionExecutor.listDefinitions();
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
-
|
|
3
|
-
function createAuthSessionEventsService() {
|
|
4
|
-
async function notifySessionChanged(options = {}) {
|
|
5
|
-
const actorId = normalizeRecordId(options?.context?.actor?.id, { fallback: null });
|
|
6
|
-
if (!actorId) {
|
|
7
|
-
return null;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
return {
|
|
11
|
-
id: actorId
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return Object.freeze({
|
|
16
|
-
notifySessionChanged
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export { createAuthSessionEventsService };
|