From 5fe9a5099f5a1ea7e68769c998adb382a3fe9b2b Mon Sep 17 00:00:00 2001 From: Lukasz Ostrowski Date: Tue, 4 Oct 2022 16:28:00 +0200 Subject: [PATCH] Add authorization HOC --- package.json | 5 +++ src/app-bridge/with-authorization.tsx | 57 +++++++++++++++++++++++++++ src/util/index.ts | 2 + src/util/is-in-iframe.ts | 11 ++++++ src/util/use-is-mounted.ts | 8 ++++ 5 files changed, 83 insertions(+) create mode 100644 src/app-bridge/with-authorization.tsx create mode 100644 src/util/index.ts create mode 100644 src/util/is-in-iframe.ts create mode 100644 src/util/use-is-mounted.ts diff --git a/package.json b/package.json index 9293f3f7..9dbbcd4d 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,11 @@ "import": "./app-bridge/index.mjs", "require": "./app-bridge/index.js" }, + "./util": { + "types": "./util/index.d.ts", + "import": "./util/index.mjs", + "require": "./util/index.js" + }, "./handlers/next": { "types": "./handlers/next/index.d.ts", "import": "./handlers/next/index.mjs", diff --git a/src/app-bridge/with-authorization.tsx b/src/app-bridge/with-authorization.tsx new file mode 100644 index 00000000..8eaae69e --- /dev/null +++ b/src/app-bridge/with-authorization.tsx @@ -0,0 +1,57 @@ +import { NextPage } from "next"; +import * as React from "react"; +import { PropsWithChildren, ReactNode } from "react"; + +import { isInIframe, useIsMounted } from "../util"; +import { useDashboardToken } from "./use-dashboard-token"; + +function SimpleError({ children }: PropsWithChildren<{}>) { + return ( +
+

{children}

+
+ ); +} + +type Props = { + unmounted?: ReactNode; + notIframe?: ReactNode; + noDashboardToken?: ReactNode; + dashboardTokenInvalid?: ReactNode; +}; + +export const withAuthorization = + ({ + dashboardTokenInvalid = Dashboard token is invalid, + noDashboardToken = Dashboard token doesn"t exist, + notIframe = The view can only be displayed inside iframe., + unmounted =

Loading

, + }: Props) => + >( + BaseComponent: React.FunctionComponent + ) => { + function AuthorizedPage(props: BaseProps) { + const mounted = useIsMounted(); + const { isTokenValid, hasAppToken } = useDashboardToken(); + + if (!mounted) { + return unmounted; + } + + if (!isInIframe()) { + return notIframe; + } + + if (!hasAppToken) { + return noDashboardToken; + } + + if (!isTokenValid) { + return dashboardTokenInvalid; + } + + return ; + } + + return AuthorizedPage; + }; diff --git a/src/util/index.ts b/src/util/index.ts new file mode 100644 index 00000000..deacb276 --- /dev/null +++ b/src/util/index.ts @@ -0,0 +1,2 @@ +export * from "./is-in-iframe"; +export * from "./use-is-mounted"; diff --git a/src/util/is-in-iframe.ts b/src/util/is-in-iframe.ts new file mode 100644 index 00000000..6e900d17 --- /dev/null +++ b/src/util/is-in-iframe.ts @@ -0,0 +1,11 @@ +export const isInIframe = () => { + if (!document || !window) { + throw new Error("isInIframe should be called only in browser"); + } + + try { + return document.location !== window.parent.location; + } catch (e) { + return false; + } +}; diff --git a/src/util/use-is-mounted.ts b/src/util/use-is-mounted.ts new file mode 100644 index 00000000..dfb473b7 --- /dev/null +++ b/src/util/use-is-mounted.ts @@ -0,0 +1,8 @@ +import { useEffect, useState } from "react"; + +export const useIsMounted = (): boolean => { + const [mounted, setMounted] = useState(false); + useEffect(() => setMounted(true), []); + + return mounted; +};