@monocloud/auth-nextjs 0.1.6 → 0.1.8
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/README.md +1 -1
- package/dist/client/index.cjs +3 -3
- package/dist/client/index.d.mts +68 -112
- package/dist/client/index.mjs +2 -2
- package/dist/components/client/index.cjs +40 -84
- package/dist/components/client/index.cjs.map +1 -1
- package/dist/components/client/index.d.mts +55 -90
- package/dist/components/client/index.mjs +38 -82
- package/dist/components/client/index.mjs.map +1 -1
- package/dist/components/index.cjs +44 -41
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.mts +68 -45
- package/dist/components/index.mjs +44 -41
- package/dist/components/index.mjs.map +1 -1
- package/dist/index.cjs +384 -365
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +1545 -1833
- package/dist/index.mjs +380 -372
- package/dist/index.mjs.map +1 -1
- package/dist/{protect-K9srvUkq.mjs → protect-client-page-BFVskb3X.mjs} +58 -106
- package/dist/protect-client-page-BFVskb3X.mjs.map +1 -0
- package/dist/{protect-BCIji2i7.cjs → protect-client-page-BdsnH8gs.cjs} +59 -107
- package/dist/protect-client-page-BdsnH8gs.cjs.map +1 -0
- package/dist/types-ClljFIvK.d.mts +543 -0
- package/package.json +3 -2
- package/dist/protect-BCIji2i7.cjs.map +0 -1
- package/dist/protect-K9srvUkq.mjs.map +0 -1
- package/dist/types-Cx32VRoI.d.mts +0 -409
|
@@ -11,32 +11,33 @@ const fetchUser = async (url) => {
|
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* `useAuth()` is a client-side hook that provides access to the current authentication state.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
16
|
+
* It can only be used inside **Client Components**.
|
|
17
17
|
*
|
|
18
|
-
* @example
|
|
19
|
-
*
|
|
20
|
-
* ```tsx
|
|
18
|
+
* @example Basic Usage
|
|
19
|
+
* ```tsx title="Basic Usage"
|
|
21
20
|
* "use client";
|
|
22
21
|
*
|
|
23
22
|
* import { useAuth } from "@monocloud/auth-nextjs/client";
|
|
24
23
|
*
|
|
25
24
|
* export default function Home() {
|
|
26
|
-
* const { user } = useAuth();
|
|
25
|
+
* const { user, isAuthenticated } = useAuth();
|
|
26
|
+
*
|
|
27
|
+
* if (!isAuthenticated) {
|
|
28
|
+
* return <>Not signed in</>;
|
|
29
|
+
* }
|
|
27
30
|
*
|
|
28
31
|
* return <>User Id: {user?.sub}</>;
|
|
29
32
|
* }
|
|
30
33
|
* ```
|
|
31
34
|
*
|
|
32
|
-
* @example
|
|
35
|
+
* @example Refetch user
|
|
33
36
|
*
|
|
34
|
-
* Calling `refetch(true)`
|
|
35
|
-
*
|
|
37
|
+
* Calling `refetch(true)` forces a refresh of the user profile from the `UserInfo` endpoint.
|
|
38
|
+
* Calling `refetch()` refreshes authentication state without forcing a `UserInfo` request.
|
|
36
39
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* ```tsx
|
|
40
|
+
* ```tsx title="Refetch User"
|
|
40
41
|
* "use client";
|
|
41
42
|
*
|
|
42
43
|
* import { useAuth } from "@monocloud/auth-nextjs/client";
|
|
@@ -46,47 +47,16 @@ const fetchUser = async (url) => {
|
|
|
46
47
|
*
|
|
47
48
|
* return (
|
|
48
49
|
* <>
|
|
49
|
-
* <pre>{JSON.stringify(user)}</pre>
|
|
50
|
-
* <button onClick={() => refetch(true)}>Refresh</button>
|
|
50
|
+
* <pre>{JSON.stringify(user, null, 2)}</pre>
|
|
51
|
+
* <button onClick={() => refetch(true)}>Refresh Profile</button>
|
|
51
52
|
* </>
|
|
52
53
|
* );
|
|
53
54
|
* }
|
|
54
55
|
* ```
|
|
55
56
|
*
|
|
56
|
-
* @
|
|
57
|
-
*
|
|
58
|
-
* ```tsx
|
|
59
|
-
* import { useAuth } from "@monocloud/auth-nextjs/client";
|
|
60
|
-
*
|
|
61
|
-
* export default function Home() {
|
|
62
|
-
* const { user } = useAuth();
|
|
63
|
-
*
|
|
64
|
-
* return <>User Id: {user?.sub}</>;
|
|
65
|
-
* }
|
|
66
|
-
* ```
|
|
67
|
-
*
|
|
68
|
-
* @example Pages Router - Refetch user from Userinfo endpoint
|
|
69
|
-
*
|
|
70
|
-
* Calling `refetch(true)` will force refresh the user's profile from the userinfo endpoint.
|
|
71
|
-
* If you do not intent to refersh from your tenants userinfo endpoint, use just `refetch()`
|
|
72
|
-
*
|
|
73
|
-
* **Note⚠️: You need to set the env `MONOCLOUD_AUTH_ALLOW_QUERY_PARAM_OVERRIDES=true` or `allowQueryParamOverrides` should be `true` in the client initialization for force refresh to work**
|
|
74
|
-
*
|
|
75
|
-
* ```tsx
|
|
76
|
-
* import { useAuth } from "@monocloud/auth-nextjs/client";
|
|
77
|
-
*
|
|
78
|
-
* export default function Home() {
|
|
79
|
-
* const { user, refetch } = useAuth();
|
|
80
|
-
*
|
|
81
|
-
* return (
|
|
82
|
-
* <>
|
|
83
|
-
* <pre>{JSON.stringify(user)}</pre>
|
|
84
|
-
* <button onClick={() => refetch(true)}>Refresh</button>
|
|
85
|
-
* </>
|
|
86
|
-
* );
|
|
87
|
-
* }
|
|
88
|
-
* ```
|
|
57
|
+
* @returns
|
|
89
58
|
*
|
|
59
|
+
* @category Hooks
|
|
90
60
|
*/
|
|
91
61
|
const useAuth = () => {
|
|
92
62
|
const key = process.env.NEXT_PUBLIC_MONOCLOUD_AUTH_USER_INFO_URL ?? `${process.env.__NEXT_ROUTER_BASEPATH ?? ""}/api/auth/userinfo`;
|
|
@@ -120,7 +90,7 @@ const useAuth = () => {
|
|
|
120
90
|
};
|
|
121
91
|
|
|
122
92
|
//#endregion
|
|
123
|
-
//#region src/client/protect.tsx
|
|
93
|
+
//#region src/client/protect-client-page.tsx
|
|
124
94
|
const redirectToSignIn = (options) => {
|
|
125
95
|
const searchParams = new URLSearchParams(window.location.search);
|
|
126
96
|
searchParams.set("return_url", options.returnUrl ?? window.location.toString());
|
|
@@ -142,54 +112,53 @@ const handlePageError = (error, options) => {
|
|
|
142
112
|
throw error;
|
|
143
113
|
};
|
|
144
114
|
/**
|
|
145
|
-
*
|
|
146
|
-
* Ensures that only authenticated users can access the component.
|
|
147
|
-
*
|
|
148
|
-
* **Note⚠️: Since `window.location` is set as `returnUrl` query param by default, you need to set the env `MONOCLOUD_AUTH_ALLOW_QUERY_PARAM_OVERRIDES=true` or `allowQueryParamOverrides` should be `true` in the client initialization for returning to the same page.**
|
|
115
|
+
* `protectClientPage()` wraps a **client-rendered page component** and ensures that only authenticated users can access it.
|
|
149
116
|
*
|
|
150
|
-
*
|
|
151
|
-
* @param options - The options.
|
|
117
|
+
* If the user is authenticated, the wrapped component receives a `user` prop.
|
|
152
118
|
*
|
|
153
|
-
*
|
|
119
|
+
* > This function runs on the client and controls rendering only.
|
|
120
|
+
* > To enforce access before rendering (server-side), use the server {@link MonoCloudNextClient.protectPage | protectPage()} method on {@link MonoCloudNextClient}.
|
|
154
121
|
*
|
|
155
|
-
* @example
|
|
122
|
+
* @example Basic Usage
|
|
156
123
|
*
|
|
157
|
-
* ```tsx
|
|
124
|
+
* ```tsx title="Basic Usage"
|
|
158
125
|
* "use client";
|
|
159
126
|
*
|
|
160
|
-
* import {
|
|
127
|
+
* import { protectClientPage } from "@monocloud/auth-nextjs/client";
|
|
161
128
|
*
|
|
162
|
-
* export default
|
|
163
|
-
* return <>
|
|
129
|
+
* export default protectClientPage(function Home({ user }) {
|
|
130
|
+
* return <>Signed in as {user.email}</>;
|
|
164
131
|
* });
|
|
165
132
|
* ```
|
|
166
133
|
*
|
|
167
|
-
* @example
|
|
168
|
-
*
|
|
169
|
-
* See {@link ProtectPageOptions} for more options.
|
|
134
|
+
* @example With Options
|
|
170
135
|
*
|
|
171
|
-
* ```tsx
|
|
136
|
+
* ```tsx title="With Options"
|
|
172
137
|
* "use client";
|
|
173
138
|
*
|
|
174
|
-
* import {
|
|
139
|
+
* import { protectClientPage } from "@monocloud/auth-nextjs/client";
|
|
175
140
|
*
|
|
176
|
-
* export default
|
|
177
|
-
* function Home() {
|
|
178
|
-
* return <>
|
|
141
|
+
* export default protectClientPage(
|
|
142
|
+
* function Home({ user }) {
|
|
143
|
+
* return <>Signed in as {user.email}</>;
|
|
179
144
|
* },
|
|
180
|
-
* {
|
|
145
|
+
* {
|
|
146
|
+
* returnUrl: "/dashboard",
|
|
147
|
+
* authParams: { loginHint: "user@example.com" }
|
|
148
|
+
* }
|
|
181
149
|
* );
|
|
182
150
|
* ```
|
|
183
|
-
* @example Fallback with onAccessDenied
|
|
184
151
|
*
|
|
185
|
-
*
|
|
152
|
+
* @example Custom access denied UI
|
|
153
|
+
*
|
|
154
|
+
* ```tsx title="Custom access denied UI"
|
|
186
155
|
* "use client";
|
|
187
156
|
*
|
|
188
|
-
* import {
|
|
157
|
+
* import { protectClientPage } from "@monocloud/auth-nextjs/client";
|
|
189
158
|
*
|
|
190
|
-
* export default
|
|
191
|
-
* function Home() {
|
|
192
|
-
* return <>
|
|
159
|
+
* export default protectClientPage(
|
|
160
|
+
* function Home({ user }) {
|
|
161
|
+
* return <>Signed in as {user.email}</>;
|
|
193
162
|
* },
|
|
194
163
|
* {
|
|
195
164
|
* onAccessDenied: () => <div>Please sign in to continue</div>
|
|
@@ -197,16 +166,16 @@ const handlePageError = (error, options) => {
|
|
|
197
166
|
* );
|
|
198
167
|
* ```
|
|
199
168
|
*
|
|
200
|
-
* @example Group
|
|
169
|
+
* @example Group protection
|
|
201
170
|
*
|
|
202
|
-
* ```tsx
|
|
171
|
+
* ```tsx title="Group protection"
|
|
203
172
|
* "use client";
|
|
204
173
|
*
|
|
205
|
-
* import {
|
|
174
|
+
* import { protectClientPage } from "@monocloud/auth-nextjs/client";
|
|
206
175
|
*
|
|
207
|
-
* export default
|
|
208
|
-
* function Home() {
|
|
209
|
-
* return <>Welcome Admin</>;
|
|
176
|
+
* export default protectClientPage(
|
|
177
|
+
* function Home({ user }) {
|
|
178
|
+
* return <>Welcome Admin {user.email}</>;
|
|
210
179
|
* },
|
|
211
180
|
* {
|
|
212
181
|
* groups: ["admin"],
|
|
@@ -215,32 +184,15 @@ const handlePageError = (error, options) => {
|
|
|
215
184
|
* );
|
|
216
185
|
* ```
|
|
217
186
|
*
|
|
218
|
-
* @
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
* export default protectPage(function Home() {
|
|
224
|
-
* return <>You are signed in</>;
|
|
225
|
-
* });
|
|
226
|
-
* ```
|
|
227
|
-
*
|
|
228
|
-
* @example Pages Router with options
|
|
229
|
-
*
|
|
230
|
-
* See {@link ProtectPageOptions} for more options.
|
|
187
|
+
* @param Component - The page component to protect
|
|
188
|
+
* @typeParam P - Props of the protected component (excluding `user`).
|
|
189
|
+
* @param options - Optional configuration
|
|
190
|
+
* @returns A protected React component.
|
|
231
191
|
*
|
|
232
|
-
*
|
|
233
|
-
* import { protectPage } from "@monocloud/auth-nextjs/client";
|
|
192
|
+
* @category Functions
|
|
234
193
|
*
|
|
235
|
-
* export default protectPage(
|
|
236
|
-
* function Home() {
|
|
237
|
-
* return <>You are signed in</>;
|
|
238
|
-
* },
|
|
239
|
-
* { returnUrl: "/dashboard", authParams: { loginHint: "username" } }
|
|
240
|
-
* );
|
|
241
|
-
* ```
|
|
242
194
|
*/
|
|
243
|
-
const
|
|
195
|
+
const protectClientPage = (Component, options) => {
|
|
244
196
|
return (props) => {
|
|
245
197
|
const { user, error, isLoading } = useAuth();
|
|
246
198
|
useEffect(() => {
|
|
@@ -274,5 +226,5 @@ const protectPage = (Component, options) => {
|
|
|
274
226
|
};
|
|
275
227
|
|
|
276
228
|
//#endregion
|
|
277
|
-
export { redirectToSignIn as n, useAuth as r,
|
|
278
|
-
//# sourceMappingURL=protect-
|
|
229
|
+
export { redirectToSignIn as n, useAuth as r, protectClientPage as t };
|
|
230
|
+
//# sourceMappingURL=protect-client-page-BFVskb3X.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protect-client-page-BFVskb3X.mjs","names":[],"sources":["../src/client/use-auth.tsx","../src/client/protect-client-page.tsx"],"sourcesContent":["'use client';\n\nimport type { MonoCloudUser } from '@monocloud/auth-node-core';\nimport useSWR from 'swr';\n\n/**\n * Authentication State returned by `useAuth` hook.\n *\n * @category Types\n */\nexport interface AuthenticationState {\n /**\n * Flag indicating if the authentication state is still loading.\n */\n isLoading: boolean;\n /**\n * Flag indicating if the user is authenticated.\n */\n isAuthenticated: boolean;\n /**\n * Error encountered during authentication, if any.\n */\n error?: Error;\n /**\n * The authenticated user's information, if available.\n */\n user?: MonoCloudUser;\n /**\n * Function to refetch the authentication state.\n *\n */\n refetch: (refresh?: boolean) => void;\n}\n\nconst fetchUser = async (url: string): Promise<MonoCloudUser | undefined> => {\n const res = await fetch(url, { credentials: 'include' });\n\n if (res.status === 204) {\n return undefined;\n }\n\n if (res.ok) {\n return res.json();\n }\n\n throw new Error('Failed to fetch user');\n};\n\n/**\n *\n * `useAuth()` is a client-side hook that provides access to the current authentication state.\n *\n * It can only be used inside **Client Components**.\n *\n * @example Basic Usage\n * ```tsx title=\"Basic Usage\"\n * \"use client\";\n *\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user, isAuthenticated } = useAuth();\n *\n * if (!isAuthenticated) {\n * return <>Not signed in</>;\n * }\n *\n * return <>User Id: {user?.sub}</>;\n * }\n * ```\n *\n * @example Refetch user\n *\n * Calling `refetch(true)` forces a refresh of the user profile from the `UserInfo` endpoint.\n * Calling `refetch()` refreshes authentication state without forcing a `UserInfo` request.\n *\n * ```tsx title=\"Refetch User\"\n * \"use client\";\n *\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user, refetch } = useAuth();\n *\n * return (\n * <>\n * <pre>{JSON.stringify(user, null, 2)}</pre>\n * <button onClick={() => refetch(true)}>Refresh Profile</button>\n * </>\n * );\n * }\n * ```\n *\n * @returns\n *\n * @category Hooks\n */\nexport const useAuth = (): AuthenticationState => {\n const key =\n process.env.NEXT_PUBLIC_MONOCLOUD_AUTH_USER_INFO_URL ??\n // eslint-disable-next-line no-underscore-dangle\n `${process.env.__NEXT_ROUTER_BASEPATH ?? ''}/api/auth/userinfo`;\n\n const { data, error, isLoading, mutate } = useSWR<MonoCloudUser | undefined>(\n key,\n fetchUser\n );\n\n const refetch = (refresh?: boolean): void => {\n const url = new URL(key, 'https://dummy');\n if (refresh) {\n url.searchParams.set('refresh', 'true');\n }\n\n void mutate(async () => await fetchUser(url.pathname + url.search), {\n revalidate: false,\n });\n };\n\n if (error) {\n return {\n user: undefined,\n isLoading: false,\n isAuthenticated: false,\n error: error as Error,\n refetch,\n };\n }\n\n if (data) {\n return {\n user: data,\n isLoading,\n isAuthenticated: !!data && Object.keys(data).length > 0,\n error: undefined,\n refetch,\n };\n }\n\n return {\n user: undefined,\n isLoading,\n isAuthenticated: false,\n error: undefined,\n /* v8 ignore next -- @preserve */\n refetch: (): void => {},\n };\n};\n","/* eslint-disable react/display-name */\n'use client';\n\nimport React, { ComponentType, useEffect } from 'react';\nimport type { MonoCloudUser } from '@monocloud/auth-node-core';\nimport { isUserInGroup } from '@monocloud/auth-node-core/utils';\nimport { useAuth } from './use-auth';\nimport { ExtraAuthParams, GroupOptions } from '../types';\nimport type { MonoCloudNextClient } from '../monocloud-next-client';\n\n/**\n * Options for configuring page protection.\n *\n * @category Types\n */\nexport interface ProtectClientPageOptions extends GroupOptions {\n /**\n * The URL where the user will be redirected to after sign in.\n */\n returnUrl?: string;\n\n /**\n * A custom react element to render when the user is not authenticated.\n */\n onAccessDenied?: () => React.ReactNode;\n\n /**\n * A custom react element to render when the user is authenticated but does not belong to the required groups.\n */\n onGroupAccessDenied?: (user: MonoCloudUser) => React.ReactNode;\n\n /**\n * Authorization parameters to be used during authentication.\n */\n authParams?: ExtraAuthParams;\n\n /**\n * Callback function to handle errors.\n * If not provided, errors will be thrown.\n *\n * @param error - The error object.\n * @returns JSX element to handle the error.\n */\n onError?: (error: Error) => React.ReactNode;\n}\n\nexport const redirectToSignIn = (\n options: { returnUrl?: string } & ExtraAuthParams\n): void => {\n const searchParams = new URLSearchParams(window.location.search);\n searchParams.set(\n 'return_url',\n options.returnUrl ?? window.location.toString()\n );\n\n if (options?.scopes) {\n searchParams.set('scope', options.scopes);\n }\n if (options?.resource) {\n searchParams.set('resource', options.resource);\n }\n\n if (options?.acrValues) {\n searchParams.set('acr_values', options.acrValues.join(' '));\n }\n\n if (options?.display) {\n searchParams.set('display', options.display);\n }\n\n if (options?.prompt) {\n searchParams.set('prompt', options.prompt);\n }\n\n if (options?.authenticatorHint) {\n searchParams.set('authenticator_hint', options.authenticatorHint);\n }\n\n if (options?.uiLocales) {\n searchParams.set('ui_locales', options.uiLocales);\n }\n\n if (options?.maxAge) {\n searchParams.set('max_age', options.maxAge.toString());\n }\n\n if (options?.loginHint) {\n searchParams.set('login_hint', options.loginHint);\n }\n\n window.location.assign(\n // eslint-disable-next-line no-underscore-dangle\n `${process.env.NEXT_PUBLIC_MONOCLOUD_AUTH_SIGNIN_URL ?? `${process.env.__NEXT_ROUTER_BASEPATH ?? ''}/api/auth/signin`}?${searchParams.toString()}`\n );\n};\n\nconst handlePageError = (\n error: Error,\n options?: ProtectClientPageOptions\n): React.ReactNode => {\n /* v8 ignore else -- @preserve */\n if (options?.onError) {\n return options.onError(error);\n }\n\n /* v8 ignore next -- @preserve */\n throw error;\n};\n\n/**\n * `protectClientPage()` wraps a **client-rendered page component** and ensures that only authenticated users can access it.\n *\n * If the user is authenticated, the wrapped component receives a `user` prop.\n *\n * > This function runs on the client and controls rendering only.\n * > To enforce access before rendering (server-side), use the server {@link MonoCloudNextClient.protectPage | protectPage()} method on {@link MonoCloudNextClient}.\n *\n * @example Basic Usage\n *\n * ```tsx title=\"Basic Usage\"\n * \"use client\";\n *\n * import { protectClientPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectClientPage(function Home({ user }) {\n * return <>Signed in as {user.email}</>;\n * });\n * ```\n *\n * @example With Options\n *\n * ```tsx title=\"With Options\"\n * \"use client\";\n *\n * import { protectClientPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectClientPage(\n * function Home({ user }) {\n * return <>Signed in as {user.email}</>;\n * },\n * {\n * returnUrl: \"/dashboard\",\n * authParams: { loginHint: \"user@example.com\" }\n * }\n * );\n * ```\n *\n * @example Custom access denied UI\n *\n * ```tsx title=\"Custom access denied UI\"\n * \"use client\";\n *\n * import { protectClientPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectClientPage(\n * function Home({ user }) {\n * return <>Signed in as {user.email}</>;\n * },\n * {\n * onAccessDenied: () => <div>Please sign in to continue</div>\n * }\n * );\n * ```\n *\n * @example Group protection\n *\n * ```tsx title=\"Group protection\"\n * \"use client\";\n *\n * import { protectClientPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectClientPage(\n * function Home({ user }) {\n * return <>Welcome Admin {user.email}</>;\n * },\n * {\n * groups: [\"admin\"],\n * onGroupAccessDenied: (user) => <div>User {user.email} is not an admin</div>\n * }\n * );\n * ```\n *\n * @param Component - The page component to protect\n * @typeParam P - Props of the protected component (excluding `user`).\n * @param options - Optional configuration\n * @returns A protected React component.\n *\n * @category Functions\n *\n */\nexport const protectClientPage = <P extends object>(\n Component: ComponentType<P & { user: MonoCloudUser }>,\n options?: ProtectClientPageOptions\n): React.FC<P> => {\n return props => {\n const { user, error, isLoading } = useAuth();\n\n useEffect(() => {\n if (!user && !isLoading && !error) {\n if (options?.onAccessDenied) {\n return;\n }\n\n const authParams = options?.authParams ?? {};\n redirectToSignIn({\n returnUrl: options?.returnUrl,\n ...authParams,\n });\n }\n }, [user, isLoading, error]);\n\n if (error) {\n return handlePageError(error, options);\n }\n\n if (!user && !isLoading && options?.onAccessDenied) {\n return options.onAccessDenied();\n }\n\n if (user) {\n if (\n options?.groups &&\n !isUserInGroup(\n user,\n options.groups,\n options.groupsClaim ??\n process.env.NEXT_PUBLIC_MONOCLOUD_AUTH_GROUPS_CLAIM,\n options.matchAll\n )\n ) {\n const {\n onGroupAccessDenied = (): React.ReactNode => <div>Access Denied</div>,\n } = options;\n return onGroupAccessDenied(user);\n }\n\n return <Component user={user} {...props} />;\n }\n\n return null;\n };\n};\n"],"mappings":"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"}
|
|
@@ -14,32 +14,33 @@ const fetchUser = async (url) => {
|
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
16
|
*
|
|
17
|
-
*
|
|
17
|
+
* `useAuth()` is a client-side hook that provides access to the current authentication state.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
19
|
+
* It can only be used inside **Client Components**.
|
|
20
20
|
*
|
|
21
|
-
* @example
|
|
22
|
-
*
|
|
23
|
-
* ```tsx
|
|
21
|
+
* @example Basic Usage
|
|
22
|
+
* ```tsx title="Basic Usage"
|
|
24
23
|
* "use client";
|
|
25
24
|
*
|
|
26
25
|
* import { useAuth } from "@monocloud/auth-nextjs/client";
|
|
27
26
|
*
|
|
28
27
|
* export default function Home() {
|
|
29
|
-
* const { user } = useAuth();
|
|
28
|
+
* const { user, isAuthenticated } = useAuth();
|
|
29
|
+
*
|
|
30
|
+
* if (!isAuthenticated) {
|
|
31
|
+
* return <>Not signed in</>;
|
|
32
|
+
* }
|
|
30
33
|
*
|
|
31
34
|
* return <>User Id: {user?.sub}</>;
|
|
32
35
|
* }
|
|
33
36
|
* ```
|
|
34
37
|
*
|
|
35
|
-
* @example
|
|
38
|
+
* @example Refetch user
|
|
36
39
|
*
|
|
37
|
-
* Calling `refetch(true)`
|
|
38
|
-
*
|
|
40
|
+
* Calling `refetch(true)` forces a refresh of the user profile from the `UserInfo` endpoint.
|
|
41
|
+
* Calling `refetch()` refreshes authentication state without forcing a `UserInfo` request.
|
|
39
42
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* ```tsx
|
|
43
|
+
* ```tsx title="Refetch User"
|
|
43
44
|
* "use client";
|
|
44
45
|
*
|
|
45
46
|
* import { useAuth } from "@monocloud/auth-nextjs/client";
|
|
@@ -49,47 +50,16 @@ const fetchUser = async (url) => {
|
|
|
49
50
|
*
|
|
50
51
|
* return (
|
|
51
52
|
* <>
|
|
52
|
-
* <pre>{JSON.stringify(user)}</pre>
|
|
53
|
-
* <button onClick={() => refetch(true)}>Refresh</button>
|
|
53
|
+
* <pre>{JSON.stringify(user, null, 2)}</pre>
|
|
54
|
+
* <button onClick={() => refetch(true)}>Refresh Profile</button>
|
|
54
55
|
* </>
|
|
55
56
|
* );
|
|
56
57
|
* }
|
|
57
58
|
* ```
|
|
58
59
|
*
|
|
59
|
-
* @
|
|
60
|
-
*
|
|
61
|
-
* ```tsx
|
|
62
|
-
* import { useAuth } from "@monocloud/auth-nextjs/client";
|
|
63
|
-
*
|
|
64
|
-
* export default function Home() {
|
|
65
|
-
* const { user } = useAuth();
|
|
66
|
-
*
|
|
67
|
-
* return <>User Id: {user?.sub}</>;
|
|
68
|
-
* }
|
|
69
|
-
* ```
|
|
70
|
-
*
|
|
71
|
-
* @example Pages Router - Refetch user from Userinfo endpoint
|
|
72
|
-
*
|
|
73
|
-
* Calling `refetch(true)` will force refresh the user's profile from the userinfo endpoint.
|
|
74
|
-
* If you do not intent to refersh from your tenants userinfo endpoint, use just `refetch()`
|
|
75
|
-
*
|
|
76
|
-
* **Note⚠️: You need to set the env `MONOCLOUD_AUTH_ALLOW_QUERY_PARAM_OVERRIDES=true` or `allowQueryParamOverrides` should be `true` in the client initialization for force refresh to work**
|
|
77
|
-
*
|
|
78
|
-
* ```tsx
|
|
79
|
-
* import { useAuth } from "@monocloud/auth-nextjs/client";
|
|
80
|
-
*
|
|
81
|
-
* export default function Home() {
|
|
82
|
-
* const { user, refetch } = useAuth();
|
|
83
|
-
*
|
|
84
|
-
* return (
|
|
85
|
-
* <>
|
|
86
|
-
* <pre>{JSON.stringify(user)}</pre>
|
|
87
|
-
* <button onClick={() => refetch(true)}>Refresh</button>
|
|
88
|
-
* </>
|
|
89
|
-
* );
|
|
90
|
-
* }
|
|
91
|
-
* ```
|
|
60
|
+
* @returns
|
|
92
61
|
*
|
|
62
|
+
* @category Hooks
|
|
93
63
|
*/
|
|
94
64
|
const useAuth = () => {
|
|
95
65
|
const key = process.env.NEXT_PUBLIC_MONOCLOUD_AUTH_USER_INFO_URL ?? `${process.env.__NEXT_ROUTER_BASEPATH ?? ""}/api/auth/userinfo`;
|
|
@@ -123,7 +93,7 @@ const useAuth = () => {
|
|
|
123
93
|
};
|
|
124
94
|
|
|
125
95
|
//#endregion
|
|
126
|
-
//#region src/client/protect.tsx
|
|
96
|
+
//#region src/client/protect-client-page.tsx
|
|
127
97
|
const redirectToSignIn = (options) => {
|
|
128
98
|
const searchParams = new URLSearchParams(window.location.search);
|
|
129
99
|
searchParams.set("return_url", options.returnUrl ?? window.location.toString());
|
|
@@ -145,54 +115,53 @@ const handlePageError = (error, options) => {
|
|
|
145
115
|
throw error;
|
|
146
116
|
};
|
|
147
117
|
/**
|
|
148
|
-
*
|
|
149
|
-
* Ensures that only authenticated users can access the component.
|
|
150
|
-
*
|
|
151
|
-
* **Note⚠️: Since `window.location` is set as `returnUrl` query param by default, you need to set the env `MONOCLOUD_AUTH_ALLOW_QUERY_PARAM_OVERRIDES=true` or `allowQueryParamOverrides` should be `true` in the client initialization for returning to the same page.**
|
|
118
|
+
* `protectClientPage()` wraps a **client-rendered page component** and ensures that only authenticated users can access it.
|
|
152
119
|
*
|
|
153
|
-
*
|
|
154
|
-
* @param options - The options.
|
|
120
|
+
* If the user is authenticated, the wrapped component receives a `user` prop.
|
|
155
121
|
*
|
|
156
|
-
*
|
|
122
|
+
* > This function runs on the client and controls rendering only.
|
|
123
|
+
* > To enforce access before rendering (server-side), use the server {@link MonoCloudNextClient.protectPage | protectPage()} method on {@link MonoCloudNextClient}.
|
|
157
124
|
*
|
|
158
|
-
* @example
|
|
125
|
+
* @example Basic Usage
|
|
159
126
|
*
|
|
160
|
-
* ```tsx
|
|
127
|
+
* ```tsx title="Basic Usage"
|
|
161
128
|
* "use client";
|
|
162
129
|
*
|
|
163
|
-
* import {
|
|
130
|
+
* import { protectClientPage } from "@monocloud/auth-nextjs/client";
|
|
164
131
|
*
|
|
165
|
-
* export default
|
|
166
|
-
* return <>
|
|
132
|
+
* export default protectClientPage(function Home({ user }) {
|
|
133
|
+
* return <>Signed in as {user.email}</>;
|
|
167
134
|
* });
|
|
168
135
|
* ```
|
|
169
136
|
*
|
|
170
|
-
* @example
|
|
171
|
-
*
|
|
172
|
-
* See {@link ProtectPageOptions} for more options.
|
|
137
|
+
* @example With Options
|
|
173
138
|
*
|
|
174
|
-
* ```tsx
|
|
139
|
+
* ```tsx title="With Options"
|
|
175
140
|
* "use client";
|
|
176
141
|
*
|
|
177
|
-
* import {
|
|
142
|
+
* import { protectClientPage } from "@monocloud/auth-nextjs/client";
|
|
178
143
|
*
|
|
179
|
-
* export default
|
|
180
|
-
* function Home() {
|
|
181
|
-
* return <>
|
|
144
|
+
* export default protectClientPage(
|
|
145
|
+
* function Home({ user }) {
|
|
146
|
+
* return <>Signed in as {user.email}</>;
|
|
182
147
|
* },
|
|
183
|
-
* {
|
|
148
|
+
* {
|
|
149
|
+
* returnUrl: "/dashboard",
|
|
150
|
+
* authParams: { loginHint: "user@example.com" }
|
|
151
|
+
* }
|
|
184
152
|
* );
|
|
185
153
|
* ```
|
|
186
|
-
* @example Fallback with onAccessDenied
|
|
187
154
|
*
|
|
188
|
-
*
|
|
155
|
+
* @example Custom access denied UI
|
|
156
|
+
*
|
|
157
|
+
* ```tsx title="Custom access denied UI"
|
|
189
158
|
* "use client";
|
|
190
159
|
*
|
|
191
|
-
* import {
|
|
160
|
+
* import { protectClientPage } from "@monocloud/auth-nextjs/client";
|
|
192
161
|
*
|
|
193
|
-
* export default
|
|
194
|
-
* function Home() {
|
|
195
|
-
* return <>
|
|
162
|
+
* export default protectClientPage(
|
|
163
|
+
* function Home({ user }) {
|
|
164
|
+
* return <>Signed in as {user.email}</>;
|
|
196
165
|
* },
|
|
197
166
|
* {
|
|
198
167
|
* onAccessDenied: () => <div>Please sign in to continue</div>
|
|
@@ -200,16 +169,16 @@ const handlePageError = (error, options) => {
|
|
|
200
169
|
* );
|
|
201
170
|
* ```
|
|
202
171
|
*
|
|
203
|
-
* @example Group
|
|
172
|
+
* @example Group protection
|
|
204
173
|
*
|
|
205
|
-
* ```tsx
|
|
174
|
+
* ```tsx title="Group protection"
|
|
206
175
|
* "use client";
|
|
207
176
|
*
|
|
208
|
-
* import {
|
|
177
|
+
* import { protectClientPage } from "@monocloud/auth-nextjs/client";
|
|
209
178
|
*
|
|
210
|
-
* export default
|
|
211
|
-
* function Home() {
|
|
212
|
-
* return <>Welcome Admin</>;
|
|
179
|
+
* export default protectClientPage(
|
|
180
|
+
* function Home({ user }) {
|
|
181
|
+
* return <>Welcome Admin {user.email}</>;
|
|
213
182
|
* },
|
|
214
183
|
* {
|
|
215
184
|
* groups: ["admin"],
|
|
@@ -218,32 +187,15 @@ const handlePageError = (error, options) => {
|
|
|
218
187
|
* );
|
|
219
188
|
* ```
|
|
220
189
|
*
|
|
221
|
-
* @
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* export default protectPage(function Home() {
|
|
227
|
-
* return <>You are signed in</>;
|
|
228
|
-
* });
|
|
229
|
-
* ```
|
|
230
|
-
*
|
|
231
|
-
* @example Pages Router with options
|
|
232
|
-
*
|
|
233
|
-
* See {@link ProtectPageOptions} for more options.
|
|
190
|
+
* @param Component - The page component to protect
|
|
191
|
+
* @typeParam P - Props of the protected component (excluding `user`).
|
|
192
|
+
* @param options - Optional configuration
|
|
193
|
+
* @returns A protected React component.
|
|
234
194
|
*
|
|
235
|
-
*
|
|
236
|
-
* import { protectPage } from "@monocloud/auth-nextjs/client";
|
|
195
|
+
* @category Functions
|
|
237
196
|
*
|
|
238
|
-
* export default protectPage(
|
|
239
|
-
* function Home() {
|
|
240
|
-
* return <>You are signed in</>;
|
|
241
|
-
* },
|
|
242
|
-
* { returnUrl: "/dashboard", authParams: { loginHint: "username" } }
|
|
243
|
-
* );
|
|
244
|
-
* ```
|
|
245
197
|
*/
|
|
246
|
-
const
|
|
198
|
+
const protectClientPage = (Component, options) => {
|
|
247
199
|
return (props) => {
|
|
248
200
|
const { user, error, isLoading } = useAuth();
|
|
249
201
|
(0, react.useEffect)(() => {
|
|
@@ -277,10 +229,10 @@ const protectPage = (Component, options) => {
|
|
|
277
229
|
};
|
|
278
230
|
|
|
279
231
|
//#endregion
|
|
280
|
-
Object.defineProperty(exports, '
|
|
232
|
+
Object.defineProperty(exports, 'protectClientPage', {
|
|
281
233
|
enumerable: true,
|
|
282
234
|
get: function () {
|
|
283
|
-
return
|
|
235
|
+
return protectClientPage;
|
|
284
236
|
}
|
|
285
237
|
});
|
|
286
238
|
Object.defineProperty(exports, 'redirectToSignIn', {
|
|
@@ -295,4 +247,4 @@ Object.defineProperty(exports, 'useAuth', {
|
|
|
295
247
|
return useAuth;
|
|
296
248
|
}
|
|
297
249
|
});
|
|
298
|
-
//# sourceMappingURL=protect-
|
|
250
|
+
//# sourceMappingURL=protect-client-page-BdsnH8gs.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protect-client-page-BdsnH8gs.cjs","names":[],"sources":["../src/client/use-auth.tsx","../src/client/protect-client-page.tsx"],"sourcesContent":["'use client';\n\nimport type { MonoCloudUser } from '@monocloud/auth-node-core';\nimport useSWR from 'swr';\n\n/**\n * Authentication State returned by `useAuth` hook.\n *\n * @category Types\n */\nexport interface AuthenticationState {\n /**\n * Flag indicating if the authentication state is still loading.\n */\n isLoading: boolean;\n /**\n * Flag indicating if the user is authenticated.\n */\n isAuthenticated: boolean;\n /**\n * Error encountered during authentication, if any.\n */\n error?: Error;\n /**\n * The authenticated user's information, if available.\n */\n user?: MonoCloudUser;\n /**\n * Function to refetch the authentication state.\n *\n */\n refetch: (refresh?: boolean) => void;\n}\n\nconst fetchUser = async (url: string): Promise<MonoCloudUser | undefined> => {\n const res = await fetch(url, { credentials: 'include' });\n\n if (res.status === 204) {\n return undefined;\n }\n\n if (res.ok) {\n return res.json();\n }\n\n throw new Error('Failed to fetch user');\n};\n\n/**\n *\n * `useAuth()` is a client-side hook that provides access to the current authentication state.\n *\n * It can only be used inside **Client Components**.\n *\n * @example Basic Usage\n * ```tsx title=\"Basic Usage\"\n * \"use client\";\n *\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user, isAuthenticated } = useAuth();\n *\n * if (!isAuthenticated) {\n * return <>Not signed in</>;\n * }\n *\n * return <>User Id: {user?.sub}</>;\n * }\n * ```\n *\n * @example Refetch user\n *\n * Calling `refetch(true)` forces a refresh of the user profile from the `UserInfo` endpoint.\n * Calling `refetch()` refreshes authentication state without forcing a `UserInfo` request.\n *\n * ```tsx title=\"Refetch User\"\n * \"use client\";\n *\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user, refetch } = useAuth();\n *\n * return (\n * <>\n * <pre>{JSON.stringify(user, null, 2)}</pre>\n * <button onClick={() => refetch(true)}>Refresh Profile</button>\n * </>\n * );\n * }\n * ```\n *\n * @returns\n *\n * @category Hooks\n */\nexport const useAuth = (): AuthenticationState => {\n const key =\n process.env.NEXT_PUBLIC_MONOCLOUD_AUTH_USER_INFO_URL ??\n // eslint-disable-next-line no-underscore-dangle\n `${process.env.__NEXT_ROUTER_BASEPATH ?? ''}/api/auth/userinfo`;\n\n const { data, error, isLoading, mutate } = useSWR<MonoCloudUser | undefined>(\n key,\n fetchUser\n );\n\n const refetch = (refresh?: boolean): void => {\n const url = new URL(key, 'https://dummy');\n if (refresh) {\n url.searchParams.set('refresh', 'true');\n }\n\n void mutate(async () => await fetchUser(url.pathname + url.search), {\n revalidate: false,\n });\n };\n\n if (error) {\n return {\n user: undefined,\n isLoading: false,\n isAuthenticated: false,\n error: error as Error,\n refetch,\n };\n }\n\n if (data) {\n return {\n user: data,\n isLoading,\n isAuthenticated: !!data && Object.keys(data).length > 0,\n error: undefined,\n refetch,\n };\n }\n\n return {\n user: undefined,\n isLoading,\n isAuthenticated: false,\n error: undefined,\n /* v8 ignore next -- @preserve */\n refetch: (): void => {},\n };\n};\n","/* eslint-disable react/display-name */\n'use client';\n\nimport React, { ComponentType, useEffect } from 'react';\nimport type { MonoCloudUser } from '@monocloud/auth-node-core';\nimport { isUserInGroup } from '@monocloud/auth-node-core/utils';\nimport { useAuth } from './use-auth';\nimport { ExtraAuthParams, GroupOptions } from '../types';\nimport type { MonoCloudNextClient } from '../monocloud-next-client';\n\n/**\n * Options for configuring page protection.\n *\n * @category Types\n */\nexport interface ProtectClientPageOptions extends GroupOptions {\n /**\n * The URL where the user will be redirected to after sign in.\n */\n returnUrl?: string;\n\n /**\n * A custom react element to render when the user is not authenticated.\n */\n onAccessDenied?: () => React.ReactNode;\n\n /**\n * A custom react element to render when the user is authenticated but does not belong to the required groups.\n */\n onGroupAccessDenied?: (user: MonoCloudUser) => React.ReactNode;\n\n /**\n * Authorization parameters to be used during authentication.\n */\n authParams?: ExtraAuthParams;\n\n /**\n * Callback function to handle errors.\n * If not provided, errors will be thrown.\n *\n * @param error - The error object.\n * @returns JSX element to handle the error.\n */\n onError?: (error: Error) => React.ReactNode;\n}\n\nexport const redirectToSignIn = (\n options: { returnUrl?: string } & ExtraAuthParams\n): void => {\n const searchParams = new URLSearchParams(window.location.search);\n searchParams.set(\n 'return_url',\n options.returnUrl ?? window.location.toString()\n );\n\n if (options?.scopes) {\n searchParams.set('scope', options.scopes);\n }\n if (options?.resource) {\n searchParams.set('resource', options.resource);\n }\n\n if (options?.acrValues) {\n searchParams.set('acr_values', options.acrValues.join(' '));\n }\n\n if (options?.display) {\n searchParams.set('display', options.display);\n }\n\n if (options?.prompt) {\n searchParams.set('prompt', options.prompt);\n }\n\n if (options?.authenticatorHint) {\n searchParams.set('authenticator_hint', options.authenticatorHint);\n }\n\n if (options?.uiLocales) {\n searchParams.set('ui_locales', options.uiLocales);\n }\n\n if (options?.maxAge) {\n searchParams.set('max_age', options.maxAge.toString());\n }\n\n if (options?.loginHint) {\n searchParams.set('login_hint', options.loginHint);\n }\n\n window.location.assign(\n // eslint-disable-next-line no-underscore-dangle\n `${process.env.NEXT_PUBLIC_MONOCLOUD_AUTH_SIGNIN_URL ?? `${process.env.__NEXT_ROUTER_BASEPATH ?? ''}/api/auth/signin`}?${searchParams.toString()}`\n );\n};\n\nconst handlePageError = (\n error: Error,\n options?: ProtectClientPageOptions\n): React.ReactNode => {\n /* v8 ignore else -- @preserve */\n if (options?.onError) {\n return options.onError(error);\n }\n\n /* v8 ignore next -- @preserve */\n throw error;\n};\n\n/**\n * `protectClientPage()` wraps a **client-rendered page component** and ensures that only authenticated users can access it.\n *\n * If the user is authenticated, the wrapped component receives a `user` prop.\n *\n * > This function runs on the client and controls rendering only.\n * > To enforce access before rendering (server-side), use the server {@link MonoCloudNextClient.protectPage | protectPage()} method on {@link MonoCloudNextClient}.\n *\n * @example Basic Usage\n *\n * ```tsx title=\"Basic Usage\"\n * \"use client\";\n *\n * import { protectClientPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectClientPage(function Home({ user }) {\n * return <>Signed in as {user.email}</>;\n * });\n * ```\n *\n * @example With Options\n *\n * ```tsx title=\"With Options\"\n * \"use client\";\n *\n * import { protectClientPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectClientPage(\n * function Home({ user }) {\n * return <>Signed in as {user.email}</>;\n * },\n * {\n * returnUrl: \"/dashboard\",\n * authParams: { loginHint: \"user@example.com\" }\n * }\n * );\n * ```\n *\n * @example Custom access denied UI\n *\n * ```tsx title=\"Custom access denied UI\"\n * \"use client\";\n *\n * import { protectClientPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectClientPage(\n * function Home({ user }) {\n * return <>Signed in as {user.email}</>;\n * },\n * {\n * onAccessDenied: () => <div>Please sign in to continue</div>\n * }\n * );\n * ```\n *\n * @example Group protection\n *\n * ```tsx title=\"Group protection\"\n * \"use client\";\n *\n * import { protectClientPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectClientPage(\n * function Home({ user }) {\n * return <>Welcome Admin {user.email}</>;\n * },\n * {\n * groups: [\"admin\"],\n * onGroupAccessDenied: (user) => <div>User {user.email} is not an admin</div>\n * }\n * );\n * ```\n *\n * @param Component - The page component to protect\n * @typeParam P - Props of the protected component (excluding `user`).\n * @param options - Optional configuration\n * @returns A protected React component.\n *\n * @category Functions\n *\n */\nexport const protectClientPage = <P extends object>(\n Component: ComponentType<P & { user: MonoCloudUser }>,\n options?: ProtectClientPageOptions\n): React.FC<P> => {\n return props => {\n const { user, error, isLoading } = useAuth();\n\n useEffect(() => {\n if (!user && !isLoading && !error) {\n if (options?.onAccessDenied) {\n return;\n }\n\n const authParams = options?.authParams ?? {};\n redirectToSignIn({\n returnUrl: options?.returnUrl,\n ...authParams,\n });\n }\n }, [user, isLoading, error]);\n\n if (error) {\n return handlePageError(error, options);\n }\n\n if (!user && !isLoading && options?.onAccessDenied) {\n return options.onAccessDenied();\n }\n\n if (user) {\n if (\n options?.groups &&\n !isUserInGroup(\n user,\n options.groups,\n options.groupsClaim ??\n process.env.NEXT_PUBLIC_MONOCLOUD_AUTH_GROUPS_CLAIM,\n options.matchAll\n )\n ) {\n const {\n onGroupAccessDenied = (): React.ReactNode => <div>Access Denied</div>,\n } = options;\n return onGroupAccessDenied(user);\n }\n\n return <Component user={user} {...props} />;\n }\n\n return null;\n };\n};\n"],"mappings":"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"}
|