@tribe-nest/forge 3.20.0 → 3.21.0
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
CHANGED
|
@@ -69,13 +69,26 @@ export function useUpdateAccount() {
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* Change the member's password.
|
|
74
|
+
*
|
|
75
|
+
* Tenant-scoped (`/public/sessions/password`), because that is where a fan's
|
|
76
|
+
* credential lives — see the note at the top of `useAuthActions.ts`. The global
|
|
77
|
+
* `/public/accounts/password` this used to call wrote a hash that login never
|
|
78
|
+
* reads: it returned 200, invalidated the session, and left the old password
|
|
79
|
+
* working.
|
|
80
|
+
*
|
|
81
|
+
* Requires an association-scoped session (the endpoint is behind
|
|
82
|
+
* `requireAssociation`). Every current login path mints one; a member still
|
|
83
|
+
* holding a pre-cutover account-scoped token gets a 401 and needs to sign in
|
|
84
|
+
* again — honest, where the old call was silently a no-op.
|
|
85
|
+
*/
|
|
73
86
|
export function useChangePassword() {
|
|
74
87
|
const { client } = useForge();
|
|
75
88
|
|
|
76
89
|
return useMutation<unknown, unknown, { currentPassword: string; newPassword: string }>({
|
|
77
90
|
mutationFn: async (body) => {
|
|
78
|
-
const res = await client.put("/public/
|
|
91
|
+
const res = await client.put("/public/sessions/password", body);
|
|
79
92
|
return res.data;
|
|
80
93
|
},
|
|
81
94
|
});
|
|
@@ -2,26 +2,70 @@ import { useForge } from "../../provider/ForgeProvider";
|
|
|
2
2
|
import { useMutation } from "@tanstack/react-query";
|
|
3
3
|
|
|
4
4
|
// Stateless account actions (no session state) — kept out of PublicAuthContext.
|
|
5
|
+
//
|
|
6
|
+
// ## Why these hit `/public/sessions/*` and not `/public/accounts/*`
|
|
7
|
+
//
|
|
8
|
+
// A member's credential for a tenant lives on their `account_associations` row,
|
|
9
|
+
// not on `accounts` — that is the hash `POST /public/sessions` checks at login
|
|
10
|
+
// (see `resolveAssociationLogin` on the backend). `accounts.password` is the
|
|
11
|
+
// CREATOR/admin-dashboard credential; for a fan it is at most a lazy-heal
|
|
12
|
+
// fallback, gated on the account having some other relationship to the profile.
|
|
13
|
+
//
|
|
14
|
+
// These three calls used to point at the global `/public/accounts/*` endpoints,
|
|
15
|
+
// which write `accounts.password`. Every one of them returned 200 and changed
|
|
16
|
+
// nothing that login reads:
|
|
17
|
+
//
|
|
18
|
+
// - "Change password" logged the member out (a credential change invalidates
|
|
19
|
+
// sessions), then refused the new password and kept accepting the old one.
|
|
20
|
+
// - "Forgot password" was worse: it reset a hash login never consults, so the
|
|
21
|
+
// member — who by definition no longer knows the old password — was locked
|
|
22
|
+
// out with a reset link that appeared to work.
|
|
23
|
+
//
|
|
24
|
+
// The tenant-scoped endpoints below are the ones the credential actually lives
|
|
25
|
+
// behind, and they invalidate only this context's sessions.
|
|
5
26
|
|
|
6
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Request a password-reset email for this tenant.
|
|
29
|
+
*
|
|
30
|
+
* `profileId` comes from the provider (one deploy = one tenant); pass it
|
|
31
|
+
* explicitly only when rendering outside a pinned context.
|
|
32
|
+
*/
|
|
7
33
|
export function useForgotPassword() {
|
|
8
|
-
const { client } = useForge();
|
|
34
|
+
const { client, profileId } = useForge();
|
|
9
35
|
|
|
10
|
-
return useMutation<unknown, unknown, { email: string; origin?: string }>({
|
|
11
|
-
mutationFn: async ({ email, origin }) => {
|
|
12
|
-
const
|
|
36
|
+
return useMutation<unknown, unknown, { email: string; origin?: string; profileId?: string }>({
|
|
37
|
+
mutationFn: async ({ email, origin, profileId: overrideProfileId }) => {
|
|
38
|
+
const contextId = overrideProfileId ?? profileId;
|
|
39
|
+
if (!contextId) {
|
|
40
|
+
// Better than posting without it: the endpoint would 400 on schema
|
|
41
|
+
// validation and the form would show "something went wrong".
|
|
42
|
+
throw new Error("useForgotPassword: no profileId — pass one or render inside a ForgeProvider with profileId");
|
|
43
|
+
}
|
|
44
|
+
const res = await client.post("/public/sessions/forgot-password", {
|
|
45
|
+
profileId: contextId,
|
|
46
|
+
email,
|
|
47
|
+
// The reset link is built server-side from this, and the association
|
|
48
|
+
// endpoint requires it (the global one treated it as optional).
|
|
49
|
+
origin: origin ?? (typeof window !== "undefined" ? window.location.origin : undefined),
|
|
50
|
+
});
|
|
13
51
|
return res.data;
|
|
14
52
|
},
|
|
15
53
|
});
|
|
16
54
|
}
|
|
17
55
|
|
|
18
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Complete a password reset with the emailed token.
|
|
58
|
+
*
|
|
59
|
+
* The token is looked up on the association row, so it must be redeemed against
|
|
60
|
+
* the same surface that issued it — a token minted by the hook above will not
|
|
61
|
+
* resolve on `/public/accounts/reset-password`, and vice versa.
|
|
62
|
+
*/
|
|
19
63
|
export function useResetPassword() {
|
|
20
64
|
const { client } = useForge();
|
|
21
65
|
|
|
22
66
|
return useMutation<unknown, unknown, { token: string; password: string }>({
|
|
23
67
|
mutationFn: async ({ token, password }) => {
|
|
24
|
-
const res = await client.post("/public/
|
|
68
|
+
const res = await client.post("/public/sessions/reset-password", { token, password });
|
|
25
69
|
return res.data;
|
|
26
70
|
},
|
|
27
71
|
});
|