Skip to content

Commit 7dae0da

Browse files
committed
fix: automatically close /auth_flow page at end of Shopify OAuth UX and hide Shop field unless necessary (#16441)
1 parent 7b678d9 commit 7dae0da

File tree

5 files changed

+46
-10
lines changed

5 files changed

+46
-10
lines changed

airbyte-webapp/src/area/connector/types/oauthCallback.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@ interface CancelOAuthEvent {
1212
tabUuid: string;
1313
}
1414

15-
export type OAuthEvent = CompletedOAuthEvent | TakeoverOAuthEvent | CancelOAuthEvent;
15+
interface CloseOAuthEvent {
16+
type: "close";
17+
}
18+
19+
export type OAuthEvent = CompletedOAuthEvent | TakeoverOAuthEvent | CancelOAuthEvent | CloseOAuthEvent;

airbyte-webapp/src/area/connector/utils/oauthCallback.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,18 @@ if (window.opener && window.opener.location.origin === window.location.origin) {
2626
const bc = new BroadcastChannel<OAuthEvent>(OAUTH_BROADCAST_CHANNEL_NAME);
2727
bc.postMessage({ type: "completed", query });
2828

29-
const redirectToIndex = "redirect_to_index" in query ? true : false;
30-
// Close popup window once we're done
31-
if (redirectToIndex) {
32-
// Redirect to the index page to support Shopify's strange integration requirements
33-
// see https://shopify.dev/docs/apps/launch/app-requirements-checklist#b-permissions for more details
29+
bc.onmessage = async (event) => {
30+
if (event.type === "close") {
31+
window.close();
32+
}
33+
};
34+
35+
// Wait to receive the "close" event from the original tab.
36+
// This is necessary because Shopify requires clicking the Open app button to open our app homepage, rather than just close out immediately.
37+
// To satisfy this requirement, we wait 5 seconds in this /auth_flow page to receive the "close" event, which indicates that the user initiated
38+
// this from the Airbyte UI rather than the Shopify app.
39+
// If we don't receive it, redirect to the index page to satisfy Shopify's requirements.
40+
// See notion page for more details: https://www.notion.so/Shopify-App-Listing-Requirements-1f51b3df260c8029b2a7de0399c8d5d2
41+
setTimeout(() => {
3442
window.location.href = "/";
35-
} else {
36-
window.close();
37-
}
43+
}, 5000);

airbyte-webapp/src/components/ui/Toast/Toast.module.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,19 @@
2121
// Use as a background color the primary color of the message as set via the Message component
2222
background-color: var(--message-color);
2323
animation: timeout-bar forwards 5s linear;
24+
animation-play-state: paused;
2425
}
2526

2627
&:hover::after {
2728
animation-play-state: paused;
2829
}
2930
}
31+
32+
&--focused {
33+
&::after {
34+
animation-play-state: running;
35+
}
36+
}
3037
}
3138

3239
@keyframes timeout-bar {

airbyte-webapp/src/components/ui/Toast/Toast.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import classNames from "classnames";
2-
import React from "react";
2+
import React, { useEffect, useState } from "react";
33

44
import styles from "./Toast.module.scss";
55
import { Message, MessageProps } from "../Message";
@@ -9,12 +9,28 @@ export interface ToastProps extends MessageProps {
99
}
1010

1111
export const Toast: React.FC<ToastProps> = ({ timeout, ...props }) => {
12+
// Delay the timeout animation until the user has focused the current tab
13+
const [isFocused, setIsFocused] = useState(document.hasFocus());
14+
useEffect(() => {
15+
const onFocus = () => setIsFocused(true);
16+
const onBlur = () => setIsFocused(false);
17+
18+
window.addEventListener("focus", onFocus);
19+
window.addEventListener("blur", onBlur);
20+
21+
return () => {
22+
window.removeEventListener("focus", onFocus);
23+
window.removeEventListener("blur", onBlur);
24+
};
25+
}, []);
26+
1227
return (
1328
<div onAnimationEnd={props.onClose}>
1429
<Message
1530
{...props}
1631
className={classNames(props.className, styles.toastContainer, {
1732
[styles["toastContainer--timeout"]]: timeout,
33+
[styles["toastContainer--focused"]]: isFocused,
1834
})}
1935
textClassName={styles.toastText}
2036
/>

airbyte-webapp/src/hooks/services/useConnectorAuth.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,9 @@ export function useRunOauthFlow({
369369
};
370370

371371
await completeOauth(queryParams);
372+
373+
// Close the /auth_flow page after completing the oauth flow
374+
bc.postMessage({ type: "close" });
372375
}
373376
// OAuth flow is completed or taken over by another tab, so close the broadcast channel
374377
// and popup window if it is still open.

0 commit comments

Comments
 (0)