@monocloud/auth-nextjs 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.cjs +1 -1
- package/dist/client/index.d.mts +4 -3
- package/dist/client/index.mjs +1 -1
- package/dist/components/client/index.cjs +1 -1
- package/dist/components/client/index.cjs.map +1 -1
- package/dist/components/client/index.d.mts +2 -2
- package/dist/components/client/index.mjs +1 -1
- package/dist/components/client/index.mjs.map +1 -1
- package/dist/components/index.d.mts +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{protect-client-page-BFlGkK8F.mjs → protect-client-page-BFVskb3X.mjs} +4 -3
- package/dist/protect-client-page-BFVskb3X.mjs.map +1 -0
- package/dist/{protect-client-page-B1fOU4Zl.cjs → protect-client-page-BdsnH8gs.cjs} +4 -3
- package/dist/protect-client-page-BdsnH8gs.cjs.map +1 -0
- package/dist/{types-xS_Me3Qg.d.mts → types-ClljFIvK.d.mts} +49 -69
- package/package.json +3 -3
- package/dist/protect-client-page-B1fOU4Zl.cjs.map +0 -1
- package/dist/protect-client-page-BFlGkK8F.mjs.map +0 -1
|
@@ -34,8 +34,8 @@ const fetchUser = async (url) => {
|
|
|
34
34
|
*
|
|
35
35
|
* @example Refetch user
|
|
36
36
|
*
|
|
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.
|
|
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.
|
|
39
39
|
*
|
|
40
40
|
* ```tsx title="Refetch User"
|
|
41
41
|
* "use client";
|
|
@@ -185,6 +185,7 @@ const handlePageError = (error, options) => {
|
|
|
185
185
|
* ```
|
|
186
186
|
*
|
|
187
187
|
* @param Component - The page component to protect
|
|
188
|
+
* @typeParam P - Props of the protected component (excluding `user`).
|
|
188
189
|
* @param options - Optional configuration
|
|
189
190
|
* @returns A protected React component.
|
|
190
191
|
*
|
|
@@ -226,4 +227,4 @@ const protectClientPage = (Component, options) => {
|
|
|
226
227
|
|
|
227
228
|
//#endregion
|
|
228
229
|
export { redirectToSignIn as n, useAuth as r, protectClientPage as t };
|
|
229
|
-
//# sourceMappingURL=protect-client-page-
|
|
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":";;;;;AAkCA,MAAM,YAAY,OAAO,QAAoD;CAC3E,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,aAAa,WAAW,CAAC;AAExD,KAAI,IAAI,WAAW,IACjB;AAGF,KAAI,IAAI,GACN,QAAO,IAAI,MAAM;AAGnB,OAAM,IAAI,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDzC,MAAa,gBAAqC;CAChD,MAAM,MACJ,QAAQ,IAAI,4CAEZ,GAAG,QAAQ,IAAI,0BAA0B,GAAG;CAE9C,MAAM,EAAE,MAAM,OAAO,WAAW,WAAW,OACzC,KACA,UACD;CAED,MAAM,WAAW,YAA4B;EAC3C,MAAM,MAAM,IAAI,IAAI,KAAK,gBAAgB;AACzC,MAAI,QACF,KAAI,aAAa,IAAI,WAAW,OAAO;AAGzC,EAAK,OAAO,YAAY,MAAM,UAAU,IAAI,WAAW,IAAI,OAAO,EAAE,EAClE,YAAY,OACb,CAAC;;AAGJ,KAAI,MACF,QAAO;EACL,MAAM;EACN,WAAW;EACX,iBAAiB;EACV;EACP;EACD;AAGH,KAAI,KACF,QAAO;EACL,MAAM;EACN;EACA,iBAAiB,CAAC,CAAC,QAAQ,OAAO,KAAK,KAAK,CAAC,SAAS;EACtD,OAAO;EACP;EACD;AAGH,QAAO;EACL,MAAM;EACN;EACA,iBAAiB;EACjB,OAAO;EAEP,eAAqB;EACtB;;;;;ACpGH,MAAa,oBACX,YACS;CACT,MAAM,eAAe,IAAI,gBAAgB,OAAO,SAAS,OAAO;AAChE,cAAa,IACX,cACA,QAAQ,aAAa,OAAO,SAAS,UAAU,CAChD;AAED,uDAAI,QAAS,OACX,cAAa,IAAI,SAAS,QAAQ,OAAO;AAE3C,uDAAI,QAAS,SACX,cAAa,IAAI,YAAY,QAAQ,SAAS;AAGhD,uDAAI,QAAS,UACX,cAAa,IAAI,cAAc,QAAQ,UAAU,KAAK,IAAI,CAAC;AAG7D,uDAAI,QAAS,QACX,cAAa,IAAI,WAAW,QAAQ,QAAQ;AAG9C,uDAAI,QAAS,OACX,cAAa,IAAI,UAAU,QAAQ,OAAO;AAG5C,uDAAI,QAAS,kBACX,cAAa,IAAI,sBAAsB,QAAQ,kBAAkB;AAGnE,uDAAI,QAAS,UACX,cAAa,IAAI,cAAc,QAAQ,UAAU;AAGnD,uDAAI,QAAS,OACX,cAAa,IAAI,WAAW,QAAQ,OAAO,UAAU,CAAC;AAGxD,uDAAI,QAAS,UACX,cAAa,IAAI,cAAc,QAAQ,UAAU;AAGnD,QAAO,SAAS,OAEd,GAAG,QAAQ,IAAI,yCAAyC,GAAG,QAAQ,IAAI,0BAA0B,GAAG,kBAAkB,GAAG,aAAa,UAAU,GACjJ;;AAGH,MAAM,mBACJ,OACA,YACoB;;AAEpB,uDAAI,QAAS,QACX,QAAO,QAAQ,QAAQ,MAAM;;AAI/B,OAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoFR,MAAa,qBACX,WACA,YACgB;AAChB,SAAO,UAAS;EACd,MAAM,EAAE,MAAM,OAAO,cAAc,SAAS;AAE5C,kBAAgB;AACd,OAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO;AACjC,0DAAI,QAAS,eACX;IAGF,MAAM,gEAAa,QAAS,eAAc,EAAE;AAC5C,qBAAiB;KACf,6DAAW,QAAS;KACpB,GAAG;KACJ,CAAC;;KAEH;GAAC;GAAM;GAAW;GAAM,CAAC;AAE5B,MAAI,MACF,QAAO,gBAAgB,OAAO,QAAQ;AAGxC,MAAI,CAAC,QAAQ,CAAC,gEAAa,QAAS,gBAClC,QAAO,QAAQ,gBAAgB;AAGjC,MAAI,MAAM;AACR,0DACE,QAAS,WACT,CAAC,cACC,MACA,QAAQ,QACR,QAAQ,eACN,QAAQ,IAAI,yCACd,QAAQ,SACT,EACD;IACA,MAAM,EACJ,4BAA6C,oCAAC,aAAI,gBAAmB,KACnE;AACJ,WAAO,oBAAoB,KAAK;;AAGlC,UAAO,oCAAC;IAAgB;IAAM,GAAI;KAAS;;AAG7C,SAAO"}
|
|
@@ -37,8 +37,8 @@ const fetchUser = async (url) => {
|
|
|
37
37
|
*
|
|
38
38
|
* @example Refetch user
|
|
39
39
|
*
|
|
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.
|
|
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.
|
|
42
42
|
*
|
|
43
43
|
* ```tsx title="Refetch User"
|
|
44
44
|
* "use client";
|
|
@@ -188,6 +188,7 @@ const handlePageError = (error, options) => {
|
|
|
188
188
|
* ```
|
|
189
189
|
*
|
|
190
190
|
* @param Component - The page component to protect
|
|
191
|
+
* @typeParam P - Props of the protected component (excluding `user`).
|
|
191
192
|
* @param options - Optional configuration
|
|
192
193
|
* @returns A protected React component.
|
|
193
194
|
*
|
|
@@ -246,4 +247,4 @@ Object.defineProperty(exports, 'useAuth', {
|
|
|
246
247
|
return useAuth;
|
|
247
248
|
}
|
|
248
249
|
});
|
|
249
|
-
//# sourceMappingURL=protect-client-page-
|
|
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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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AuthorizationParams, MonoCloudUser } from "@monocloud/auth-node-core";
|
|
2
2
|
import { JSX } from "react";
|
|
3
3
|
import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
|
|
4
4
|
import { GetServerSideProps, GetServerSidePropsContext, GetServerSidePropsResult, NextApiRequest, NextApiResponse } from "next/types";
|
|
@@ -21,7 +21,7 @@ interface AppRouterContext {
|
|
|
21
21
|
params: Record<string, string | string[]> | Promise<Record<string, string | string[]>>;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* Handler function returned by
|
|
24
|
+
* Handler function returned by {@link monoCloudAuth | monoCloudAuth()}.
|
|
25
25
|
*
|
|
26
26
|
* This handler processes authentication routes such as sign-in, callback, sign-out, and userinfo across supported Next.js runtimes (App Router, Pages Router, and API routes).
|
|
27
27
|
*
|
|
@@ -76,13 +76,9 @@ type NextMiddlewareOnGroupAccessDenied = (request: NextRequest, event: NextFetch
|
|
|
76
76
|
* @category Types
|
|
77
77
|
*
|
|
78
78
|
*/
|
|
79
|
-
type ProtectedRouteMatcher = string | RegExp | {
|
|
79
|
+
type ProtectedRouteMatcher = /** A single relative route path to protect. */string /** A regular expression used to match routes to protect. */ | RegExp /** An object with fine-grained control over routes and group-based access. */ | {
|
|
80
80
|
/**
|
|
81
|
-
* Route patterns that should be protected.
|
|
82
|
-
*
|
|
83
|
-
* Each entry may be:
|
|
84
|
-
* - A relative route path
|
|
85
|
-
* - A regular expression used to match routes
|
|
81
|
+
* Route patterns that should be protected. Each entry may be a relative route path or a regular expression used to match routes.
|
|
86
82
|
*/
|
|
87
83
|
routes: (string | RegExp)[];
|
|
88
84
|
/**
|
|
@@ -110,6 +106,7 @@ type CustomProtectedRouteMatcher = (req: NextRequest) => Promise<boolean> | bool
|
|
|
110
106
|
*
|
|
111
107
|
* @param req The incoming Next.js request.
|
|
112
108
|
* @param ctx The App Router context containing dynamic route parameters.
|
|
109
|
+
* @typeParam T The type of the App Router context passed to the handler.
|
|
113
110
|
* @param error The error thrown during endpoint execution.
|
|
114
111
|
* @returns Returns a `NextResponse` or `void`.
|
|
115
112
|
*/
|
|
@@ -137,7 +134,7 @@ type PageOnError = (req: NextApiRequest, res: NextApiResponse, error: Error) =>
|
|
|
137
134
|
*/
|
|
138
135
|
type OnError = AppOnError | PageOnError;
|
|
139
136
|
/**
|
|
140
|
-
* Options for
|
|
137
|
+
* Options for {@link monoCloudAuth | monoCloudAuth()}.
|
|
141
138
|
*
|
|
142
139
|
* @category Types
|
|
143
140
|
*/
|
|
@@ -159,7 +156,7 @@ interface MonoCloudAuthOptions {
|
|
|
159
156
|
*/
|
|
160
157
|
type ProtectedRoutes = ProtectedRouteMatcher[] | CustomProtectedRouteMatcher;
|
|
161
158
|
/**
|
|
162
|
-
* Options for configuring
|
|
159
|
+
* Options for configuring {@link authMiddleware | authMiddleware()}.
|
|
163
160
|
*
|
|
164
161
|
* These options control which routes are protected and how authentication and authorization failures are handled during request processing.
|
|
165
162
|
*
|
|
@@ -179,9 +176,8 @@ interface MonoCloudMiddlewareOptions {
|
|
|
179
176
|
* Defines which routes require authentication.
|
|
180
177
|
*
|
|
181
178
|
* Accepts either an array of {@link ProtectedRouteMatcher} or a {@link CustomProtectedRouteMatcher}.
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* - If an empty array is provided, no routes are protected.
|
|
179
|
+
* If omitted, all routes matched by the middleware config are protected.
|
|
180
|
+
* If an empty array is provided, no routes are protected.
|
|
185
181
|
*/
|
|
186
182
|
protectedRoutes?: ProtectedRoutes;
|
|
187
183
|
/**
|
|
@@ -240,7 +236,7 @@ type AppRouterPageHandler = (props: {
|
|
|
240
236
|
*/
|
|
241
237
|
type AppRouterApiHandlerFn = (req: NextRequest | Request, ctx: AppRouterContext) => Promise<Response | NextResponse> | Response | NextResponse;
|
|
242
238
|
/**
|
|
243
|
-
* Options for configuring
|
|
239
|
+
* Options for configuring {@link protectPage | protectPage()} in the App Router.
|
|
244
240
|
*
|
|
245
241
|
* @category Types
|
|
246
242
|
*/
|
|
@@ -276,7 +272,7 @@ interface ProtectAppPageOptions extends GroupOptions {
|
|
|
276
272
|
authParams?: ExtraAuthParams;
|
|
277
273
|
}
|
|
278
274
|
/**
|
|
279
|
-
* Options for configuring
|
|
275
|
+
* Options for configuring {@link protectPage | protectPage()} in the Pages Router.
|
|
280
276
|
*
|
|
281
277
|
* @category Types
|
|
282
278
|
*
|
|
@@ -312,7 +308,7 @@ interface ProtectPagePageOptions<P extends Record<string, any> = Record<string,
|
|
|
312
308
|
authParams?: ExtraAuthParams;
|
|
313
309
|
}
|
|
314
310
|
/**
|
|
315
|
-
* Handler invoked when no valid session exists while running a Pages Router `getServerSideProps` protected by
|
|
311
|
+
* Handler invoked when no valid session exists while running a Pages Router `getServerSideProps` protected by {@link protectPage | protectPage()}.
|
|
316
312
|
*
|
|
317
313
|
* @category Types (Handler)
|
|
318
314
|
*
|
|
@@ -323,7 +319,19 @@ interface ProtectPagePageOptions<P extends Record<string, any> = Record<string,
|
|
|
323
319
|
*/
|
|
324
320
|
type ProtectPagePageOnAccessDeniedType<P, Q extends ParsedUrlQuery = ParsedUrlQuery> = (context: GetServerSidePropsContext<Q>) => Promise<GetServerSidePropsResult<P>> | GetServerSidePropsResult<P>;
|
|
325
321
|
/**
|
|
326
|
-
*
|
|
322
|
+
* Next.js `getServerSideProps` context extended with the authenticated user when using {@link protectPage | protectPage()}.
|
|
323
|
+
*
|
|
324
|
+
* @category Types (Handler)
|
|
325
|
+
*
|
|
326
|
+
*/
|
|
327
|
+
interface ProtectPageGetServerSidePropsContext<Q extends ParsedUrlQuery = ParsedUrlQuery> extends GetServerSidePropsContext<Q> {
|
|
328
|
+
/**
|
|
329
|
+
* The authenticated user resolved from the current session.
|
|
330
|
+
*/
|
|
331
|
+
user: MonoCloudUser;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Handler invoked when an authenticated user does not satisfy the required group restrictions while running a Pages Router `getServerSideProps` protected by {@link protectPage | protectPage()}.
|
|
327
335
|
*
|
|
328
336
|
* @category Types (Handler)
|
|
329
337
|
*
|
|
@@ -332,11 +340,9 @@ type ProtectPagePageOnAccessDeniedType<P, Q extends ParsedUrlQuery = ParsedUrlQu
|
|
|
332
340
|
* @param context The Next.js `getServerSideProps` context.
|
|
333
341
|
* @returns A `getServerSideProps` result.
|
|
334
342
|
*/
|
|
335
|
-
type ProtectPagePageOnGroupAccessDeniedType<P, Q extends ParsedUrlQuery = ParsedUrlQuery> = (context:
|
|
336
|
-
user: MonoCloudUser;
|
|
337
|
-
}) => Promise<GetServerSidePropsResult<P>> | GetServerSidePropsResult<P>;
|
|
343
|
+
type ProtectPagePageOnGroupAccessDeniedType<P, Q extends ParsedUrlQuery = ParsedUrlQuery> = (context: ProtectPageGetServerSidePropsContext<Q>) => Promise<GetServerSidePropsResult<P>> | GetServerSidePropsResult<P>;
|
|
338
344
|
/**
|
|
339
|
-
* Return type produced by the
|
|
345
|
+
* Return type produced by the {@link protectPage | protectPage()} wrapper for the Pages Router.
|
|
340
346
|
*
|
|
341
347
|
* Represents a `getServerSideProps` compatible function that resolves authentication before executing page logic and injects the authenticated `user` into the returned props.
|
|
342
348
|
*
|
|
@@ -350,13 +356,13 @@ type ProtectPagePageReturnType<P, Q extends ParsedUrlQuery = ParsedUrlQuery> = (
|
|
|
350
356
|
accessDenied?: boolean;
|
|
351
357
|
}>>;
|
|
352
358
|
/**
|
|
353
|
-
* App Router Server Component wrapped by
|
|
359
|
+
* Props injected into an App Router Server Component wrapped by {@link protectPage | protectPage()}.
|
|
354
360
|
*
|
|
355
|
-
*
|
|
361
|
+
* Includes the authenticated `user` and optional route/search parameters provided by Next.js.
|
|
356
362
|
*
|
|
357
363
|
* @category Types (Handler)
|
|
358
364
|
*/
|
|
359
|
-
|
|
365
|
+
interface ProtectedAppServerComponentProps {
|
|
360
366
|
/**
|
|
361
367
|
* The authenticated user resolved from the current session.
|
|
362
368
|
*/
|
|
@@ -369,7 +375,17 @@ type ProtectedAppServerComponent = (props: {
|
|
|
369
375
|
* URL search parameters provided by the App Router.
|
|
370
376
|
*/
|
|
371
377
|
searchParams?: Record<string, string | string[] | undefined>;
|
|
372
|
-
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* App Router Server Component wrapped by {@link protectPage | protectPage()}.
|
|
381
|
+
*
|
|
382
|
+
* This component is only executed after authentication (and optional authorization) succeeds. The authenticated `user` is injected into the component props automatically.
|
|
383
|
+
*
|
|
384
|
+
* @param props - The component props, including the authenticated user and any additional page props.
|
|
385
|
+
*
|
|
386
|
+
* @category Types (Handler)
|
|
387
|
+
*/
|
|
388
|
+
type ProtectedAppServerComponent = (props: ProtectedAppServerComponentProps) => Promise<JSX.Element> | JSX.Element;
|
|
373
389
|
/**
|
|
374
390
|
* Handler invoked when a request is denied because the user is not authenticated in an App Router API route.
|
|
375
391
|
*
|
|
@@ -394,7 +410,7 @@ type AppRouterApiOnAccessDeniedHandler = (req: NextRequest, ctx: AppRouterContex
|
|
|
394
410
|
*/
|
|
395
411
|
type AppRouterApiOnGroupAccessDeniedHandler = (req: NextRequest, ctx: AppRouterContext, user: MonoCloudUser) => Promise<Response> | Response;
|
|
396
412
|
/**
|
|
397
|
-
* Options for configuring
|
|
413
|
+
* Options for configuring {@link protectApi | protectApi()} in the App Router.
|
|
398
414
|
*
|
|
399
415
|
* @category Types
|
|
400
416
|
*/
|
|
@@ -431,7 +447,7 @@ type PageRouterApiOnAccessDeniedHandler = (req: NextApiRequest, res: NextApiResp
|
|
|
431
447
|
*/
|
|
432
448
|
type PageRouterApiOnGroupAccessDeniedHandler = (req: NextApiRequest, res: NextApiResponse<any>, user: MonoCloudUser) => Promise<unknown> | unknown;
|
|
433
449
|
/**
|
|
434
|
-
* Options for configuring
|
|
450
|
+
* Options for configuring {@link protectApi | protectApi()} in the Pages Router.
|
|
435
451
|
*
|
|
436
452
|
* @category Types
|
|
437
453
|
*/
|
|
@@ -446,7 +462,7 @@ interface ProtectApiPageOptions extends GroupOptions {
|
|
|
446
462
|
onGroupAccessDenied?: PageRouterApiOnGroupAccessDeniedHandler;
|
|
447
463
|
}
|
|
448
464
|
/**
|
|
449
|
-
* Options for configuring
|
|
465
|
+
* Options for configuring {@link protect | protect()}.
|
|
450
466
|
*
|
|
451
467
|
* @category Types
|
|
452
468
|
*/
|
|
@@ -495,54 +511,18 @@ interface GroupOptions extends IsUserInGroupOptions {
|
|
|
495
511
|
groups?: string[];
|
|
496
512
|
}
|
|
497
513
|
/**
|
|
498
|
-
* Options for
|
|
514
|
+
* Options for {@link redirectToSignIn | redirectToSignIn()}.
|
|
499
515
|
*
|
|
500
516
|
* @category Types
|
|
501
517
|
*/
|
|
502
|
-
interface RedirectToSignInOptions {
|
|
518
|
+
interface RedirectToSignInOptions extends ExtraAuthParams {
|
|
503
519
|
/**
|
|
504
520
|
* URL to return the user to after successful authentication. Must be a relative application URL.
|
|
505
521
|
*/
|
|
506
522
|
returnUrl?: string;
|
|
507
|
-
/**
|
|
508
|
-
* Maximum allowed time (in seconds) since the user's last authentication.
|
|
509
|
-
*/
|
|
510
|
-
maxAge?: number;
|
|
511
|
-
/**
|
|
512
|
-
* Hint to the authorization server indicating which authenticator should be used during sign-in.
|
|
513
|
-
*/
|
|
514
|
-
authenticatorHint?: Authenticators;
|
|
515
|
-
/**
|
|
516
|
-
* Scopes to request during authentication.
|
|
517
|
-
*/
|
|
518
|
-
scopes?: string[];
|
|
519
|
-
/**
|
|
520
|
-
* Resource indicators the access token should be issued for.
|
|
521
|
-
*/
|
|
522
|
-
resource?: string[];
|
|
523
|
-
/**
|
|
524
|
-
* Preferred UI language(s) for the authentication experience.
|
|
525
|
-
*/
|
|
526
|
-
uiLocales?: string;
|
|
527
|
-
/**
|
|
528
|
-
* Preferred display mode for the authentication UI.
|
|
529
|
-
*/
|
|
530
|
-
display?: DisplayOptions;
|
|
531
|
-
/**
|
|
532
|
-
* Authentication Context Class Reference (ACR) values requesting specific authentication methods or assurance levels.
|
|
533
|
-
*/
|
|
534
|
-
acrValues?: string[];
|
|
535
|
-
/**
|
|
536
|
-
* Hint about the user's identifier (for example, email or username).
|
|
537
|
-
*/
|
|
538
|
-
loginHint?: string;
|
|
539
|
-
/**
|
|
540
|
-
* Controls whether the authorization server should force specific user interactions during authentication
|
|
541
|
-
*/
|
|
542
|
-
prompt?: Prompt;
|
|
543
523
|
}
|
|
544
524
|
/**
|
|
545
|
-
* Options for
|
|
525
|
+
* Options for {@link redirectToSignOut | redirectToSignOut()}.
|
|
546
526
|
*
|
|
547
527
|
* @category Types
|
|
548
528
|
*/
|
|
@@ -559,5 +539,5 @@ interface RedirectToSignOutOptions {
|
|
|
559
539
|
federated?: boolean;
|
|
560
540
|
}
|
|
561
541
|
//#endregion
|
|
562
|
-
export {
|
|
563
|
-
//# sourceMappingURL=types-
|
|
542
|
+
export { ProtectedAppServerComponent as A, ProtectAppPageOptions as C, ProtectPagePageOnGroupAccessDeniedType as D, ProtectPagePageOnAccessDeniedType as E, RedirectToSignOutOptions as F, ProtectedRouteMatcher as M, ProtectedRoutes as N, ProtectPagePageOptions as O, RedirectToSignInOptions as P, ProtectApiPageOptions as S, ProtectPageGetServerSidePropsContext as T, OnError as _, AppRouterContext as a, PageRouterApiOnGroupAccessDeniedHandler as b, ExtraAuthParams as c, MonoCloudAuthHandler as d, MonoCloudAuthOptions as f, NextMiddlewareResult as g, NextMiddlewareOnGroupAccessDenied as h, AppRouterApiOnGroupAccessDeniedHandler as i, ProtectedAppServerComponentProps as j, ProtectPagePageReturnType as k, GroupOptions as l, NextMiddlewareOnAccessDenied as m, AppRouterApiHandlerFn as n, AppRouterPageHandler as o, MonoCloudMiddlewareOptions as p, AppRouterApiOnAccessDeniedHandler as r, CustomProtectedRouteMatcher as s, AppOnError as t, IsUserInGroupOptions as u, PageOnError as v, ProtectOptions as w, ProtectApiAppOptions as x, PageRouterApiOnAccessDeniedHandler as y };
|
|
543
|
+
//# sourceMappingURL=types-ClljFIvK.d.mts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monocloud/auth-nextjs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "MonoCloud Next.js Authentication SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"monocloud",
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"cookie": "1.1.1",
|
|
59
59
|
"swr": "2.4.0",
|
|
60
|
-
"@monocloud/auth-node-core": "0.1.
|
|
61
|
-
"@monocloud/auth-core": "0.1.
|
|
60
|
+
"@monocloud/auth-node-core": "0.1.8",
|
|
61
|
+
"@monocloud/auth-core": "0.1.6"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@edge-runtime/vm": "5.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protect-client-page-B1fOU4Zl.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 * @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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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protect-client-page-BFlGkK8F.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 * @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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"}
|