@object-ui/auth 17.5.0 → 17.6.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/CHANGELOG.md +110 -0
- package/README.md +148 -0
- package/dist/ActiveOrganizationStorage.d.ts +168 -0
- package/dist/ActiveOrganizationStorage.d.ts.map +1 -0
- package/dist/ActiveOrganizationStorage.js +515 -0
- package/dist/AuthContext.d.ts +19 -1
- package/dist/AuthContext.d.ts.map +1 -1
- package/dist/AuthGuard.d.ts +8 -1
- package/dist/AuthGuard.d.ts.map +1 -1
- package/dist/AuthGuard.js +16 -3
- package/dist/AuthProvider.d.ts +1 -1
- package/dist/AuthProvider.d.ts.map +1 -1
- package/dist/AuthProvider.js +272 -7
- package/dist/ForgotPasswordForm.d.ts +1 -1
- package/dist/ForgotPasswordForm.d.ts.map +1 -1
- package/dist/ForgotPasswordForm.js +5 -5
- package/dist/LoginForm.d.ts +1 -1
- package/dist/LoginForm.d.ts.map +1 -1
- package/dist/LoginForm.js +4 -4
- package/dist/PreviewBanner.js +1 -1
- package/dist/RegisterForm.d.ts +1 -1
- package/dist/RegisterForm.d.ts.map +1 -1
- package/dist/RegisterForm.js +3 -3
- package/dist/SocialSignInButtons.js +2 -2
- package/dist/UserMenu.js +2 -2
- package/dist/createAuthClient.d.ts +1 -1
- package/dist/createAuthClient.d.ts.map +1 -1
- package/dist/createAuthClient.js +36 -4
- package/dist/createAuthenticatedFetch.d.ts +9 -9
- package/dist/createAuthenticatedFetch.d.ts.map +1 -1
- package/dist/createAuthenticatedFetch.js +49 -37
- package/dist/index.d.ts +20 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -17
- package/dist/invitation-status.d.ts +59 -0
- package/dist/invitation-status.d.ts.map +1 -0
- package/dist/invitation-status.js +32 -0
- package/dist/types.d.ts +11 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/useAuth.d.ts +1 -1
- package/dist/useAuth.d.ts.map +1 -1
- package/dist/useAuth.js +5 -1
- package/dist/useWorkspaceAdminStatus.d.ts +85 -0
- package/dist/useWorkspaceAdminStatus.d.ts.map +1 -0
- package/dist/useWorkspaceAdminStatus.js +102 -0
- package/package.json +4 -4
- package/dist/useIsWorkspaceAdmin.d.ts +0 -21
- package/dist/useIsWorkspaceAdmin.d.ts.map +0 -1
- package/dist/useIsWorkspaceAdmin.js +0 -47
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import { useAuth } from './useAuth.js';
|
|
9
|
+
const ADMIN_ROLES = new Set([
|
|
10
|
+
'owner',
|
|
11
|
+
'admin',
|
|
12
|
+
'super_admin',
|
|
13
|
+
'superadmin',
|
|
14
|
+
'platform_admin',
|
|
15
|
+
'system_admin',
|
|
16
|
+
// ADR-0068 canonical names, emitted into `user.positions[]` by the server's
|
|
17
|
+
// customSession (raw better-auth owner/admin are normalized to org_owner/
|
|
18
|
+
// org_admin). Accept both raw and canonical so admin detection survives the
|
|
19
|
+
// removal of the `user.role = 'admin'` overwrite footgun.
|
|
20
|
+
'org_owner',
|
|
21
|
+
'org_admin',
|
|
22
|
+
]);
|
|
23
|
+
/**
|
|
24
|
+
* One name from any of the three sources below — a membership role, the stored
|
|
25
|
+
* `user.role` scalar, or an entry of `user.positions[]`. The three draw on one
|
|
26
|
+
* vocabulary by design (ADR-0068 D2 maps a membership `owner`/`admin` onto the
|
|
27
|
+
* canonical `org_owner`/`org_admin` that `positions[]` carries), so one
|
|
28
|
+
* predicate serves all three.
|
|
29
|
+
*/
|
|
30
|
+
function isAdminRole(role) {
|
|
31
|
+
return typeof role === 'string' && ADMIN_ROLES.has(role.toLowerCase());
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether the current user has owner/admin privileges, and whether that answer
|
|
35
|
+
* has finished resolving.
|
|
36
|
+
*
|
|
37
|
+
* Sources considered (any one is sufficient):
|
|
38
|
+
* - The active organization member row (multi-tenant mode).
|
|
39
|
+
* - The top-level `user.role` scalar from the session — legacy deployments
|
|
40
|
+
* that still store an admin role on the user row.
|
|
41
|
+
* - `user.positions[]` from the session — the canonical identity/position
|
|
42
|
+
* names the server derives (ADR-0068 D1/D2, renamed from `roles` by
|
|
43
|
+
* ADR-0090 D3). This is the ONLY leg that sees a platform administrator
|
|
44
|
+
* whose adminship comes from the `admin_full_access` permission set: on a
|
|
45
|
+
* single-tenant deployment there is no member row to read (leg 1), and the
|
|
46
|
+
* server deliberately no longer overwrites `user.role` (leg 2), so
|
|
47
|
+
* `positions` carrying `platform_admin` is the whole signal. Measured on a
|
|
48
|
+
* live protocol-17 server in objectui#5389; pinned by
|
|
49
|
+
* `__tests__/workspaceAdminPositions-5389.test.tsx`.
|
|
50
|
+
*
|
|
51
|
+
* `positions` is the ONE published spelling. It is deliberately not read
|
|
52
|
+
* alongside the retired `roles` (ADR-0090 D3 renamed with no deprecation
|
|
53
|
+
* window and bans resurrecting the old name as a fallback), and not alongside
|
|
54
|
+
* the derived `user.isPlatformAdmin` either — that flag is computed by the
|
|
55
|
+
* server as `'platform_admin' in positions` from the same expression, so
|
|
56
|
+
* reading both would be two spellings for one fact with no way to disagree
|
|
57
|
+
* usefully and every way to drift.
|
|
58
|
+
*
|
|
59
|
+
* In preview-mode / no-auth-enabled mode the provider seeds an admin user,
|
|
60
|
+
* so this returns `isAdmin: true` for dev/demo setups.
|
|
61
|
+
*
|
|
62
|
+
* ## Why this returns a pair and not a boolean (objectui#5619)
|
|
63
|
+
*
|
|
64
|
+
* It used to return a bare `boolean`, which gave "not resolved yet" and
|
|
65
|
+
* "resolved: not an admin" the same answer — `false`. Leg 1 is not settled
|
|
66
|
+
* when a console page first mounts: `AuthProvider.refreshActiveMember` runs
|
|
67
|
+
* from its own effect, keyed on `activeOrganization`, which itself appears
|
|
68
|
+
* only after `listOrganizations()` → `getActiveOrganization()` (→ the ADR-0081
|
|
69
|
+
* single-membership `setActiveOrganization()` repair) resolve. So an
|
|
70
|
+
* administrator whose adminship lives ONLY in the member row rendered at least
|
|
71
|
+
* once as a non-admin, and every gate downstream acted on it: two marketplace
|
|
72
|
+
* surfaces painted `MarketplaceAccessDenied` at a real administrator, the
|
|
73
|
+
* console chrome dropped and re-added a nav entry, and `AppContent` fired a
|
|
74
|
+
* `<Navigate to="/home" replace>` that the later flip cannot undo.
|
|
75
|
+
*
|
|
76
|
+
* Legs 2 and 3 DO cover that viewer whenever the session was minted with an
|
|
77
|
+
* active organization stamped on it — framework `auth-manager.ts` reads
|
|
78
|
+
* `session.activeOrganizationId` and, when it is set, maps the member row into
|
|
79
|
+
* `positions[]` as `org_owner`/`org_admin`. The window is therefore not "every
|
|
80
|
+
* tenant admin" as first reported, but "every session that reaches the console
|
|
81
|
+
* without that stamp", which is exactly the path `refreshOrganizations`'
|
|
82
|
+
* ADR-0081 repair exists to serve.
|
|
83
|
+
*
|
|
84
|
+
* `isResolved` is deliberately true the moment `isAdmin` is true: a confirmed
|
|
85
|
+
* admin is a final answer, and the legs still in flight can only agree with it.
|
|
86
|
+
* Callers therefore never wait on a verdict they already have — an admin
|
|
87
|
+
* detected through `positions` pays nothing for this gate.
|
|
88
|
+
*/
|
|
89
|
+
export function useWorkspaceAdminStatus() {
|
|
90
|
+
const { activeMember, user, isLoading, isMembershipResolved } = useAuth();
|
|
91
|
+
const positions = user?.positions;
|
|
92
|
+
const isAdmin = isAdminRole(activeMember?.role) ||
|
|
93
|
+
isAdminRole(user?.role) ||
|
|
94
|
+
(Array.isArray(positions) && positions.some(isAdminRole));
|
|
95
|
+
if (isAdmin)
|
|
96
|
+
return { isAdmin: true, isResolved: true };
|
|
97
|
+
// Legs 2 and 3 are settled once the session has landed (`isLoading`); leg 1
|
|
98
|
+
// is settled once the organization/member pipeline has reached a terminal
|
|
99
|
+
// state (`isMembershipResolved`). Both, because a viewer can be an admin
|
|
100
|
+
// through either.
|
|
101
|
+
return { isAdmin: false, isResolved: !isLoading && isMembershipResolved };
|
|
102
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/auth",
|
|
3
|
-
"version": "17.
|
|
3
|
+
"version": "17.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Authentication system for Object UI with AuthProvider, useAuth hook, AuthGuard, and form components.",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"react": "^18.0.0 || ^19.0.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@objectstack/spec": "^17.0.0
|
|
34
|
-
"better-auth": "^1.6.
|
|
35
|
-
"@object-ui/types": "17.
|
|
33
|
+
"@objectstack/spec": "^17.0.0",
|
|
34
|
+
"better-auth": "^1.6.28",
|
|
35
|
+
"@object-ui/types": "17.6.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/react": "19.2.18",
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Returns true when the current user has owner/admin privileges.
|
|
10
|
-
*
|
|
11
|
-
* Sources considered (any one is sufficient):
|
|
12
|
-
* - The active organization member row (multi-tenant mode).
|
|
13
|
-
* - The top-level `user.role` / `user.roles` from the session — this covers
|
|
14
|
-
* platform / system administrators in single-tenant deployments where
|
|
15
|
-
* there is no `activeMember` row to read a role from.
|
|
16
|
-
*
|
|
17
|
-
* In preview-mode / no-auth-enabled mode the provider seeds an admin user,
|
|
18
|
-
* so this returns `true` for dev/demo setups.
|
|
19
|
-
*/
|
|
20
|
-
export declare function useIsWorkspaceAdmin(): boolean;
|
|
21
|
-
//# sourceMappingURL=useIsWorkspaceAdmin.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useIsWorkspaceAdmin.d.ts","sourceRoot":"","sources":["../src/useIsWorkspaceAdmin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAuBH;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAM7C"}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
import { useAuth } from './useAuth';
|
|
9
|
-
const ADMIN_ROLES = new Set([
|
|
10
|
-
'owner',
|
|
11
|
-
'admin',
|
|
12
|
-
'super_admin',
|
|
13
|
-
'superadmin',
|
|
14
|
-
'platform_admin',
|
|
15
|
-
'system_admin',
|
|
16
|
-
// ADR-0068 canonical role names emitted into `user.roles[]` by the server
|
|
17
|
-
// customSession (raw better-auth owner/admin are normalized to org_owner/
|
|
18
|
-
// org_admin). Accept both raw and canonical so admin detection survives the
|
|
19
|
-
// removal of the `user.role = 'admin'` overwrite footgun.
|
|
20
|
-
'org_owner',
|
|
21
|
-
'org_admin',
|
|
22
|
-
]);
|
|
23
|
-
function isAdminRole(role) {
|
|
24
|
-
return typeof role === 'string' && ADMIN_ROLES.has(role.toLowerCase());
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Returns true when the current user has owner/admin privileges.
|
|
28
|
-
*
|
|
29
|
-
* Sources considered (any one is sufficient):
|
|
30
|
-
* - The active organization member row (multi-tenant mode).
|
|
31
|
-
* - The top-level `user.role` / `user.roles` from the session — this covers
|
|
32
|
-
* platform / system administrators in single-tenant deployments where
|
|
33
|
-
* there is no `activeMember` row to read a role from.
|
|
34
|
-
*
|
|
35
|
-
* In preview-mode / no-auth-enabled mode the provider seeds an admin user,
|
|
36
|
-
* so this returns `true` for dev/demo setups.
|
|
37
|
-
*/
|
|
38
|
-
export function useIsWorkspaceAdmin() {
|
|
39
|
-
const { activeMember, user } = useAuth();
|
|
40
|
-
if (isAdminRole(activeMember?.role))
|
|
41
|
-
return true;
|
|
42
|
-
if (isAdminRole(user?.role))
|
|
43
|
-
return true;
|
|
44
|
-
if (Array.isArray(user?.roles) && user.roles.some(isAdminRole))
|
|
45
|
-
return true;
|
|
46
|
-
return false;
|
|
47
|
-
}
|