@gopherium/react-auth 0.9.0 → 0.11.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/dist/admin/index.d.ts +16 -0
- package/dist/admin/index.js +70 -40
- package/dist/api.d.ts +2 -0
- package/dist/api.js +2 -0
- package/dist/capabilities.d.ts +8 -0
- package/dist/capabilities.js +10 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/session.d.ts +2 -0
- package/dist/testing/index.d.ts +8 -0
- package/dist/testing/index.js +10 -0
- package/package.json +7 -7
package/dist/admin/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { ValidationError } from '../api.js';
|
|
2
3
|
export declare const usersQueryKey: readonly ["users"];
|
|
3
4
|
declare const userSchema: z.ZodObject<{
|
|
4
5
|
id: z.ZodString;
|
|
@@ -49,6 +50,21 @@ export declare class SelfRoleError extends Error {
|
|
|
49
50
|
*/
|
|
50
51
|
export declare class LastPrivilegedError extends Error {
|
|
51
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* RoleUnknownError is thrown when a write names a role nobody declared.
|
|
55
|
+
*/
|
|
56
|
+
export declare class RoleUnknownError extends ValidationError {
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* RoleBeyondReachError is thrown when the actor may not hand out the role or change the account.
|
|
60
|
+
*/
|
|
61
|
+
export declare class RoleBeyondReachError extends Error {
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* RoleChangedError is thrown when the account's role changed before the write landed.
|
|
65
|
+
*/
|
|
66
|
+
export declare class RoleChangedError extends Error {
|
|
67
|
+
}
|
|
52
68
|
/**
|
|
53
69
|
* Returns every user account.
|
|
54
70
|
* @param signal - Aborts the in-flight request.
|
package/dist/admin/index.js
CHANGED
|
@@ -39,21 +39,79 @@ export class SelfRoleError extends Error {
|
|
|
39
39
|
*/
|
|
40
40
|
export class LastPrivilegedError extends Error {
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* RoleUnknownError is thrown when a write names a role nobody declared.
|
|
44
|
+
*/
|
|
45
|
+
export class RoleUnknownError extends ValidationError {
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* RoleBeyondReachError is thrown when the actor may not hand out the role or change the account.
|
|
49
|
+
*/
|
|
50
|
+
export class RoleBeyondReachError extends Error {
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* RoleChangedError is thrown when the account's role changed before the write landed.
|
|
54
|
+
*/
|
|
55
|
+
export class RoleChangedError extends Error {
|
|
56
|
+
}
|
|
42
57
|
const errorSchema = z.object({ error: z.string(), code: z.string().optional() });
|
|
58
|
+
const roleErrors = new Map([
|
|
59
|
+
['role_unknown', RoleUnknownError],
|
|
60
|
+
['role_beyond_reach', RoleBeyondReachError],
|
|
61
|
+
]);
|
|
62
|
+
const accountErrors = new Map([
|
|
63
|
+
...roleErrors,
|
|
64
|
+
['role_changed', RoleChangedError],
|
|
65
|
+
]);
|
|
66
|
+
const roleChangeErrors = new Map([
|
|
67
|
+
...accountErrors,
|
|
68
|
+
['role_insufficient', RoleRefusedError],
|
|
69
|
+
['self_role_refused', SelfRoleError],
|
|
70
|
+
['last_privileged_refused', LastPrivilegedError],
|
|
71
|
+
]);
|
|
43
72
|
/**
|
|
44
|
-
*
|
|
45
|
-
* @param response - The
|
|
73
|
+
* Returns the code and message a refused response carries.
|
|
74
|
+
* @param response - The refused HTTP response.
|
|
46
75
|
* @param fallback - The message to use when the body is unreadable.
|
|
47
|
-
* @returns The
|
|
76
|
+
* @returns The error code, if any, and its message.
|
|
48
77
|
*/
|
|
49
|
-
async function
|
|
78
|
+
async function errorOf(response, fallback) {
|
|
50
79
|
try {
|
|
51
|
-
|
|
80
|
+
const held = errorSchema.parse(await response.json());
|
|
81
|
+
return { code: held.code, message: held.error };
|
|
52
82
|
}
|
|
53
83
|
catch {
|
|
54
|
-
return fallback;
|
|
84
|
+
return { message: fallback };
|
|
55
85
|
}
|
|
56
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Returns the typed error a refused response's code names.
|
|
89
|
+
* @param refused - The code and message the response carries.
|
|
90
|
+
* @param errors - The error class each known code maps to.
|
|
91
|
+
* @returns The typed error, or undefined for any other code.
|
|
92
|
+
*/
|
|
93
|
+
function typedError(refused, errors = roleErrors) {
|
|
94
|
+
const typed = errors.get(refused.code ?? '');
|
|
95
|
+
return typed && new typed(refused.message);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Returns the error a refused account write throws.
|
|
99
|
+
* @param response - The refused HTTP response.
|
|
100
|
+
* @param fallback - The message to use when the body is unreadable.
|
|
101
|
+
* @param write - Names the write in the error for any other status.
|
|
102
|
+
* @returns The error to throw.
|
|
103
|
+
*/
|
|
104
|
+
async function writeError(response, fallback, write) {
|
|
105
|
+
const refused = await errorOf(response, fallback);
|
|
106
|
+
const typed = typedError(refused);
|
|
107
|
+
if (typed) {
|
|
108
|
+
return typed;
|
|
109
|
+
}
|
|
110
|
+
if (response.status === 422) {
|
|
111
|
+
return new ValidationError(refused.message);
|
|
112
|
+
}
|
|
113
|
+
return new Error(`${write} failed with status ${response.status}`);
|
|
114
|
+
}
|
|
57
115
|
/**
|
|
58
116
|
* Lists the accounts over REST.
|
|
59
117
|
* @param signal - Aborts the in-flight request.
|
|
@@ -86,11 +144,8 @@ async function restCreateUser(input) {
|
|
|
86
144
|
if (response.status === 409) {
|
|
87
145
|
throw new EmailTakenError('email already in use');
|
|
88
146
|
}
|
|
89
|
-
if (response.status === 422) {
|
|
90
|
-
throw new ValidationError(await errorMessage(response, __('invalid user details', DOMAIN)));
|
|
91
|
-
}
|
|
92
147
|
if (!response.ok) {
|
|
93
|
-
throw
|
|
148
|
+
throw await writeError(response, __('invalid user details', DOMAIN), 'creating user');
|
|
94
149
|
}
|
|
95
150
|
return userSchema.parse(await response.json());
|
|
96
151
|
}
|
|
@@ -109,14 +164,11 @@ async function restInvite(input) {
|
|
|
109
164
|
if (response.status === 401) {
|
|
110
165
|
throw new UnauthorizedError('session expired');
|
|
111
166
|
}
|
|
112
|
-
if (response.status === 422) {
|
|
113
|
-
throw new ValidationError(await errorMessage(response, __('invalid invitation details', DOMAIN)));
|
|
114
|
-
}
|
|
115
167
|
if (response.status === 429) {
|
|
116
168
|
throw new RateLimitedError('too many attempts');
|
|
117
169
|
}
|
|
118
170
|
if (!response.ok) {
|
|
119
|
-
throw
|
|
171
|
+
throw await writeError(response, __('invalid invitation details', DOMAIN), 'inviting');
|
|
120
172
|
}
|
|
121
173
|
return invitationSchema.parse(await response.json());
|
|
122
174
|
}
|
|
@@ -135,7 +187,8 @@ async function restSetUserDisabled(id, disabled) {
|
|
|
135
187
|
throw new UnauthorizedError('session expired');
|
|
136
188
|
}
|
|
137
189
|
if (!response.ok) {
|
|
138
|
-
|
|
190
|
+
const refused = await errorOf(response, __('the account could not be updated', DOMAIN));
|
|
191
|
+
throw typedError(refused, accountErrors) ?? new Error(`updating user failed with status ${response.status}`);
|
|
139
192
|
}
|
|
140
193
|
}
|
|
141
194
|
/**
|
|
@@ -155,31 +208,8 @@ async function restSetUserRole(id, role) {
|
|
|
155
208
|
if (response.status === 401) {
|
|
156
209
|
throw new UnauthorizedError('session expired');
|
|
157
210
|
}
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
throw new RoleRefusedError(failed.message);
|
|
161
|
-
}
|
|
162
|
-
if (failed.code === 'self_role_refused') {
|
|
163
|
-
throw new SelfRoleError(failed.message);
|
|
164
|
-
}
|
|
165
|
-
if (failed.code === 'last_privileged_refused') {
|
|
166
|
-
throw new LastPrivilegedError(failed.message);
|
|
167
|
-
}
|
|
168
|
-
throw new Error(`updating role failed with status ${response.status}`);
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Returns the code and message a refused response carries.
|
|
172
|
-
* @param response - The refused HTTP response.
|
|
173
|
-
* @returns The error code, if any, and its message.
|
|
174
|
-
*/
|
|
175
|
-
async function errorOf(response) {
|
|
176
|
-
try {
|
|
177
|
-
const held = errorSchema.parse(await response.json());
|
|
178
|
-
return { code: held.code, message: held.error };
|
|
179
|
-
}
|
|
180
|
-
catch {
|
|
181
|
-
return { message: __('the account could not be updated', DOMAIN) };
|
|
182
|
-
}
|
|
211
|
+
const refused = await errorOf(response, __('the account could not be updated', DOMAIN));
|
|
212
|
+
throw typedError(refused, roleChangeErrors) ?? new Error(`updating role failed with status ${response.status}`);
|
|
183
213
|
}
|
|
184
214
|
/**
|
|
185
215
|
* Returns every user account.
|
package/dist/api.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ declare const userSchema: z.ZodObject<{
|
|
|
4
4
|
email: z.ZodString;
|
|
5
5
|
name: z.ZodString;
|
|
6
6
|
role: z.ZodDefault<z.ZodString>;
|
|
7
|
+
capabilities: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
8
|
+
grantable: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7
9
|
}, z.core.$strip>;
|
|
8
10
|
export type User = z.infer<typeof userSchema>;
|
|
9
11
|
/**
|
package/dist/api.js
CHANGED
|
@@ -8,6 +8,8 @@ const userSchema = z.object({
|
|
|
8
8
|
email: z.string(),
|
|
9
9
|
name: z.string(),
|
|
10
10
|
role: z.string().default(''),
|
|
11
|
+
capabilities: z.array(z.string()).optional(),
|
|
12
|
+
grantable: z.array(z.string()).optional(),
|
|
11
13
|
});
|
|
12
14
|
/**
|
|
13
15
|
* InvalidCredentialsError is thrown when the backend rejects a login.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { User } from './api.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reports whether the user holds the capability, a missing user holding none.
|
|
4
|
+
* @param user - The signed-in user, if any.
|
|
5
|
+
* @param capability - The capability asked for.
|
|
6
|
+
* @returns Whether the user holds the capability.
|
|
7
|
+
*/
|
|
8
|
+
export declare function can(user: User | null | undefined, capability: string): boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Reports whether the user holds the capability, a missing user holding none.
|
|
4
|
+
* @param user - The signed-in user, if any.
|
|
5
|
+
* @param capability - The capability asked for.
|
|
6
|
+
* @returns Whether the user holds the capability.
|
|
7
|
+
*/
|
|
8
|
+
export function can(user, capability) {
|
|
9
|
+
return user?.capabilities?.includes(capability) ?? false;
|
|
10
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { InvalidCredentialsError, InvalidTokenError, RateLimitedError, UnauthorizedError, ValidationError, acceptInvite, fetchSession, login, logout, requestPasswordReset, resetPassword, } from './api.js';
|
|
2
2
|
export type { User } from './api.js';
|
|
3
3
|
export { AuthGate } from './AuthGate.js';
|
|
4
|
+
export { can } from './capabilities.js';
|
|
4
5
|
export { DOMAIN } from './domain.js';
|
|
5
6
|
export { catalogFor } from './languages.js';
|
|
6
7
|
export type { Catalog } from './languages.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
export { InvalidCredentialsError, InvalidTokenError, RateLimitedError, UnauthorizedError, ValidationError, acceptInvite, fetchSession, login, logout, requestPasswordReset, resetPassword, } from './api.js';
|
|
3
3
|
export { AuthGate } from './AuthGate.js';
|
|
4
|
+
export { can } from './capabilities.js';
|
|
4
5
|
export { DOMAIN } from './domain.js';
|
|
5
6
|
export { catalogFor } from './languages.js';
|
|
6
7
|
export { isSessionRevoked } from './probe.js';
|
package/dist/session.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export declare function useSession(): import("@tanstack/react-query").UseQueryRe
|
|
|
13
13
|
email: string;
|
|
14
14
|
name: string;
|
|
15
15
|
role: string;
|
|
16
|
+
capabilities?: string[] | undefined;
|
|
17
|
+
grantable?: string[] | undefined;
|
|
16
18
|
} | null>, Error>;
|
|
17
19
|
/**
|
|
18
20
|
* Ends the current session and drops all cached data belonging to the
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -17,6 +17,14 @@ export declare const defaultUser: User;
|
|
|
17
17
|
* @returns The account.
|
|
18
18
|
*/
|
|
19
19
|
export declare function userWithRole(role: string): User;
|
|
20
|
+
/**
|
|
21
|
+
* Returns a canned account holding the role, its capabilities, and the roles it may hand out.
|
|
22
|
+
* @param role - The role the account holds.
|
|
23
|
+
* @param capabilities - The capabilities the role carries.
|
|
24
|
+
* @param grantable - The roles the account may hand out.
|
|
25
|
+
* @returns The account.
|
|
26
|
+
*/
|
|
27
|
+
export declare function userWithCapabilities(role: string, capabilities: string[], grantable?: string[]): User;
|
|
20
28
|
/**
|
|
21
29
|
* Answers an invitation as mailed.
|
|
22
30
|
* @returns The msw handler.
|
package/dist/testing/index.js
CHANGED
|
@@ -46,6 +46,16 @@ export function userWithRole(role) {
|
|
|
46
46
|
role,
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Returns a canned account holding the role, its capabilities, and the roles it may hand out.
|
|
51
|
+
* @param role - The role the account holds.
|
|
52
|
+
* @param capabilities - The capabilities the role carries.
|
|
53
|
+
* @param grantable - The roles the account may hand out.
|
|
54
|
+
* @returns The account.
|
|
55
|
+
*/
|
|
56
|
+
export function userWithCapabilities(role, capabilities, grantable = []) {
|
|
57
|
+
return { ...userWithRole(role), capabilities: [...capabilities], grantable: [...grantable] };
|
|
58
|
+
}
|
|
49
59
|
/**
|
|
50
60
|
* Answers an invitation as mailed.
|
|
51
61
|
* @returns The msw handler.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gopherium/react-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Session authentication for React apps backed by gouncer's authkit.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"@tanstack/react-query": "^5.101.2",
|
|
51
51
|
"@testing-library/jest-dom": "^6.9.1 || ^7.0.0",
|
|
52
52
|
"@testing-library/react": "^16.3.2",
|
|
53
|
-
"@wordpress/i18n": ">=6.
|
|
54
|
-
"@wordpress/theme": ">=2.
|
|
55
|
-
"@wordpress/ui": ">=0.
|
|
53
|
+
"@wordpress/i18n": ">=6.29.0 <7.0.0",
|
|
54
|
+
"@wordpress/theme": ">=2.2.0 <3.0.0",
|
|
55
|
+
"@wordpress/ui": ">=0.23.0 <0.24.0",
|
|
56
56
|
"msw": "^2.15.0",
|
|
57
57
|
"react": "^19.2.7",
|
|
58
58
|
"vitest": "^4.1.10"
|
|
@@ -86,9 +86,9 @@
|
|
|
86
86
|
"@types/react": "^19.2.17",
|
|
87
87
|
"@vitejs/plugin-react": "^6.0.3",
|
|
88
88
|
"@vitest/coverage-v8": "^4.1.10",
|
|
89
|
-
"@wordpress/i18n": "6.
|
|
90
|
-
"@wordpress/theme": "2.
|
|
91
|
-
"@wordpress/ui": "0.
|
|
89
|
+
"@wordpress/i18n": "6.29.0",
|
|
90
|
+
"@wordpress/theme": "2.2.0",
|
|
91
|
+
"@wordpress/ui": "0.23.0",
|
|
92
92
|
"eslint": "^9.39.1",
|
|
93
93
|
"eslint-plugin-jsdoc": "^63.0.13",
|
|
94
94
|
"eslint-plugin-sonarjs": "^4.2.0",
|