@monocloud/auth-nextjs 0.1.4 → 0.1.6

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.
@@ -1,4 +1,4 @@
1
- const require_chunk = require('./chunk-CbDLau6x.cjs');
1
+ const require_chunk = require('./chunk-C0xms8kb.cjs');
2
2
  let _monocloud_auth_node_core_utils = require("@monocloud/auth-node-core/utils");
3
3
  let swr = require("swr");
4
4
  swr = require_chunk.__toESM(swr);
@@ -147,7 +147,7 @@ const handlePageError = (error, options) => {
147
147
  /**
148
148
  * Function to protect a client rendered page component.
149
149
  * Ensures that only authenticated users can access the component.
150
- *
150
+ *
151
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.**
152
152
  *
153
153
  * @param Component - The component to protect.
@@ -168,7 +168,7 @@ const handlePageError = (error, options) => {
168
168
  * ```
169
169
  *
170
170
  * @example App Router with options
171
- *
171
+ *
172
172
  * See {@link ProtectPageOptions} for more options.
173
173
  *
174
174
  * ```tsx
@@ -183,7 +183,41 @@ const handlePageError = (error, options) => {
183
183
  * { returnUrl: "/dashboard", authParams: { loginHint: "username" } }
184
184
  * );
185
185
  * ```
186
-
186
+ * @example Fallback with onAccessDenied
187
+ *
188
+ * ```tsx
189
+ * "use client";
190
+ *
191
+ * import { protectPage } from "@monocloud/auth-nextjs/client";
192
+ *
193
+ * export default protectPage(
194
+ * function Home() {
195
+ * return <>You are signed in</>;
196
+ * },
197
+ * {
198
+ * onAccessDenied: () => <div>Please sign in to continue</div>
199
+ * }
200
+ * );
201
+ * ```
202
+ *
203
+ * @example Group Protection with onGroupAccessDenied
204
+ *
205
+ * ```tsx
206
+ * "use client";
207
+ *
208
+ * import { protectPage } from "@monocloud/auth-nextjs/client";
209
+ *
210
+ * export default protectPage(
211
+ * function Home() {
212
+ * return <>Welcome Admin</>;
213
+ * },
214
+ * {
215
+ * groups: ["admin"],
216
+ * onGroupAccessDenied: (user) => <div>User {user.email} is not an admin</div>
217
+ * }
218
+ * );
219
+ * ```
220
+ *
187
221
  * @example Pages Router
188
222
  *
189
223
  * ```tsx
@@ -195,7 +229,7 @@ const handlePageError = (error, options) => {
195
229
  * ```
196
230
  *
197
231
  * @example Pages Router with options
198
- *
232
+ *
199
233
  * See {@link ProtectPageOptions} for more options.
200
234
  *
201
235
  * ```tsx
@@ -230,8 +264,8 @@ const protectPage = (Component, options) => {
230
264
  if (!user && !isLoading && (options === null || options === void 0 ? void 0 : options.onAccessDenied)) return options.onAccessDenied();
231
265
  if (user) {
232
266
  if ((options === null || options === void 0 ? void 0 : options.groups) && !(0, _monocloud_auth_node_core_utils.isUserInGroup)(user, options.groups, options.groupsClaim ?? process.env.NEXT_PUBLIC_MONOCLOUD_AUTH_GROUPS_CLAIM, options.matchAll)) {
233
- const { onAccessDenied = () => /* @__PURE__ */ react.default.createElement("div", null, "Access Denied") } = options;
234
- return onAccessDenied(user);
267
+ const { onGroupAccessDenied = () => /* @__PURE__ */ react.default.createElement("div", null, "Access Denied") } = options;
268
+ return onGroupAccessDenied(user);
235
269
  }
236
270
  return /* @__PURE__ */ react.default.createElement(Component, {
237
271
  user,
@@ -261,4 +295,4 @@ Object.defineProperty(exports, 'useAuth', {
261
295
  return useAuth;
262
296
  }
263
297
  });
264
- //# sourceMappingURL=client-Be6A2vEn.cjs.map
298
+ //# sourceMappingURL=protect-BCIji2i7.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protect-BCIji2i7.cjs","names":[],"sources":["../src/client/use-auth.tsx","../src/client/protect.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 */\nexport interface AuthState {\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 * Hook for getting the user's profile on client components\n *\n * @returns Authentication State\n *\n * @example App Router\n *\n * ```tsx\n * \"use client\";\n *\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user } = useAuth();\n *\n * return <>User Id: {user?.sub}</>;\n * }\n * ```\n *\n * @example App Router - Refetch user from Userinfo endpoint\n *\n * Calling `refetch(true)` will force refresh the user's profile from the userinfo endpoint.\n * If you do not intent to refersh from your tenants userinfo endpoint, use just `refetch()`\n *\n * **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**\n *\n * ```tsx\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)}</pre>\n * <button onClick={() => refetch(true)}>Refresh</button>\n * </>\n * );\n * }\n * ```\n *\n * @example Pages Router\n *\n * ```tsx\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user } = useAuth();\n *\n * return <>User Id: {user?.sub}</>;\n * }\n * ```\n *\n * @example Pages Router - Refetch user from Userinfo endpoint\n *\n * Calling `refetch(true)` will force refresh the user's profile from the userinfo endpoint.\n * If you do not intent to refersh from your tenants userinfo endpoint, use just `refetch()`\n *\n * **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**\n *\n * ```tsx\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)}</pre>\n * <button onClick={() => refetch(true)}>Refresh</button>\n * </>\n * );\n * }\n * ```\n *\n */\nexport const useAuth = (): AuthState => {\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';\n\n/**\n * Options for configuring page protection.\n */\nexport type ProtectPageOptions = {\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} & GroupOptions;\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?: ProtectPageOptions\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 * Function to protect a client rendered page component.\n * Ensures that only authenticated users can access the component.\n *\n * **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.**\n *\n * @param Component - The component to protect.\n * @param options - The options.\n *\n * @returns Protected client rendered page component.\n *\n * @example App Router\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(function Home() {\n * return <>You are signed in</>;\n * });\n * ```\n *\n * @example App Router with options\n *\n * See {@link ProtectPageOptions} for more options.\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>You are signed in</>;\n * },\n * { returnUrl: \"/dashboard\", authParams: { loginHint: \"username\" } }\n * );\n * ```\n * @example Fallback with onAccessDenied\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>You are signed in</>;\n * },\n * {\n * onAccessDenied: () => <div>Please sign in to continue</div>\n * }\n * );\n * ```\n *\n * @example Group Protection with onGroupAccessDenied\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>Welcome Admin</>;\n * },\n * {\n * groups: [\"admin\"],\n * onGroupAccessDenied: (user) => <div>User {user.email} is not an admin</div>\n * }\n * );\n * ```\n *\n * @example Pages Router\n *\n * ```tsx\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(function Home() {\n * return <>You are signed in</>;\n * });\n * ```\n *\n * @example Pages Router with options\n *\n * See {@link ProtectPageOptions} for more options.\n *\n * ```tsx\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>You are signed in</>;\n * },\n * { returnUrl: \"/dashboard\", authParams: { loginHint: \"username\" } }\n * );\n * ```\n */\nexport const protectPage = <P extends object>(\n Component: ComponentType<P & { user: MonoCloudUser }>,\n options?: ProtectPageOptions\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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"}
@@ -144,7 +144,7 @@ const handlePageError = (error, options) => {
144
144
  /**
145
145
  * Function to protect a client rendered page component.
146
146
  * Ensures that only authenticated users can access the component.
147
- *
147
+ *
148
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.**
149
149
  *
150
150
  * @param Component - The component to protect.
@@ -165,7 +165,7 @@ const handlePageError = (error, options) => {
165
165
  * ```
166
166
  *
167
167
  * @example App Router with options
168
- *
168
+ *
169
169
  * See {@link ProtectPageOptions} for more options.
170
170
  *
171
171
  * ```tsx
@@ -180,7 +180,41 @@ const handlePageError = (error, options) => {
180
180
  * { returnUrl: "/dashboard", authParams: { loginHint: "username" } }
181
181
  * );
182
182
  * ```
183
-
183
+ * @example Fallback with onAccessDenied
184
+ *
185
+ * ```tsx
186
+ * "use client";
187
+ *
188
+ * import { protectPage } from "@monocloud/auth-nextjs/client";
189
+ *
190
+ * export default protectPage(
191
+ * function Home() {
192
+ * return <>You are signed in</>;
193
+ * },
194
+ * {
195
+ * onAccessDenied: () => <div>Please sign in to continue</div>
196
+ * }
197
+ * );
198
+ * ```
199
+ *
200
+ * @example Group Protection with onGroupAccessDenied
201
+ *
202
+ * ```tsx
203
+ * "use client";
204
+ *
205
+ * import { protectPage } from "@monocloud/auth-nextjs/client";
206
+ *
207
+ * export default protectPage(
208
+ * function Home() {
209
+ * return <>Welcome Admin</>;
210
+ * },
211
+ * {
212
+ * groups: ["admin"],
213
+ * onGroupAccessDenied: (user) => <div>User {user.email} is not an admin</div>
214
+ * }
215
+ * );
216
+ * ```
217
+ *
184
218
  * @example Pages Router
185
219
  *
186
220
  * ```tsx
@@ -192,7 +226,7 @@ const handlePageError = (error, options) => {
192
226
  * ```
193
227
  *
194
228
  * @example Pages Router with options
195
- *
229
+ *
196
230
  * See {@link ProtectPageOptions} for more options.
197
231
  *
198
232
  * ```tsx
@@ -227,8 +261,8 @@ const protectPage = (Component, options) => {
227
261
  if (!user && !isLoading && (options === null || options === void 0 ? void 0 : options.onAccessDenied)) return options.onAccessDenied();
228
262
  if (user) {
229
263
  if ((options === null || options === void 0 ? void 0 : options.groups) && !isUserInGroup(user, options.groups, options.groupsClaim ?? process.env.NEXT_PUBLIC_MONOCLOUD_AUTH_GROUPS_CLAIM, options.matchAll)) {
230
- const { onAccessDenied = () => /* @__PURE__ */ React.createElement("div", null, "Access Denied") } = options;
231
- return onAccessDenied(user);
264
+ const { onGroupAccessDenied = () => /* @__PURE__ */ React.createElement("div", null, "Access Denied") } = options;
265
+ return onGroupAccessDenied(user);
232
266
  }
233
267
  return /* @__PURE__ */ React.createElement(Component, {
234
268
  user,
@@ -241,4 +275,4 @@ const protectPage = (Component, options) => {
241
275
 
242
276
  //#endregion
243
277
  export { redirectToSignIn as n, useAuth as r, protectPage as t };
244
- //# sourceMappingURL=client-CnvBgZM-.mjs.map
278
+ //# sourceMappingURL=protect-K9srvUkq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protect-K9srvUkq.mjs","names":[],"sources":["../src/client/use-auth.tsx","../src/client/protect.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 */\nexport interface AuthState {\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 * Hook for getting the user's profile on client components\n *\n * @returns Authentication State\n *\n * @example App Router\n *\n * ```tsx\n * \"use client\";\n *\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user } = useAuth();\n *\n * return <>User Id: {user?.sub}</>;\n * }\n * ```\n *\n * @example App Router - Refetch user from Userinfo endpoint\n *\n * Calling `refetch(true)` will force refresh the user's profile from the userinfo endpoint.\n * If you do not intent to refersh from your tenants userinfo endpoint, use just `refetch()`\n *\n * **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**\n *\n * ```tsx\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)}</pre>\n * <button onClick={() => refetch(true)}>Refresh</button>\n * </>\n * );\n * }\n * ```\n *\n * @example Pages Router\n *\n * ```tsx\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user } = useAuth();\n *\n * return <>User Id: {user?.sub}</>;\n * }\n * ```\n *\n * @example Pages Router - Refetch user from Userinfo endpoint\n *\n * Calling `refetch(true)` will force refresh the user's profile from the userinfo endpoint.\n * If you do not intent to refersh from your tenants userinfo endpoint, use just `refetch()`\n *\n * **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**\n *\n * ```tsx\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)}</pre>\n * <button onClick={() => refetch(true)}>Refresh</button>\n * </>\n * );\n * }\n * ```\n *\n */\nexport const useAuth = (): AuthState => {\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';\n\n/**\n * Options for configuring page protection.\n */\nexport type ProtectPageOptions = {\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} & GroupOptions;\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?: ProtectPageOptions\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 * Function to protect a client rendered page component.\n * Ensures that only authenticated users can access the component.\n *\n * **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.**\n *\n * @param Component - The component to protect.\n * @param options - The options.\n *\n * @returns Protected client rendered page component.\n *\n * @example App Router\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(function Home() {\n * return <>You are signed in</>;\n * });\n * ```\n *\n * @example App Router with options\n *\n * See {@link ProtectPageOptions} for more options.\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>You are signed in</>;\n * },\n * { returnUrl: \"/dashboard\", authParams: { loginHint: \"username\" } }\n * );\n * ```\n * @example Fallback with onAccessDenied\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>You are signed in</>;\n * },\n * {\n * onAccessDenied: () => <div>Please sign in to continue</div>\n * }\n * );\n * ```\n *\n * @example Group Protection with onGroupAccessDenied\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>Welcome Admin</>;\n * },\n * {\n * groups: [\"admin\"],\n * onGroupAccessDenied: (user) => <div>User {user.email} is not an admin</div>\n * }\n * );\n * ```\n *\n * @example Pages Router\n *\n * ```tsx\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(function Home() {\n * return <>You are signed in</>;\n * });\n * ```\n *\n * @example Pages Router with options\n *\n * See {@link ProtectPageOptions} for more options.\n *\n * ```tsx\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>You are signed in</>;\n * },\n * { returnUrl: \"/dashboard\", authParams: { loginHint: \"username\" } }\n * );\n * ```\n */\nexport const protectPage = <P extends object>(\n Component: ComponentType<P & { user: MonoCloudUser }>,\n options?: ProtectPageOptions\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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"}
@@ -5,7 +5,6 @@ import { GetServerSideProps, GetServerSidePropsContext, GetServerSidePropsResult
5
5
  import { ParsedUrlQuery } from "node:querystring";
6
6
 
7
7
  //#region src/types.d.ts
8
-
9
8
  /**
10
9
  * Context object passed to App Router API route handlers.
11
10
  * Contains the dynamic route parameters.
@@ -24,10 +23,18 @@ type NextMiddlewareResult = NextResponse | Response | null | undefined | void;
24
23
  *
25
24
  * @param request - The incoming Next.js request.
26
25
  * @param event - The Next.js fetch event.
27
- * @param user - The authenticated user object (if available).
28
26
  * @returns A `NextMiddlewareResult` (e.g., a redirect or rewrite) or a Promise resolving to one.
29
27
  */
30
- type NextMiddlewareOnAccessDenied = (request: NextRequest, event: NextFetchEvent, user?: MonoCloudUser) => NextMiddlewareResult | Promise<NextMiddlewareResult>;
28
+ type NextMiddlewareOnAccessDenied = (request: NextRequest, event: NextFetchEvent) => NextMiddlewareResult | Promise<NextMiddlewareResult>;
29
+ /**
30
+ * Handler function triggered when a user is denied access in Next.js Middleware due to group restrictions.
31
+ *
32
+ * @param request - The incoming Next.js request.
33
+ * @param event - The Next.js fetch event.
34
+ * @param user - The authenticated user object.
35
+ * @returns A `NextMiddlewareResult` (e.g., a redirect or rewrite) or a Promise resolving to one.
36
+ */
37
+ type NextMiddlewareOnGroupAccessDenied = (request: NextRequest, event: NextFetchEvent, user: MonoCloudUser) => NextMiddlewareResult | Promise<NextMiddlewareResult>;
31
38
  /**
32
39
  *
33
40
  * ProtectedRouteMatcher can be a combination of the following types.
@@ -111,9 +118,13 @@ interface MonoCloudMiddlewareOptions {
111
118
  */
112
119
  matchAll?: boolean;
113
120
  /**
114
- * A middleware function called when the user is not authenticated or is not a member of the specified groups.
121
+ * A middleware function called when the user is not authenticated.
115
122
  */
116
123
  onAccessDenied?: NextMiddlewareOnAccessDenied;
124
+ /**
125
+ * A middleware function called when the user is authenticated but is not a member of the specified groups.
126
+ */
127
+ onGroupAccessDenied?: NextMiddlewareOnGroupAccessDenied;
117
128
  }
118
129
  /**
119
130
  * A subset of authorization parameters used on client side functions
@@ -141,10 +152,17 @@ type ProtectAppPageOptions = {
141
152
  */
142
153
  returnUrl?: string;
143
154
  /**
144
- * Alternate page handler called when the user is not authenticated or is not a member of the specified groups.
155
+ * Alternate page handler called when the user is not authenticated.
145
156
  */
146
157
  onAccessDenied?: (props: {
147
- user?: MonoCloudUser;
158
+ params?: Record<string, string | string[]>;
159
+ searchParams?: Record<string, string | string[] | undefined>;
160
+ }) => Promise<JSX.Element> | JSX.Element;
161
+ /**
162
+ * Alternate page handler called when the user is authenticated but is not a member of the specified groups.
163
+ */
164
+ onGroupAccessDenied?: (props: {
165
+ user: MonoCloudUser;
148
166
  params?: Record<string, string | string[]>;
149
167
  searchParams?: Record<string, string | string[] | undefined>;
150
168
  }) => Promise<JSX.Element> | JSX.Element;
@@ -173,26 +191,41 @@ type ProtectPagePageOptions<P extends Record<string, any> = Record<string, any>,
173
191
  */
174
192
  returnUrl?: string;
175
193
  /**
176
- * Alternate `getServerSideProps` function called when the user is not authenticated or is not a member of the specified groups.
194
+ * Alternate `getServerSideProps` function called when the user is not authenticated.
177
195
  */
178
196
  onAccessDenied?: ProtectPagePageOnAccessDeniedType<P, Q>;
197
+ /**
198
+ * Alternate `getServerSideProps` function called when the user IS authenticated but is not a member of the specified groups.
199
+ */
200
+ onGroupAccessDenied?: ProtectPagePageOnGroupAccessDeniedType<P, Q>;
179
201
  /**
180
202
  * Authorization parameters to be used during authentication.
181
203
  */
182
204
  authParams?: ExtraAuthParams;
183
205
  } & GroupOptions;
184
206
  /**
185
- * Handler function triggered when a user is denied access in a Pages Router `getServerSideProps` flow.
207
+ * Handler function triggered when a user is not authenticated in a Pages Router `getServerSideProps` flow.
208
+ *
209
+ * @typeParam P - The type of the props.
210
+ * @typeParam Q - The type of the parsed query object.
211
+ *
212
+ * @param context - The server-side props context
213
+ *
214
+ * @returns The result for `getServerSideProps`.
215
+ */
216
+ type ProtectPagePageOnAccessDeniedType<P, Q extends ParsedUrlQuery = ParsedUrlQuery> = (context: GetServerSidePropsContext<Q>) => Promise<GetServerSidePropsResult<P>> | GetServerSidePropsResult<P>;
217
+ /**
218
+ * Handler function triggered when a user is denied access in a Pages Router `getServerSideProps` flow due to group restrictions.
186
219
  *
187
220
  * @typeParam P - The type of the props.
188
221
  * @typeParam Q - The type of the parsed query object.
189
222
  *
190
- * @param context - The server-side props context extended with the optional user object.
223
+ * @param context - The server-side props context with the user object.
191
224
  *
192
225
  * @returns The result for `getServerSideProps`.
193
226
  */
194
- type ProtectPagePageOnAccessDeniedType<P, Q extends ParsedUrlQuery = ParsedUrlQuery> = (context: GetServerSidePropsContext<Q> & {
195
- user?: MonoCloudUser;
227
+ type ProtectPagePageOnGroupAccessDeniedType<P, Q extends ParsedUrlQuery = ParsedUrlQuery> = (context: GetServerSidePropsContext<Q> & {
228
+ user: MonoCloudUser;
196
229
  }) => Promise<GetServerSidePropsResult<P>> | GetServerSidePropsResult<P>;
197
230
  /**
198
231
  * The return type of the `protectPage()` wrapper for Pages Router.
@@ -216,39 +249,64 @@ type ProtectedAppServerComponent = (props: {
216
249
  searchParams?: Record<string, string | string[] | undefined>;
217
250
  }) => Promise<JSX.Element> | JSX.Element;
218
251
  /**
219
- * Handler function triggered when a user is denied access in an App Router API route.
252
+ * Handler function triggered when a user is not authenticated in an App Router API route.
220
253
  *
221
254
  * @param req - The incoming Next.js request.
222
255
  * @param ctx - The App Router context.
223
256
  *
224
- * @param user - The authenticated user object (if available).
257
+ * @returns A Response/NextResponse or Promise resolving to one.
258
+ */
259
+ type AppRouterApiOnAccessDeniedHandler = (req: NextRequest, ctx: AppRouterContext) => Promise<Response> | Response;
260
+ /**
261
+ * Handler function triggered when a user is denied access in an App Router API route due to group restrictions.
262
+ *
263
+ * @param req - The incoming Next.js request.
264
+ * @param ctx - The App Router context.
265
+ * @param user - The authenticated user object.
225
266
  *
226
267
  * @returns A Response/NextResponse or Promise resolving to one.
227
268
  */
228
- type AppRouterApiOnAccessDeniedHandler = (req: NextRequest, ctx: AppRouterContext, user?: MonoCloudUser) => Promise<Response> | Response;
269
+ type AppRouterApiOnGroupAccessDeniedHandler = (req: NextRequest, ctx: AppRouterContext, user: MonoCloudUser) => Promise<Response> | Response;
229
270
  /** Options for App Router `protectApi()` */
230
271
  type ProtectApiAppOptions = {
231
272
  /**
232
- * Alternate app router api handler called when the user is not authenticated or is not a member of the specified groups.
273
+ * Alternate app router api handler called when the user is not authenticated.
233
274
  */
234
275
  onAccessDenied?: AppRouterApiOnAccessDeniedHandler;
276
+ /**
277
+ * Alternate app router api handler called when the user is authenticated but is not a member of the specified groups.
278
+ */
279
+ onGroupAccessDenied?: AppRouterApiOnGroupAccessDeniedHandler;
235
280
  } & GroupOptions;
236
281
  /**
237
- * Handler function triggered when a user is denied access in a Pages Router API route.
282
+ * Handler function triggered when a user is not authenticated in a Pages Router API route.
283
+ *
284
+ * @param req - The incoming Next.js API request.
285
+ * @param res - The Next.js API response.
286
+ *
287
+ * @returns
288
+ */
289
+ type PageRouterApiOnAccessDeniedHandler = (req: NextApiRequest, res: NextApiResponse<any>) => Promise<unknown> | unknown;
290
+ /**
291
+ * Handler function triggered when a user is denied access in a Pages Router API route due to group restrictions.
238
292
  *
239
293
  * @param req - The incoming Next.js API request.
240
294
  * @param res - The Next.js API response.
241
- * @param user - The authenticated user object (if available).
295
+ * @param user - The authenticated user object.
242
296
  *
243
297
  * @returns
244
298
  */
245
- type PageRouterApiOnAccessDeniedHandler = (req: NextApiRequest, res: NextApiResponse<any>, user?: MonoCloudUser) => Promise<unknown> | unknown;
299
+ type PageRouterApiOnGroupAccessDeniedHandler = (req: NextApiRequest, res: NextApiResponse<any>, user: MonoCloudUser) => Promise<unknown> | unknown;
246
300
  /** Options for Page Router `protectApi()` */
247
301
  type ProtectApiPageOptions = {
248
302
  /**
249
- * Alternate page router api handler called when the user is not authenticated or is not a member of the specified groups.
303
+ * Alternate page router api handler called when the user is not authenticated.
250
304
  */
251
305
  onAccessDenied?: PageRouterApiOnAccessDeniedHandler;
306
+ /**
307
+ * Alternate page router api handler called when the user is authenticated but is not a member of the specified groups.
308
+ */
309
+ onGroupAccessDenied?: PageRouterApiOnGroupAccessDeniedHandler;
252
310
  } & GroupOptions;
253
311
  /**
254
312
  * Options for the `protect()` helper function.
@@ -322,7 +380,7 @@ interface RedirectToSignInOptions {
322
380
  */
323
381
  acrValues?: string[];
324
382
  /**
325
- * A hint to the authorization server about the user's identifier
383
+ * A hint to the authorization server about the user's identifier
326
384
  */
327
385
  loginHint?: string;
328
386
  /**
@@ -348,4 +406,4 @@ interface RedirectToSignOutOptions {
348
406
  }
349
407
  //#endregion
350
408
  export { ProtectPagePageOptions as _, GroupOptions as a, RedirectToSignInOptions as b, MonoCloudAuthOptions as c, PageRouterApiOnAccessDeniedHandler as d, ProtectApiAppOptions as f, ProtectPagePageOnAccessDeniedType as g, ProtectOptions as h, ExtraAuthParams as i, MonoCloudMiddlewareOptions as l, ProtectAppPageOptions as m, AppRouterApiOnAccessDeniedHandler as n, IsUserInGroupOptions as o, ProtectApiPageOptions as p, AppRouterPageHandler as r, MonoCloudAuthHandler as s, AppRouterApiHandlerFn as t, NextMiddlewareResult as u, ProtectPagePageReturnType as v, RedirectToSignOutOptions as x, ProtectedAppServerComponent as y };
351
- //# sourceMappingURL=types-DOfZTKa6.d.mts.map
409
+ //# sourceMappingURL=types-Cx32VRoI.d.mts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monocloud/auth-nextjs",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "MonoCloud Next.js Authentication SDK",
5
5
  "keywords": [
6
6
  "monocloud",
@@ -56,18 +56,18 @@
56
56
  },
57
57
  "dependencies": {
58
58
  "cookie": "1.1.1",
59
- "swr": "2.3.8",
60
- "@monocloud/auth-node-core": "0.1.4"
59
+ "swr": "2.4.0",
60
+ "@monocloud/auth-node-core": "0.1.5"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@edge-runtime/vm": "5.0.0",
64
64
  "@testing-library/dom": "10.4.1",
65
- "@testing-library/react": "16.3.1",
65
+ "@testing-library/react": "16.3.2",
66
66
  "@types/body-parser": "1.19.6",
67
- "@types/react": "19.2.7",
67
+ "@types/react": "19.2.13",
68
68
  "@types/react-dom": "19.2.3",
69
- "body-parser": "2.2.1",
70
- "eslint": "9.39.2",
69
+ "body-parser": "2.2.2",
70
+ "eslint": "10.0.0",
71
71
  "nock": "15.0.0",
72
72
  "tough-cookie": "6.0.0",
73
73
  "url-search-params-polyfill": "8.2.5",
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-Be6A2vEn.cjs","names":[],"sources":["../src/client/use-auth.tsx","../src/client/protect.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 */\nexport interface AuthState {\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 * Hook for getting the user's profile on client components\n *\n * @returns Authentication State\n *\n * @example App Router\n *\n * ```tsx\n * \"use client\";\n *\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user } = useAuth();\n *\n * return <>User Id: {user?.sub}</>;\n * }\n * ```\n *\n * @example App Router - Refetch user from Userinfo endpoint\n *\n * Calling `refetch(true)` will force refresh the user's profile from the userinfo endpoint.\n * If you do not intent to refersh from your tenants userinfo endpoint, use just `refetch()`\n *\n * **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**\n *\n * ```tsx\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)}</pre>\n * <button onClick={() => refetch(true)}>Refresh</button>\n * </>\n * );\n * }\n * ```\n *\n * @example Pages Router\n *\n * ```tsx\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user } = useAuth();\n *\n * return <>User Id: {user?.sub}</>;\n * }\n * ```\n *\n * @example Pages Router - Refetch user from Userinfo endpoint\n *\n * Calling `refetch(true)` will force refresh the user's profile from the userinfo endpoint.\n * If you do not intent to refersh from your tenants userinfo endpoint, use just `refetch()`\n *\n * **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**\n *\n * ```tsx\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)}</pre>\n * <button onClick={() => refetch(true)}>Refresh</button>\n * </>\n * );\n * }\n * ```\n *\n */\nexport const useAuth = (): AuthState => {\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, JSX, 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';\n\n/**\n * Options for configuring page protection.\n */\nexport type ProtectPageOptions = {\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 or is not a member of the specified groups.\n */\n onAccessDenied?: (user?: MonoCloudUser) => JSX.Element;\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) => JSX.Element;\n} & GroupOptions;\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?: ProtectPageOptions\n): JSX.Element => {\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 * Function to protect a client rendered page component.\n * Ensures that only authenticated users can access the component.\n * \n * **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.**\n *\n * @param Component - The component to protect.\n * @param options - The options.\n *\n * @returns Protected client rendered page component.\n *\n * @example App Router\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(function Home() {\n * return <>You are signed in</>;\n * });\n * ```\n *\n * @example App Router with options\n * \n * See {@link ProtectPageOptions} for more options.\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>You are signed in</>;\n * },\n * { returnUrl: \"/dashboard\", authParams: { loginHint: \"username\" } }\n * );\n * ```\n\n* @example Pages Router\n *\n * ```tsx\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(function Home() {\n * return <>You are signed in</>;\n * });\n * ```\n *\n * @example Pages Router with options\n * \n * See {@link ProtectPageOptions} for more options.\n *\n * ```tsx\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>You are signed in</>;\n * },\n * { returnUrl: \"/dashboard\", authParams: { loginHint: \"username\" } }\n * );\n * ```\n */\nexport const protectPage = <P extends object>(\n Component: ComponentType<P & { user: MonoCloudUser }>,\n options?: ProtectPageOptions\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 { onAccessDenied = (): JSX.Element => <div>Access Denied</div> } =\n options;\n return onAccessDenied(user);\n }\n\n return <Component user={user} {...props} />;\n }\n\n return null;\n };\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-CnvBgZM-.mjs","names":[],"sources":["../src/client/use-auth.tsx","../src/client/protect.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 */\nexport interface AuthState {\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 * Hook for getting the user's profile on client components\n *\n * @returns Authentication State\n *\n * @example App Router\n *\n * ```tsx\n * \"use client\";\n *\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user } = useAuth();\n *\n * return <>User Id: {user?.sub}</>;\n * }\n * ```\n *\n * @example App Router - Refetch user from Userinfo endpoint\n *\n * Calling `refetch(true)` will force refresh the user's profile from the userinfo endpoint.\n * If you do not intent to refersh from your tenants userinfo endpoint, use just `refetch()`\n *\n * **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**\n *\n * ```tsx\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)}</pre>\n * <button onClick={() => refetch(true)}>Refresh</button>\n * </>\n * );\n * }\n * ```\n *\n * @example Pages Router\n *\n * ```tsx\n * import { useAuth } from \"@monocloud/auth-nextjs/client\";\n *\n * export default function Home() {\n * const { user } = useAuth();\n *\n * return <>User Id: {user?.sub}</>;\n * }\n * ```\n *\n * @example Pages Router - Refetch user from Userinfo endpoint\n *\n * Calling `refetch(true)` will force refresh the user's profile from the userinfo endpoint.\n * If you do not intent to refersh from your tenants userinfo endpoint, use just `refetch()`\n *\n * **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**\n *\n * ```tsx\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)}</pre>\n * <button onClick={() => refetch(true)}>Refresh</button>\n * </>\n * );\n * }\n * ```\n *\n */\nexport const useAuth = (): AuthState => {\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, JSX, 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';\n\n/**\n * Options for configuring page protection.\n */\nexport type ProtectPageOptions = {\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 or is not a member of the specified groups.\n */\n onAccessDenied?: (user?: MonoCloudUser) => JSX.Element;\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) => JSX.Element;\n} & GroupOptions;\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?: ProtectPageOptions\n): JSX.Element => {\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 * Function to protect a client rendered page component.\n * Ensures that only authenticated users can access the component.\n * \n * **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.**\n *\n * @param Component - The component to protect.\n * @param options - The options.\n *\n * @returns Protected client rendered page component.\n *\n * @example App Router\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(function Home() {\n * return <>You are signed in</>;\n * });\n * ```\n *\n * @example App Router with options\n * \n * See {@link ProtectPageOptions} for more options.\n *\n * ```tsx\n * \"use client\";\n *\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>You are signed in</>;\n * },\n * { returnUrl: \"/dashboard\", authParams: { loginHint: \"username\" } }\n * );\n * ```\n\n* @example Pages Router\n *\n * ```tsx\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(function Home() {\n * return <>You are signed in</>;\n * });\n * ```\n *\n * @example Pages Router with options\n * \n * See {@link ProtectPageOptions} for more options.\n *\n * ```tsx\n * import { protectPage } from \"@monocloud/auth-nextjs/client\";\n *\n * export default protectPage(\n * function Home() {\n * return <>You are signed in</>;\n * },\n * { returnUrl: \"/dashboard\", authParams: { loginHint: \"username\" } }\n * );\n * ```\n */\nexport const protectPage = <P extends object>(\n Component: ComponentType<P & { user: MonoCloudUser }>,\n options?: ProtectPageOptions\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 { onAccessDenied = (): JSX.Element => <div>Access Denied</div> } =\n options;\n return onAccessDenied(user);\n }\n\n return <Component user={user} {...props} />;\n }\n\n return null;\n };\n};\n"],"mappings":"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"}