@stytch/react 12.0.0 → 13.0.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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # @stytch/react
2
2
 
3
+ ## 13.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6890694: Mark stytch.member as deprecated in favor of stytch.self
8
+ Adds RBAC functionality
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies [9ee61b3]
13
+ - Updated dependencies [c3c108b]
14
+ - Updated dependencies [6890694]
15
+ - @stytch/vanilla-js@3.3.0
16
+
3
17
  ## 12.0.0
4
18
 
5
19
  ### Patch Changes
@@ -3,6 +3,7 @@ import React from "react";
3
3
  import { ReactNode } from "react";
4
4
  import { Member, MemberSession, StytchB2BUIClient } from "@stytch/vanilla-js/b2b";
5
5
  import { StytchB2BHeadlessClient } from "@stytch/vanilla-js/b2b/headless";
6
+ import { PermissionsMap } from "@stytch/core/public";
6
7
  import { Callbacks, StyleConfig, StytchB2BUIConfig } from "@stytch/vanilla-js";
7
8
  /**
8
9
  * The Stytch Client object passed in to <StytchProvider /> in your application root.
@@ -49,6 +50,29 @@ declare const useStytchMember: () => SWRMember;
49
50
  * @returns A {@link SWRMemberSession}
50
51
  */
51
52
  declare const useStytchMemberSession: () => SWRMemberSession;
53
+ type SWRIsAuthorized = {
54
+ /**
55
+ * Whether the logged-in member is allowed to perform the specified action on the specified resource.
56
+ */
57
+ isAuthorized: boolean;
58
+ /**
59
+ * If true, indicates that the value returned is from the application cache and a state refresh is in progress.
60
+ */
61
+ fromCache: boolean;
62
+ };
63
+ /**
64
+ * Determines whether the logged-in member is allowed to perform the specified action on the specified resource.
65
+ * Returns `true` if the member can perform the action, `false` otherwise.
66
+ *
67
+ * If the member is not logged in, this method will always return false.
68
+ * If the resource or action provided are not valid for the configured RBAC policy, this method will return false.
69
+ *
70
+ * Remember - authorization checks for sensitive actions should always occur on the backend as well.
71
+ * @example
72
+ * const isAuthorized = useStytchIsAuthorized<Permissions>('documents', 'edit');
73
+ * return <button disabled={!isAuthorized}>Edit</button>
74
+ */
75
+ declare const useStytchIsAuthorized: (resourceId: string, action: string) => SWRIsAuthorized;
52
76
  /**
53
77
  * Returns the Stytch B2B client stored in the Stytch context.
54
78
  *
@@ -58,9 +82,9 @@ declare const useStytchMemberSession: () => SWRMemberSession;
58
82
  * stytch.magicLinks.authenticate('...')
59
83
  * }, [stytch]);
60
84
  */
61
- declare const useStytchB2BClient: () => StytchB2BClient;
85
+ declare const useStytchB2BClient: () => StytchB2BHeadlessClient;
62
86
  declare const withStytchB2BClient: <T extends object>(Component: React.ComponentType<T & {
63
- stytch: StytchB2BClient;
87
+ stytch: StytchB2BHeadlessClient;
64
88
  }>) => React.ComponentType<T>;
65
89
  declare const withStytchMember: <T extends object>(Component: React.ComponentType<T & {
66
90
  stytchMember: Member | null;
@@ -70,6 +94,30 @@ declare const withStytchMemberSession: <T extends object>(Component: React.Compo
70
94
  stytchMemberSession: MemberSession | null;
71
95
  stytchMemberSessionIsFromCache: boolean;
72
96
  }>) => React.ComponentType<T>;
97
+ /**
98
+ * Wrap your component with this HOC in order to receive the permissions for the logged-in member.
99
+ * Evaluates all permissions granted to the logged-in member.
100
+ * Returns a Record<RoleId, Record<Action, boolean>> response indicating the member's permissions.
101
+ * Each boolean will be `true` if the member can perform the action, `false` otherwise.
102
+ *
103
+ * If the member is not logged in, all values will be false.
104
+ *
105
+ * Remember - authorization checks for sensitive actions should always occur on the backend as well.
106
+ * @example
107
+ * type Permissions = {
108
+ * document: 'create' | 'read' | 'write
109
+ * image: 'create' | 'read'
110
+ * }
111
+ *
112
+ * const MyComponent = (props) => {
113
+ * const canEditDocuments = props.stytchPermissions.document.edit;
114
+ * const canReadImages = props.stytchPermissions.image.read;
115
+ * }
116
+ * return withStytchPermissions<Permissions>(MyComponent)
117
+ */
118
+ declare const withStytchPermissions: <Permissions_1 extends Record<string, string>, T extends object>(Component: React.ComponentType<T & {
119
+ stytchPermissions: PermissionsMap<Permissions_1>;
120
+ }>) => React.ComponentType<T>;
73
121
  type StytchB2BProviderProps = {
74
122
  /**
75
123
  * A Stytch client instance {@link StytchB2BHeadlessClient}
@@ -193,5 +241,5 @@ interface StytchB2BProps {
193
241
  * @param props {@link StytchB2BProps}
194
242
  */
195
243
  declare const StytchB2B: ({ styles, callbacks, config }: StytchB2BProps) => React.JSX.Element;
196
- export { StytchB2BProvider, useStytchB2BClient, useStytchMemberSession, useStytchMember, withStytchB2BClient, withStytchMemberSession, withStytchMember, StytchB2B };
244
+ export { StytchB2BProvider, useStytchB2BClient, useStytchMemberSession, useStytchMember, useStytchIsAuthorized, withStytchB2BClient, withStytchMemberSession, withStytchMember, withStytchPermissions, StytchB2B };
197
245
  export type { StytchB2BProviderProps };
@@ -3,6 +3,7 @@ import React from "react";
3
3
  import { ReactNode } from "react";
4
4
  import { Member, MemberSession, StytchB2BUIClient } from "@stytch/vanilla-js/b2b";
5
5
  import { StytchB2BHeadlessClient } from "@stytch/vanilla-js/b2b/headless";
6
+ import { PermissionsMap } from "@stytch/core/public";
6
7
  import { Callbacks, StyleConfig, StytchB2BUIConfig } from "@stytch/vanilla-js";
7
8
  /**
8
9
  * The Stytch Client object passed in to <StytchProvider /> in your application root.
@@ -49,6 +50,29 @@ declare const useStytchMember: () => SWRMember;
49
50
  * @returns A {@link SWRMemberSession}
50
51
  */
51
52
  declare const useStytchMemberSession: () => SWRMemberSession;
53
+ type SWRIsAuthorized = {
54
+ /**
55
+ * Whether the logged-in member is allowed to perform the specified action on the specified resource.
56
+ */
57
+ isAuthorized: boolean;
58
+ /**
59
+ * If true, indicates that the value returned is from the application cache and a state refresh is in progress.
60
+ */
61
+ fromCache: boolean;
62
+ };
63
+ /**
64
+ * Determines whether the logged-in member is allowed to perform the specified action on the specified resource.
65
+ * Returns `true` if the member can perform the action, `false` otherwise.
66
+ *
67
+ * If the member is not logged in, this method will always return false.
68
+ * If the resource or action provided are not valid for the configured RBAC policy, this method will return false.
69
+ *
70
+ * Remember - authorization checks for sensitive actions should always occur on the backend as well.
71
+ * @example
72
+ * const isAuthorized = useStytchIsAuthorized<Permissions>('documents', 'edit');
73
+ * return <button disabled={!isAuthorized}>Edit</button>
74
+ */
75
+ declare const useStytchIsAuthorized: (resourceId: string, action: string) => SWRIsAuthorized;
52
76
  /**
53
77
  * Returns the Stytch B2B client stored in the Stytch context.
54
78
  *
@@ -58,9 +82,9 @@ declare const useStytchMemberSession: () => SWRMemberSession;
58
82
  * stytch.magicLinks.authenticate('...')
59
83
  * }, [stytch]);
60
84
  */
61
- declare const useStytchB2BClient: () => StytchB2BClient;
85
+ declare const useStytchB2BClient: () => StytchB2BHeadlessClient;
62
86
  declare const withStytchB2BClient: <T extends object>(Component: React.ComponentType<T & {
63
- stytch: StytchB2BClient;
87
+ stytch: StytchB2BHeadlessClient;
64
88
  }>) => React.ComponentType<T>;
65
89
  declare const withStytchMember: <T extends object>(Component: React.ComponentType<T & {
66
90
  stytchMember: Member | null;
@@ -70,6 +94,30 @@ declare const withStytchMemberSession: <T extends object>(Component: React.Compo
70
94
  stytchMemberSession: MemberSession | null;
71
95
  stytchMemberSessionIsFromCache: boolean;
72
96
  }>) => React.ComponentType<T>;
97
+ /**
98
+ * Wrap your component with this HOC in order to receive the permissions for the logged-in member.
99
+ * Evaluates all permissions granted to the logged-in member.
100
+ * Returns a Record<RoleId, Record<Action, boolean>> response indicating the member's permissions.
101
+ * Each boolean will be `true` if the member can perform the action, `false` otherwise.
102
+ *
103
+ * If the member is not logged in, all values will be false.
104
+ *
105
+ * Remember - authorization checks for sensitive actions should always occur on the backend as well.
106
+ * @example
107
+ * type Permissions = {
108
+ * document: 'create' | 'read' | 'write
109
+ * image: 'create' | 'read'
110
+ * }
111
+ *
112
+ * const MyComponent = (props) => {
113
+ * const canEditDocuments = props.stytchPermissions.document.edit;
114
+ * const canReadImages = props.stytchPermissions.image.read;
115
+ * }
116
+ * return withStytchPermissions<Permissions>(MyComponent)
117
+ */
118
+ declare const withStytchPermissions: <Permissions_1 extends Record<string, string>, T extends object>(Component: React.ComponentType<T & {
119
+ stytchPermissions: PermissionsMap<Permissions_1>;
120
+ }>) => React.ComponentType<T>;
73
121
  type StytchB2BProviderProps = {
74
122
  /**
75
123
  * A Stytch client instance {@link StytchB2BHeadlessClient}
@@ -193,5 +241,5 @@ interface StytchB2BProps {
193
241
  * @param props {@link StytchB2BProps}
194
242
  */
195
243
  declare const StytchB2B: ({ styles, callbacks, config }: StytchB2BProps) => React.JSX.Element;
196
- export { StytchB2BProvider, useStytchB2BClient, useStytchMemberSession, useStytchMember, withStytchB2BClient, withStytchMemberSession, withStytchMember, StytchB2B };
244
+ export { StytchB2BProvider, useStytchB2BClient, useStytchMemberSession, useStytchMember, useStytchIsAuthorized, withStytchB2BClient, withStytchMemberSession, withStytchMember, withStytchPermissions, StytchB2B };
197
245
  export type { StytchB2BProviderProps };
@@ -75,6 +75,34 @@ const useStytchMemberSession = () => {
75
75
  invariant(useIsMounted__INTERNAL(), noProviderError('useStytchMemberSession', 'StytchB2BProvider'));
76
76
  return useContext(StytchMemberSessionContext);
77
77
  };
78
+ /**
79
+ * Determines whether the logged-in member is allowed to perform the specified action on the specified resource.
80
+ * Returns `true` if the member can perform the action, `false` otherwise.
81
+ *
82
+ * If the member is not logged in, this method will always return false.
83
+ * If the resource or action provided are not valid for the configured RBAC policy, this method will return false.
84
+ *
85
+ * Remember - authorization checks for sensitive actions should always occur on the backend as well.
86
+ * @example
87
+ * const isAuthorized = useStytchIsAuthorized<Permissions>('documents', 'edit');
88
+ * return <button disabled={!isAuthorized}>Edit</button>
89
+ */
90
+ const useStytchIsAuthorized = (resourceId, action) => {
91
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchIsAuthorized', 'StytchB2BProvider'));
92
+ const client = useStytchB2BClient();
93
+ const { session } = useStytchMemberSession();
94
+ const [isAuthorized, setIsAuthorized] = useAsyncState({
95
+ fromCache: true,
96
+ isAuthorized: client.rbac.isAuthorizedSync(resourceId, action),
97
+ });
98
+ useEffect(() => {
99
+ client.rbac.isAuthorized(resourceId, action).then((isAuthorized) => setIsAuthorized({
100
+ fromCache: false,
101
+ isAuthorized,
102
+ }));
103
+ }, [client, session === null || session === void 0 ? void 0 : session.roles, resourceId, action]);
104
+ return isAuthorized;
105
+ };
78
106
  /**
79
107
  * Returns the Stytch B2B client stored in the Stytch context.
80
108
  *
@@ -115,6 +143,46 @@ const withStytchMemberSession = (Component) => {
115
143
  WithStytchSession.displayName = `withStytchSession(${Component.displayName || Component.name || 'Component'})`;
116
144
  return WithStytchSession;
117
145
  };
146
+ /**
147
+ * Wrap your component with this HOC in order to receive the permissions for the logged-in member.
148
+ * Evaluates all permissions granted to the logged-in member.
149
+ * Returns a Record<RoleId, Record<Action, boolean>> response indicating the member's permissions.
150
+ * Each boolean will be `true` if the member can perform the action, `false` otherwise.
151
+ *
152
+ * If the member is not logged in, all values will be false.
153
+ *
154
+ * Remember - authorization checks for sensitive actions should always occur on the backend as well.
155
+ * @example
156
+ * type Permissions = {
157
+ * document: 'create' | 'read' | 'write
158
+ * image: 'create' | 'read'
159
+ * }
160
+ *
161
+ * const MyComponent = (props) => {
162
+ * const canEditDocuments = props.stytchPermissions.document.edit;
163
+ * const canReadImages = props.stytchPermissions.image.read;
164
+ * }
165
+ * return withStytchPermissions<Permissions>(MyComponent)
166
+ */
167
+ const withStytchPermissions = (Component) => {
168
+ const WithStytchPermissions = (props) => {
169
+ invariant(useIsMounted__INTERNAL(), noProviderError('useRBACPermissions', 'StytchB2BProvider'));
170
+ const client = useStytchB2BClient();
171
+ const { session } = useStytchMemberSession();
172
+ const [permissions, setPermissions] = useAsyncState({ loaded: false, value: null });
173
+ useEffect(() => {
174
+ client.rbac
175
+ .allPermissions()
176
+ .then((permissions) => setPermissions({ loaded: true, value: permissions }));
177
+ }, [client, session === null || session === void 0 ? void 0 : session.roles]);
178
+ if (!permissions.loaded) {
179
+ return null;
180
+ }
181
+ return React.createElement(Component, Object.assign({}, props, { stytchPermissions: permissions.value }));
182
+ };
183
+ WithStytchPermissions.displayName = `withStytchPermissions(${Component.displayName || Component.name || 'Component'})`;
184
+ return WithStytchPermissions;
185
+ };
118
186
  /**
119
187
  * The Stytch Context Provider.
120
188
  * Wrap your application with this component in the root file in order to use Stytch everywhere in your app.
@@ -133,7 +201,7 @@ const StytchB2BProvider = ({ stytch, children }) => {
133
201
  invariant(typeof window !== 'undefined', noSSRError);
134
202
  const ctx = useMemo(() => ({ client: stytch, isMounted: true }), [stytch]);
135
203
  const [member, setMember] = useAsyncState({
136
- member: stytch.member.getSync(),
204
+ member: stytch.self.getSync(),
137
205
  fromCache: true,
138
206
  });
139
207
  const [session, setMemberSession] = useAsyncState({
@@ -141,7 +209,7 @@ const StytchB2BProvider = ({ stytch, children }) => {
141
209
  fromCache: true,
142
210
  });
143
211
  useEffect(() => {
144
- const unsubscribeMember = stytch.member.onChange((member) => setMember({
212
+ const unsubscribeMember = stytch.self.onChange((member) => setMember({
145
213
  member,
146
214
  fromCache: false,
147
215
  }));
@@ -221,4 +289,4 @@ const StytchB2B = ({ styles, callbacks, config }) => {
221
289
  return React.createElement("div", { ref: containerEl });
222
290
  };
223
291
 
224
- export { StytchB2B, StytchB2BProvider, useStytchB2BClient, useStytchMember, useStytchMemberSession, withStytchB2BClient, withStytchMember, withStytchMemberSession };
292
+ export { StytchB2B, StytchB2BProvider, useStytchB2BClient, useStytchIsAuthorized, useStytchMember, useStytchMemberSession, withStytchB2BClient, withStytchMember, withStytchMemberSession, withStytchPermissions };
package/dist/b2b/index.js CHANGED
@@ -83,6 +83,34 @@ const useStytchMemberSession = () => {
83
83
  invariant(useIsMounted__INTERNAL(), noProviderError('useStytchMemberSession', 'StytchB2BProvider'));
84
84
  return React.useContext(StytchMemberSessionContext);
85
85
  };
86
+ /**
87
+ * Determines whether the logged-in member is allowed to perform the specified action on the specified resource.
88
+ * Returns `true` if the member can perform the action, `false` otherwise.
89
+ *
90
+ * If the member is not logged in, this method will always return false.
91
+ * If the resource or action provided are not valid for the configured RBAC policy, this method will return false.
92
+ *
93
+ * Remember - authorization checks for sensitive actions should always occur on the backend as well.
94
+ * @example
95
+ * const isAuthorized = useStytchIsAuthorized<Permissions>('documents', 'edit');
96
+ * return <button disabled={!isAuthorized}>Edit</button>
97
+ */
98
+ const useStytchIsAuthorized = (resourceId, action) => {
99
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchIsAuthorized', 'StytchB2BProvider'));
100
+ const client = useStytchB2BClient();
101
+ const { session } = useStytchMemberSession();
102
+ const [isAuthorized, setIsAuthorized] = useAsyncState({
103
+ fromCache: true,
104
+ isAuthorized: client.rbac.isAuthorizedSync(resourceId, action),
105
+ });
106
+ React.useEffect(() => {
107
+ client.rbac.isAuthorized(resourceId, action).then((isAuthorized) => setIsAuthorized({
108
+ fromCache: false,
109
+ isAuthorized,
110
+ }));
111
+ }, [client, session === null || session === void 0 ? void 0 : session.roles, resourceId, action]);
112
+ return isAuthorized;
113
+ };
86
114
  /**
87
115
  * Returns the Stytch B2B client stored in the Stytch context.
88
116
  *
@@ -123,6 +151,46 @@ const withStytchMemberSession = (Component) => {
123
151
  WithStytchSession.displayName = `withStytchSession(${Component.displayName || Component.name || 'Component'})`;
124
152
  return WithStytchSession;
125
153
  };
154
+ /**
155
+ * Wrap your component with this HOC in order to receive the permissions for the logged-in member.
156
+ * Evaluates all permissions granted to the logged-in member.
157
+ * Returns a Record<RoleId, Record<Action, boolean>> response indicating the member's permissions.
158
+ * Each boolean will be `true` if the member can perform the action, `false` otherwise.
159
+ *
160
+ * If the member is not logged in, all values will be false.
161
+ *
162
+ * Remember - authorization checks for sensitive actions should always occur on the backend as well.
163
+ * @example
164
+ * type Permissions = {
165
+ * document: 'create' | 'read' | 'write
166
+ * image: 'create' | 'read'
167
+ * }
168
+ *
169
+ * const MyComponent = (props) => {
170
+ * const canEditDocuments = props.stytchPermissions.document.edit;
171
+ * const canReadImages = props.stytchPermissions.image.read;
172
+ * }
173
+ * return withStytchPermissions<Permissions>(MyComponent)
174
+ */
175
+ const withStytchPermissions = (Component) => {
176
+ const WithStytchPermissions = (props) => {
177
+ invariant(useIsMounted__INTERNAL(), noProviderError('useRBACPermissions', 'StytchB2BProvider'));
178
+ const client = useStytchB2BClient();
179
+ const { session } = useStytchMemberSession();
180
+ const [permissions, setPermissions] = useAsyncState({ loaded: false, value: null });
181
+ React.useEffect(() => {
182
+ client.rbac
183
+ .allPermissions()
184
+ .then((permissions) => setPermissions({ loaded: true, value: permissions }));
185
+ }, [client, session === null || session === void 0 ? void 0 : session.roles]);
186
+ if (!permissions.loaded) {
187
+ return null;
188
+ }
189
+ return React__default['default'].createElement(Component, Object.assign({}, props, { stytchPermissions: permissions.value }));
190
+ };
191
+ WithStytchPermissions.displayName = `withStytchPermissions(${Component.displayName || Component.name || 'Component'})`;
192
+ return WithStytchPermissions;
193
+ };
126
194
  /**
127
195
  * The Stytch Context Provider.
128
196
  * Wrap your application with this component in the root file in order to use Stytch everywhere in your app.
@@ -141,7 +209,7 @@ const StytchB2BProvider = ({ stytch, children }) => {
141
209
  invariant(typeof window !== 'undefined', noSSRError);
142
210
  const ctx = React.useMemo(() => ({ client: stytch, isMounted: true }), [stytch]);
143
211
  const [member, setMember] = useAsyncState({
144
- member: stytch.member.getSync(),
212
+ member: stytch.self.getSync(),
145
213
  fromCache: true,
146
214
  });
147
215
  const [session, setMemberSession] = useAsyncState({
@@ -149,7 +217,7 @@ const StytchB2BProvider = ({ stytch, children }) => {
149
217
  fromCache: true,
150
218
  });
151
219
  React.useEffect(() => {
152
- const unsubscribeMember = stytch.member.onChange((member) => setMember({
220
+ const unsubscribeMember = stytch.self.onChange((member) => setMember({
153
221
  member,
154
222
  fromCache: false,
155
223
  }));
@@ -232,8 +300,10 @@ const StytchB2B = ({ styles, callbacks, config }) => {
232
300
  exports.StytchB2B = StytchB2B;
233
301
  exports.StytchB2BProvider = StytchB2BProvider;
234
302
  exports.useStytchB2BClient = useStytchB2BClient;
303
+ exports.useStytchIsAuthorized = useStytchIsAuthorized;
235
304
  exports.useStytchMember = useStytchMember;
236
305
  exports.useStytchMemberSession = useStytchMemberSession;
237
306
  exports.withStytchB2BClient = withStytchB2BClient;
238
307
  exports.withStytchMember = withStytchMember;
239
308
  exports.withStytchMemberSession = withStytchMemberSession;
309
+ exports.withStytchPermissions = withStytchPermissions;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stytch/react",
3
- "version": "12.0.0",
3
+ "version": "13.0.0",
4
4
  "description": "Stytch's official React Library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
@@ -33,7 +33,7 @@
33
33
  ],
34
34
  "devDependencies": {
35
35
  "@babel/runtime": "7.18.6",
36
- "@stytch/vanilla-js": "3.2.0",
36
+ "@stytch/vanilla-js": "3.3.0",
37
37
  "@testing-library/react": "14.0.0",
38
38
  "eslint-config-custom": "0.0.1",
39
39
  "react-test-renderer": "18.0.0",
@@ -41,7 +41,7 @@
41
41
  "typescript": "4.7.4"
42
42
  },
43
43
  "peerDependencies": {
44
- "@stytch/vanilla-js": "^3.2.0",
44
+ "@stytch/vanilla-js": "^3.3.0",
45
45
  "react": ">= 17.0.2",
46
46
  "react-dom": ">= 17.0.2"
47
47
  }