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 (
+
+ );
+}
+
+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;
+};