@jskit-ai/auth-web 0.1.106 → 0.1.108

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.
@@ -1,7 +1,7 @@
1
1
  export default Object.freeze({
2
2
  "packageVersion": 1,
3
3
  "packageId": "@jskit-ai/auth-web",
4
- "version": "0.1.106",
4
+ "version": "0.1.108",
5
5
  "kind": "runtime",
6
6
  "description": "Auth web module: Fastify auth routes plus web login/sign-out scaffolds.",
7
7
  "dependsOn": [
@@ -260,10 +260,10 @@ export default Object.freeze({
260
260
  "dependencies": {
261
261
  "runtime": {
262
262
  "@mdi/js": "^7.4.47",
263
- "@jskit-ai/auth-core": "0.1.104",
264
- "@jskit-ai/http-runtime": "0.1.104",
265
- "@jskit-ai/kernel": "0.1.106",
266
- "@jskit-ai/shell-web": "0.1.105"
263
+ "@jskit-ai/auth-core": "0.1.106",
264
+ "@jskit-ai/http-runtime": "0.1.106",
265
+ "@jskit-ai/kernel": "0.1.108",
266
+ "@jskit-ai/shell-web": "0.1.107"
267
267
  },
268
268
  "dev": {}
269
269
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/auth-web",
3
- "version": "0.1.106",
3
+ "version": "0.1.108",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -19,11 +19,11 @@
19
19
  "./client/runtime/useSignOut": "./src/client/runtime/useSignOut.js"
20
20
  },
21
21
  "dependencies": {
22
- "@jskit-ai/auth-core": "0.1.104",
22
+ "@jskit-ai/auth-core": "0.1.106",
23
23
  "@mdi/js": "^7.4.47",
24
- "@jskit-ai/kernel": "0.1.106",
25
- "@jskit-ai/shell-web": "0.1.105",
26
- "@jskit-ai/http-runtime": "0.1.104"
24
+ "@jskit-ai/kernel": "0.1.108",
25
+ "@jskit-ai/shell-web": "0.1.107",
26
+ "@jskit-ai/http-runtime": "0.1.106"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "@tanstack/vue-query": "^5.90.5",
@@ -160,6 +160,13 @@ export function useLoginViewActions({
160
160
  email: normalizedEmail,
161
161
  password: String(state.password.value || "")
162
162
  };
163
+ const invitationToken = String(state.invitationToken?.value || "").trim();
164
+ if (invitationToken) {
165
+ registerPayload.invitation = {
166
+ token: invitationToken,
167
+ source: "workspace-invite"
168
+ };
169
+ }
163
170
  ensureCommandSectionValid(authRegisterCommand, "body", registerPayload, "Unable to register.");
164
171
 
165
172
  const registerResult = await request(AUTH_PATHS.REGISTER, {
@@ -25,9 +25,24 @@ import {
25
25
  clearRememberedAccountHint
26
26
  } from "./rememberedAccountStorage.js";
27
27
 
28
+ function readWindowSearchParam(name = "") {
29
+ if (typeof window !== "object" || !window.location) {
30
+ return "";
31
+ }
32
+ return String(new URLSearchParams(window.location.search || "").get(name) || "").trim();
33
+ }
34
+
35
+ function resolveInitialMode() {
36
+ const mode = readWindowSearchParam("mode").toLowerCase();
37
+ if ([LOGIN_MODE, REGISTER_MODE, FORGOT_MODE, OTP_MODE].includes(mode)) {
38
+ return mode;
39
+ }
40
+ return LOGIN_MODE;
41
+ }
42
+
28
43
  export function useLoginViewState({ placementContext } = {}) {
29
- const mode = ref(LOGIN_MODE);
30
- const email = ref("");
44
+ const mode = ref(resolveInitialMode());
45
+ const email = ref(normalizeEmailAddress(readWindowSearchParam("email")));
31
46
  const password = ref("");
32
47
  const confirmPassword = ref("");
33
48
  const otpCode = ref("");
@@ -51,6 +66,7 @@ export function useLoginViewState({ placementContext } = {}) {
51
66
  const infoMessage = ref("");
52
67
  const pendingEmailConfirmationMessage = ref("");
53
68
  const pendingEmailConfirmationAddress = ref("");
69
+ const invitationToken = ref(readWindowSearchParam("invitationToken") || readWindowSearchParam("inviteToken"));
54
70
 
55
71
  const isLogin = computed(() => mode.value === LOGIN_MODE);
56
72
  const isRegister = computed(() => mode.value === REGISTER_MODE);
@@ -273,6 +289,7 @@ export function useLoginViewState({ placementContext } = {}) {
273
289
  infoMessage,
274
290
  pendingEmailConfirmationMessage,
275
291
  pendingEmailConfirmationAddress,
292
+ invitationToken,
276
293
  isLogin,
277
294
  isRegister,
278
295
  isForgot,
@@ -36,6 +36,7 @@ function createMinimalLoginViewState(requestedReturnTo = "/") {
36
36
  otpRequestPending: { value: false },
37
37
  registerConfirmationResendPending: { value: false },
38
38
  pendingEmailConfirmationAddress: { value: "" },
39
+ invitationToken: { value: "" },
39
40
  rememberAccountOnDevice: { value: true },
40
41
  oauthProviders: { value: [] },
41
42
  oauthDefaultProvider: { value: "google" },
@@ -178,3 +179,67 @@ test("useLoginViewActions keeps retry message for generic post-login unauthentic
178
179
 
179
180
  assert.equal(state.errorMessage.value, "Login succeeded but the session is not active yet. Please retry.");
180
181
  });
182
+
183
+ test("useLoginViewActions includes invitation context when registering from an invite link", async () => {
184
+ clearAuthCsrfTokenCache();
185
+ const state = createMinimalLoginViewState("/invite/invite-token");
186
+ state.isRegister.value = true;
187
+ state.invitationToken.value = "invite-token";
188
+ const originalFetch = globalThis.fetch;
189
+ const registerBodies = [];
190
+ let sessionReads = 0;
191
+
192
+ globalThis.fetch = async (url, options = {}) => {
193
+ const normalizedUrl = String(url || "");
194
+ const method = String(options.method || "GET").toUpperCase();
195
+
196
+ if (normalizedUrl === "/api/session" && method === "GET") {
197
+ sessionReads += 1;
198
+ return createJsonResponse(
199
+ sessionReads === 1
200
+ ? { authenticated: false, csrfToken: "csrf-a" }
201
+ : { authenticated: true, csrfToken: "csrf-b" }
202
+ );
203
+ }
204
+
205
+ if (normalizedUrl === "/api/register" && method === "POST") {
206
+ registerBodies.push(JSON.parse(String(options.body || "{}")));
207
+ return createJsonResponse({ ok: true, username: "Ada", requiresEmailConfirmation: false });
208
+ }
209
+
210
+ throw new Error(`Unexpected fetch call: ${method} ${normalizedUrl}`);
211
+ };
212
+
213
+ try {
214
+ const actions = useLoginViewActions({
215
+ state,
216
+ validation: {
217
+ canSubmit: { value: true }
218
+ },
219
+ queryClient: {
220
+ async fetchQuery({ queryFn }) {
221
+ return queryFn();
222
+ }
223
+ },
224
+ errorRuntime: {
225
+ report() {}
226
+ }
227
+ });
228
+
229
+ await actions.submitAuth();
230
+ } finally {
231
+ globalThis.fetch = originalFetch;
232
+ clearAuthCsrfTokenCache();
233
+ }
234
+
235
+ assert.deepEqual(registerBodies, [
236
+ {
237
+ email: "ada@example.com",
238
+ password: "password-123",
239
+ invitation: {
240
+ token: "invite-token",
241
+ source: "workspace-invite"
242
+ }
243
+ }
244
+ ]);
245
+ });